3

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.

user3844884
  • 41
  • 1
  • 2
  • 6
  • Will some one response to this – user3844884 Jul 23 '14 at 19:48
  • the I2C clock has to be steady cycling when bus activity, so erratic timing will inhibit the child from locking on the signal. Your use of function calls will result in just such erratic timing. I.E. all the items should be macros strung together, not only the actual manipulation of the i/o pins. – user3629249 Jul 24 '14 at 06:53
  • the data must be stable for each bit while the clock line transitions high and back to low. START is clock high while data transitions from high to low STOP is clock high while data transitions from low to high. There are specific timings for each portion of the communication. – user3629249 Jul 24 '14 at 07:12
  • each device on the bus must sample the data line at least twice (and preferably 16 times) for each clock period to be sure of recognizing any start or stop event. – user3629249 Jul 24 '14 at 07:17
  • the master is always driving the clock line when the master wants any communication with a slave. The master always initiates the communication, usually with a START, slave address, STOP slave xmits ack after every byte, Master xmits START, command, STOP. slave xmits ack after every byte If data is to be sent to slave, master xmits START DATA...DATA STOP after every byte, slave xmits ACK. If data is to be send by slave (after slave receives command to send, master xmits START slave xmits Data...Data, master xmits STOP if any communication is not understood, then NAK is sent rather than a ACK – user3629249 Jul 24 '14 at 07:20
  • Note: the master xmits a ACk after every byte received from the slave. – user3629249 Jul 24 '14 at 07:21
  • to paraphase: the boss yells 'john', john replies what? boss yells give me that specific data, john replies here is the data, boss replies thanks. conversely, the boss yells 'john', john replies what?, boss yells 'I'm giving you data', john replies, ok, boss give the data, john replies thanks. – user3629249 Jul 24 '14 at 07:26
  • @user3629249. Why my timing is erratic?. And you specified that, "There are specific timings for each portion of the communication", can you guide me how i need to give that timing according to hardware platform. – user3844884 Jul 24 '14 at 08:13

4 Answers4

9

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.

akohlsmith
  • 329
  • 4
  • 12
  • Very well done for identifying and correcting the incredibly misleading comments. If anyone read and believed those comments, they would have an awful time trying to make 'bit banging' I2C work. – gbulmer Sep 29 '16 at 10:02
2

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

0

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.

Ruslan Gerasimov
  • 1,752
  • 1
  • 13
  • 20
  • i have defined macro for P0_0 and P0_1 as SCL and SDA repectively. Can you tell me, is mt code is correct or i have to make any changes – user3844884 Jul 24 '14 at 04:28
  • I can tell you only that I used once such a code, it seemed like yours, so it worked for me in a real hardware. I used it in some device on General I/O ports instead of I2C controller. For further consideration I advice you to connect the real load to your bus, real I2C slave, check how it works, and then ask questions with particular issues, if any. – Ruslan Gerasimov Jul 24 '14 at 04:59
-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.

Ahmad Aghazadeh
  • 16,571
  • 12
  • 101
  • 98
FrankP
  • 1