I2C driver using bit bang

Sathyanarayana Hadadi March 24, 20137 comments Coded in C

I2C driver using bit bang. This can be used with any microcontroller which has 2 GPIO lines which can be configured as input/output

typedef struct 
{
  unsigned int PIN0:1;
  unsigned int PIN1:1;
  unsigned int PIN2:1;
  unsigned int PIN3:1;
  unsigned int PIN4:1;
  unsigned int PIN5:1;
  unsigned int PIN6:1;
  unsigned int PIN7:1;
} PORT;

/* TODO: Example address shown, but the proper address */
#define PORT0 *(volatile PORT *)0x1234

/* Define the port used for I2C data and clk as shown above to access them pin wise */
#define I2C_DATA PORT0.PIN0
#define I2C_CLK  PORT0.PIN1

#define HIGH 1
#define LOW  0

/* I2C Start - bit bang */
void I2C_START(void)
{
    /* I2C Start condition, data line goes low when clock is high */
    I2C_DATA = HIGH;
    I2C_CLK = HIGH;
    I2C_DATA = LOW;
    I2C_CLK = LOW;
}

/* I2C Stop - bit bang */
void I2C_STOP (void)
{
    /* I2C Stop condition, clock goes high when data is low */
    I2C_CLK = LOW;
    I2C_DATA = LOW;
    I2C_CLK = HIGH;
    I2C_DATA = HIGH;
}

/* I2C Write - bit bang */
void I2C_WRITE(unsigned char data)
{
	unsigned char outBits;
	unsigned char inBit;
	
 	/* 8 bits */
	for(outBits = 0; outBits < 8; outBits++) 
	{
	    if(data & 0x80)
		    I2C_DATA = 1;
		else
		    I2C_DATA = 0;
      	data  <<= 1;
		/* Generate clock for 8 data bits */
		SCLK = HIGH;
		SCLK = LOW;					
	}
	
	/* Generate clock for ACK */
	I2C_CLK = HIGH;
        /* Wait for clock to go high, clock stretching */
        while(I2C_CLK);
        /* Clock high, valid ACK */
	inBit = I2C_DATA;
	I2C_CLK = LOW;					
}

unsigned char I2C_READ (void)
{
	unsigned char inData, inBits;

	inData = 0x00;
	/* 8 bits */
	for(inBits = 0; inBits < 8; inBits++)
	{
		inData <<= 1;
		I2C_CLK = HIGH;
      	inData |= I2C_DATA;
		I2C_CLK = LOW;					
	}

   return inData;
}

/* Examble for writing to I2C Slave */
void writeI2CSlave (unsigned char data)	
{
    /* Start */
  	I2C_START();
	/* Slave address */
   	I2C_WRITE(0xAA)
	/* Slave control byte */
   	I2C_WRITE(0xBB);
	/* Slave data */
   	I2C_WRITE(data);
	/* Stop */
   	I2C_STOP();
}

/* Examble for reading from I2C Slave */
unsigned char readI2CSlave(unsigned char data)
{
   	unsigned char inData;

	/* Start */
  	I2C_START(); 
	/* Slave address */
   	I2C_WRITE(0xAA);
	/* Slave control byte */
   	I2C_WRITE(data);
	/* Stop */
   	I2C_STOP();
	
	/* Start */
   	I2C_START();
	/* Slave address + read */
   	I2C_WRITE(0xAA | 1);
	/* Read */
	inData = I2C_READ();

   	return inData;                 
}

Comments:

kanoop
Said:
Whilst this snippet may be adequate for a very basic interaction with a single I2C peripheral, it lacks some of the more basic features that any I2C implementation should have. Sooner or later, someone will end up spending a long time trying to track down problems caused by this snippet. I would suggest it should, at the very minimum, include some sort of clock rate control and support clock stretching. Clock rate control is a core part of the I2C specification and deals with the minimum setup and hold times needed to allow I2C peripherals to function correctly. On a slow processor it will probably be fine, but any reasonably fast processor will quickly violate the I2C spec using this code. Clock stretching is easy as pie. Just add code to check the clock line after you've set it HIGH, and wait for it to actually go HIGH. Also, the structure used for the PORT definition is shaky. The 'C' standard does not ensure that bitfields in structures are implemented in any particular way, so this structure may work on one compiler and not on another. Finally, the PORT0 definition need to include the word 'volatile' to ensure that the compiler doesn't try to optimize accesses to the port. Like: #define PORT0 *(volatile PORT *)0x1234
6 years ago
0
Reply
Sorry, you need javascript enabled to post any comments.
Akshara
Said:
Do we need need to insert delays in between?. My slave device has maximum baud rate of 400Kbps. and the master clock runs at 40Mhz. That means one clock cycle is 2.5micro seconds. Iam unable to determine whether to include delays in between. My master runs at 62.3 micro sec. Could you pls elaborate how to determine the delay calculation. Pls guide me on this
3 years ago
0
Reply
Sorry, you need javascript enabled to post any comments.
Karalan
Said:
while(I2C_CLK); //bug. . . It should be while(!I2C_CLK);
11 months ago
0
Reply
Sorry, you need javascript enabled to post any comments.
MisterHemi
Said:
SCLK is undefined
10 months ago
+1
Reply
Sorry, you need javascript enabled to post any comments.
Charlie
Said:
Treat the code with caution there are 4-5 typos in here, this was never tested or compiled
10 months ago
0
Reply
Sorry, you need javascript enabled to post any comments.
Sorry, you need javascript enabled to post any comments.