I2C Implementation on 8051 From Rikipedia Embedded Wiki
If you want to go through the basics of I2C, Then I would suggest reading I2C TWI Tutorial to get basic understanding of I2C on protocol level. This tutorial will help you understand on how to implement I2C on an 8051 microcontroller.
As you know 8051 e.g AT89C51 does not have on chip I2C controller so many times in our practical projects their is always a need of an I2C device like RTC or EEPROM for timekeeping or non-volatile storage respectively. To achieve we can emulate I2C on GPIOs of 8051 microcontroller to communicate between devices connected on I2C bus.
The basic concept of bit-bang i2c is same either you implement it in C or Assembly language. Following section provides a sample code which shows how to do this.
Implementing I2C in Assembly
For this implementation we are using P0.0 for SDA and P0.1 for SCL. Here we go...
;*************************************** ;Ports Used for I2C Communication ;*************************************** sda equ P0.0 scl equ P0.1 ;*************************************** ;Initializing I2C Bus Communication ;*************************************** i2cinit: setb sda setb scl ret ;**************************************** ;ReStart Condition for I2C Communication ;**************************************** rstart: clr scl setb sda setb scl clr sda ret ;**************************************** ;Start Condition for I2C Communication ;**************************************** startc: setb scl clr sda clr scl ret ;***************************************** ;Stop Condition For I2C Bus ;***************************************** stop: clr scl clr sda setb scl setb sda ret ;***************************************** ;Sending Data to slave on I2C bus ;***************************************** send: mov r7,#08 back: clr scl rlc a mov sda,c setb scl djnz r7,back clr scl setb sda setb scl mov c, sda clr scl ret ;***************************************** ;ACK and NAK for I2C Bus ;***************************************** ack: clr sda setb scl clr scl setb sda ret nak: setb sda setb scl clr scl setb scl ret ;***************************************** ;Receiving Data from slave on I2C bus ;***************************************** recv: mov r7,#08 back2: clr scl setb scl mov c,sda rlc a djnz r7,back2 clr scl setb sda ret
To use the above code in your project you can copy the source in your assembly file and call the subroutines. Here is a sample code which shows how to call these subroutines:
;***************************************** ; Write to slave device with ; slave address e.g. say 0x20 ;***************************************** ; Init i2c ports first lcall i2cinit ; Send start condition lcall startc ; Send slave address mov a,#20H acall send ; after send call Carry flag has ACK bit ; If you want to check if send was a ; success or failure ; Send data mov a,#07H acall send ; Send another data mov a,#10 acall send ; Send stop condition acall stop ;***************************************** ; Read from slave device with ; slave address e.g. say 0x20 ;***************************************** ; Init i2c ports first lcall i2cinit ; Send start condition lcall startc ; Send slave address with Read bit set ; So address is 0x20 | 1 = 0x21 mov a,#21H acall send ; Read one byte acall recv ; Send ack acall ack ; Read last byte acall recv ; Send nak for last byte to indicate ; End of transmission acall nak ; Send stop condition acall stop
Implementing I2C in C
The port pins are same as used in Assembly source
#define SDA P0_0 #define SCL P0_1 void I2CInit() { SDA = 1; SCL = 1; } void I2CStart() { SDA = 0; SCL = 0; } void I2CRestart() { SDA = 1; SCL = 1; SDA = 0; SCL = 0; } void I2CStop() { SCL = 0; SDA = 0; SCL = 1; SDA = 1; } void I2CAck() { SDA = 0; SCL = 1; SCL = 0; SDA = 1; } void I2CNak() { SDA = 1; SCL = 1; SCL = 0; SDA = 1; } unsigned char I2CSend(unsigned char Data) { unsigned char i, ack_bit; for (i = 0; i < 8; i++) { if ((Data & 0x80) == 0) SDA = 0; else SDA = 1; SCL = 1; SCL = 0; Data<<=1; } SDA = 1; SCL = 1; ack_bit = SDA; SCL = 0; return ack_bit; } unsigned char I2CRead() { unsigned char i, Data=0; for (i = 0; i < 8; i++) { SCL = 1; if(SDA) Data |=1; if(i<7) Data<<=1; SCL = 0; } return Data; }
Using C code is very simple. Here is a small example similar to assembly
/***************************************** * Write to slave device with * slave address e.g. say 0x20 *****************************************/ /* Init i2c ports first */ I2CInit(); /* Send start condition */ I2CStart(); /* Send slave address */ ack = I2CSend(0x20); /* * ack == 1 => NAK * ack == 0 => ACK */ ack = I2CSend(0x07); /* Send another data */ ack = I2CSend(0x10); /* Send stop condition */ I2CStop(); /***************************************** * Read from slave device with * slave address e.g. say 0x20 *****************************************/ /* Init i2c ports first - Should be done once in main */ I2CInit(); /* Send start condition */ I2CStart(); /* * Send slave address with Read bit set * So address is 0x20 | 1 = 0x21 */ I2CSend(0x21); data = I2CRead(); /* Send ack */ I2CAck(); /* Read last byte */ data = I2CRead(); /* * Send nak for last byte to indicate * End of transmission */ I2CNak(); /* Send stop condition */ I2CStop();
See Also
Read other tutorials using this i2c source
- DS1307 I2C RTC Interfacing Tutorial
- AT24xxx I2C EEPROM Interfacing
- LM75 I2C Temperature Sensor
- ads1115 I2C ADC Interfacing
Help & Queries
If you have any queries, doubts or feedback on this tutorial please share in our discussion forum.