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

Help & Queries

If you have any queries, doubts or feedback on this tutorial please share in our discussion forum.

Powered by MediaWiki
This page was last modified on 6 April 2015, at 20:05.
ArrayContent is available under Creative Commons Attribution Non-Commercial Share Alike unless otherwise noted.