user3844884
user3844884

Reputation: 41

I2c bit banging Programming using C

I am trying to write a c code for I2c Using bit banging. I have revised the Code of wiki(http://en.wikipedia.org/wiki/I%C2%B2C). But i am unable to get a result. As per my understanding the code in the wiki is not correct. Many changes i made, but one of the major change i made,where wiki failed tell correctly is tagged/label that line with /TBN/. My code is below, // Hardware-specific support functions that MUST be customized:

#define I2CSPEED 135
#define SCL P0_0
#define SDA P0_1

void I2C_delay() { volatile int v; int i; for (i=0; i < I2CSPEED/2; i++) v; }
bool read_SCL(void); // Set SCL as input and return current level of line, 0 or 1
bool read_SDA(void); // Set SDA as input and return current level of line, 0 or 1
void clear_SCL(void); // Actively drive SCL signal low
void clear_SDA(void); // Actively drive SDA signal low
void Set_SDA(void);  // Actively drive SDA signal High;
void Set_SCL(void);  // Actively drive SCL signal High

void Set_SCL(void)
{
    //make  P0_0 as OutputPort
    SCL = 1;
}

void Set_SDA(void)
{
    //make  P0_1 as OutputPort
    SDA = 1;
}   

void clear_SCL(void)
{
    //make  P0_0 as OutputPort
    SCL = 0;
}

void clear_SDA(void)
{
    //make  P0_1 as OutputPort
    SDA = 0;
}

bool read_SCL(void)
{
    //make  P0_0 as InputPort
    return SCL;
}

bool read_SDA(void)
{
    //make  P0_0 as InputPort
    return SDA;
}

void i2c_start_cond(void) {

  // set SDA to 1
  Set_SDA();/*TBN*/
  set_SCL();
  // SCL is high, set SDA from 1 to 0.
  I2C_delay();
  clear_SDA();
  I2C_delay();
  I2C_delay();
  clear_SCL();//make SCL Low for data transmission 
  started = true;
}

void i2c_stop_cond(void){
  // set SDA to 0
  clear_SDA();
  I2C_delay();
  // SCL is high, set SDA from 0 to 1
  Set_SCL();/*TBN*/
  I2C_delay();
  Set_SDA();
}

// Write a bit to I2C bus
void i2c_write_bit(bool bit) {
  if (bit) {
    Set_SDA();/*TBN*/
  } else {
    clear_SDA();
  }
  I2C_delay();
  clear_SCL();
}

// Read a bit from I2C bus
bool i2c_read_bit(void) {
  bool bit;
  // Let the slave drive data
  read_SDA();
  I2C_delay();
  // SCL is high, now data is valid
  bit = read_SDA();
  I2C_delay();
  clear_SCL();
  return bit;
}

// Write a byte to I2C bus. Return 0 if ack by the slave.
bool i2c_write_byte(bool send_start,
                    bool send_stop,
                    unsigned char byte) {
  unsigned bit;
  bool nack;
  if (send_start) {
    i2c_start_cond();
  }
  for (bit = 0; bit < 8; bit++) {
    i2c_write_bit((byte & 0x80) != 0);
    byte <<= 1;
  }
  nack = i2c_read_bit();
  if (send_stop) {
    i2c_stop_cond();
  }
  return nack;
}

// Read a byte from I2C bus
unsigned char i2c_read_byte(bool nack, bool send_stop) {
  unsigned char byte = 0;
  unsigned bit;
  for (bit = 0; bit < 8; bit++) {
    byte = (byte << 1) | i2c_read_bit();
  }
  i2c_write_bit(nack);
  if (send_stop) {
    i2c_stop_cond();
  }
  return byte;
}

This code is for one master in a bus. I request experts to review my code and let me know my mistakes.

Upvotes: 3

Views: 14804

Answers (4)

DesdmondMantes
DesdmondMantes

Reputation: 21

First bit banging I2C is way more complicated than bit banging SPI. So first I would take an SPI EEPROM like the 25AA1024 and try my bit banging skills.

Then try a simple Adadfruit I2C device like this one https://www.adafruit.com/products/1855 It only requires I2C out and you easily port the Arduino code to C or C#.

I know that the USB device Nusbio implement I2C and SPI bit banging written in C#.

See their source code at https://github.com/madeintheusb/Nusbio.Samples

Upvotes: 2

FrankP
FrankP

Reputation: 1

I stripped down the wikipedia code for my special-purpose application, but I only got it working after I fixed the Stop condition. It never raises the SCL so I added the following to that function:

 // Stop bit setup time, minimum 4us
  set_SCL();  // added this line
  I2C_delay();

I am verifying the correctness now, and if so I'll update wikipedia myself.

Upvotes: -1

akohlsmith
akohlsmith

Reputation: 339

There's a lot of flat-out wrong information in the comments.

First of all, your read_bit() function never toggles the clock. That's probably your problem, along with @user3629249's comment that the master sends an ACK bit after every 8 bits from the slave. You'll have to address this in your read_byte() function.

Second: I2C does not care about clock jitter; it defines only that the data must be stable when the clock falls. There will be some nanoseconds on either side of the clock edge where the data must not change but that is not the issue here. I2C slaves don't "lock on" to the clock. In fact the I2C specification doesn't define a lower limit to high or low SCL times. You could conceivably wait days or years between clock ticks. SMB does define a timeout, but that's not your problem here either.

Lastly: oversampling doesn't really apply to I2C. You can read the bit a few times to make sure it hasn't changed but a properly functioning I2C slave will not be changing the data until sometime after the rising edge of the SDA signal, which is many thousands of nanoseconds away from the critical falling edge. I2C is not asynchronous like your usual serial port/UART.

Upvotes: 9

Ruslan Gerasimov
Ruslan Gerasimov

Reputation: 1782

You need to implement P0_0 and P0_1, somehow. The calls you marked finally involve them and I am not seeing them in your code nohow.

If you are developing this on a real hardware then in order to affect corresponding pins in your hardware you need implement P0_0 and P0_1 macros with code accessing the particular control registers to control the logical levels at these two lines.

Upvotes: 0

Related Questions