I2C Details
An Autumn 2001 CSE466 Project by Mike Fernandes and Shirley Gaw

Home I2C Details Application Details Links

Summary of Using Our Interface

Functionality Source Files
I2C bus controller interface
I2C.h
I2C.c

 

Add I2C.c to your project.  Include I2C.h in your application.
Applications that use the I2C bus controller must call I2C_Init(unsigned char address, unsigned char* receiveBuffer) before using the bus controller to send and receive messages.
Although I2C allows infinitely sized messages, our implementation does not.  The size of the receiveBuffer passed in the initialization call limits the size of frames sent across the network.
Send messages across the network by calling I2C_Transmit(unsigned char address, unsigned char* buffer, unsigned char bufferSize).
Change the I2C_Interrupt() to call your transport layer's receive message function instead of SharedMemory_Receive.
Broadcast messages are unsupported.
If you wish to use P1 pins 1-7, remove any code that assigns them in I2C.c.  Use these pins to debug the I2C bus controller.
Master receiver mode and slave transmitter modes are not implemented. 

Implementation Details

As stated in the home page, we implemented an interface to the I2C bus controller1.  The code is in  I2C.c and I2C.h. The program uses memory mapped I/O to communicate with the bus controller, which basically has two registers to access - the control register and the selected (or destination) register which are selected using pin A0.  An application uses the control register to specify which destination register it will access on the I2C bus controller.  Access to the bus controller registers called through the procedures: I2C_ReadByte(), I2C_ReadCtrl(), I2C_SendByte(unsigned char c), and I2C_SendCtrl(unsigned char c)

Before using the bus controller, the 8051 must set up the I2C bus controller on the network by calling I2C_Init(unsigned char address, unsigned char* receiveBuffer).  This sets the address the node will responds to2 and it also sets details like the speed of the system clock.  The parameter receiveBuffer is used to keep a pointer of a buffer to store the temporarily store message contents as they are received.  Applications should note that the size of the receiveBuffer will limit the size of the message that can be sent over the I2C network though I2C allows messages of arbitrary length to be sent over the network.

The implementation of I2C_Init basically follows the initialization sequence though the control values used are different for specification of the control bits.  We avoid resetting the status bits by keeping PIN low (sending 0x20 instead of 0xA0 for a control byte for clock initialization).  We send 0x49 as the final control byte instead of 0xC1 so that external interrupts are enabled (ENI = 1) and to prevent resetting the status bits (PIN = 0)

When a node wants to send a message3 over the network, the program calls I2C_Transmit(unsigned char address, unsigned char* buffer, unsigned char bufferSize), where address is the destination node's address, buffer is the message to send, and bufferSize is how many characters are in the message.  When the procedure returns, the message has been sent over the network.  The implementation follows from Figure 6 of the bus controller datasheet except when the STOP condition is signaled by sending 0xC3 to the bus controller.  Since we want our nodes to be both master and slave, we need to enable external interrupts, so we send 0xCB instead.

I2C_Interrupt() is the interrupt handler for receiving messages asynchronously.  The implementation of this interrupt handler does not strictly follow the master receiver mode flow chart from the datasheet of the bus controller (Figure 7).  It does not check that the node was addressed as a slave, that that node received its own address when interrupted by the bus controller, and that the message received is for writing to the slave as it was all assumed to be true in our implementation.  Actually, we do check that our own address received is the first byte of a message.  The last byte in the message is sent twice - once before the stop condition is set and once after.  We ignored the second time the last byte is sent by double checking the value of the byte received is the address of the node when the byte count is -1 .   When the message has completely arrived, the interrupt handler signals higher level applications by calling SharedMemory_Receive(I2C_receiveBuffer, I2C_numberBytes).  Applications that wish to use our interface should replace this call with their own function call.

Applications that use the I2C interface we implemented should note that pins 1-7 of port 1 are currently used for debugging purposes - we use the values of the pins to track who code path flow at runtime.  Remove this code if you wish to use these pins.   

Footnotes:

1 As stated in the home page, our implementation assumes a master transmitter-slave receiver relationship for message passing.  A full implementation of the I2C bus controller interface would implement master receiver mode (Figure 7 in the bus controller datasheet), slave receiver mode (Figure 9), and repeated start (Figure 8).

2 The address sent by the caller of the procedure should note that I2C shifts the address by 1 bit.  We abstract this from the user by shifting the address by 1 bit whenever a message is transmitted.

3 Applications that use this procedure should not send messages to themselves nor broadcast messages over the network (set address to 0).   It should be the responsibility of the higher level network layers (such as datalink) to check for messages sent to yourself.  If a broadcast message was allowed by the application, the I2C_Interrupt() interrupt handler should be changed to check for that the node was addressed as slave and that that node received its own address when interrupted by the bus controller.