I2C using GPIO C code

Understanding the I2C Bus: I2C using GPIO

The I2C bus is a very popular and powerful bus used for communication between a master (or multiple masters) and a single or multiple slave devices. Figure 1 illustrates how many different peripherals may share a bus which is connected to a processor through only 2 wires, which is one of the largest benefits that the I 2C bus can give when compared to other interfaces.

i2c_bus

i2c_bus

                                                Figure 1: Example  I2C Bus

I2C uses an open-drain/open-collector with an input buffer on the same line, which allows a single data line to be used for bidirectional data flow.

Open-drain refers to a type of output which can either pull the bus down to a voltage (ground, in most cases), or “release” the bus and let it be pulled up by a pull-up resistor. In the event of the bus being released by the master or a slave, the pull-up resistor on the line is responsible for pulling the bus voltage up to the power rail. I2C requires that if a master in a multi-master environment transmits a high, but see’s that the line is low (another device is pulling it down), to halt communications because another device is using the bus. Push-pull interfaces do not allow for this type of freedom, which is a benefit of I2C.

Open-Drain Pulling Low:

The Open-Drain setup may only pull a bus low, or “release” it and let a resistor pull it high. Figure 3 shows the flow of current to pull the bus low. The logic wanting to transmit a low will activate the pull-down FET, which will provide a short to ground, pulling the line low.

i2c_bus_low

Open-Drain Releasing Bus:

When the slave or master wishes to transmit a logic high, it may only release the bus by turning off the pull-down FET. This leaves the bus floating, and the pull-up resistor will pull the voltage up to the voltage rail, which will be interpreted as a high. Figure 4 shows the flow of current through the pull-up resistor, which pulls the bus high.

i2c_bus_high

General I2C Operation:

The I2C bus is a standard bidirectional interface that uses a controller, known as the master, to communicate with slave devices. A slave may not transmit data unless it has been addressed by the master. Each device on the I2C bus has a specific device address to differentiate between other devices that are on the same I2C bus. Many slave devices will require configuration upon startup to set the behavior of the device. This is typically done when the master accesses the slave’s internal register maps, which have unique register addresses. A device can have one or multiple registers where data is stored, written, or read.

The physical I2C interface consists of the serial clock (SCL) and serial data (SDA) lines. Both SDA and SCL lines must be connected to VCC through a pull-up resistor. The size of the pull-up resistor is determined by the amount of capacitance on the I2C lines. Data transfer may be initiated only when the bus is idle. A bus is considered idle if both SDA and SCL lines are high after a STOP condition.

The general procedure for a master to access a slave device is the following:

1. Suppose a master wants to send data to a slave:

  •  Master-transmitter sends a START condition and addresses the slave-receiver
  •  Master-transmitter sends data to slave-receiver
  •  Master-transmitter terminates the transfer with a STOP condition

2. If a master wants to receive/read data from a slave:

  •  Master-receiver sends a START condition and addresses the slave-transmitter
  •  Master-receiver sends the requested register to read to slave-transmitter
  •  Master-receiver receives data from the slave-transmitter
  •  Master-receiver terminates the transfer with a STOP condition

START and STOP Conditions

I2C communication with this device is initiated by the master sending a START condition and terminated by the master sending a STOP condition. A high-to-low transition on the SDA line while the SCL is high defines a START condition. A low-to-high transition on the SDA line while the SCL is high defines a STOP condition.

i2c_start_stopRepeated START Condition:

A repeated START condition is similar to a START condition and is used in place of a back-to-back STOP then START condition. It looks identical to a START condition, but differs from a START condition because it happens before a STOP condition (when the bus is not idle). This is useful for when the master wishes to start a new communication, but does not wish to let the bus go idle with the STOP condition, which has the chance of the master losing control of the bus to another master (in multi-master environments).

Data Validity and Byte Format:

One data bit is transferred during each clock pulse of the SCL. One byte is comprised of eight bits on the SDA line. A byte may either be a device address, register address, or data written to or read from a slave. Data is transferred Most Significant Bit (MSB) first. Any number of data bytes can be transferred from the master to slave between the START and STOP conditions. Data on the SDA line must remain stable during the high phase of the clock period, as changes in the data line when the SCL is high are interpreted as control commands (START or STOP).

byte_transffer

Acknowledge (ACK) and Not Acknowledge (NACK)

Each byte of data (including the address byte) is followed by one ACK bit from the receiver. The ACK bit allows the receiver to communicate to the transmitter that the byte was successfully received and another byte may be sent.

Before the receiver can send an ACK, the transmitter must release the SDA line. To send an ACK bit, the receiver shall pull down the SDA line during the low phase of the ACK/NACK-related clock period (period 9), so that the SDA line is stable low during the high phase of the ACK/NACK-related clock period. Setup and hold times must be taken into account.

When the SDA line remains high during the ACK/NACK-related clock period, this is interpreted as a NACK. There are several conditions that lead to the generation of a NACK:

  1. The receiver is unable to receive or transmit because it is performing some real-time function and is not ready to start communication with the master.
  2. During the transfer, the receiver gets data or commands that it does not understand.
  3.  During the transfer, the receiver cannot receive any more data bytes.
  4. A master-receiver is done reading data and indicates this to the slave through a NACK.

i2c_NACK

Writing to a Slave On The I2C Bus

To write on the I2C bus, the master will send a start condition on the bus with the slave’s address, as well as the last bit (the R/W bit) set to 0, which signifies a write. After the slave sends the acknowledge bit, the master will then send the register address of the register it wishes to write to. The slave will acknowledge again, letting the master know it is ready. After this, the master will start sending the register data to the slave, until the master has sent all the data it needs to (sometimes this is only a single byte), and the master will terminate the transmission with a STOP condition.

i2c_write

Reading From a Slave On The I 2C Bus:

Reading from a slave is very similar to writing, but with some extra steps. In order to read from a slave, the master must first instruct the slave which register it wishes to read from. This is done by the master starting off the transmission in a similar fashion as the write, by sending the address with the R/W bit equal to 0 (signifying a write), followed by the register address it wishes to read from. Once the slave acknowledges this register address, the master will send a START condition again, followed by the slave address with the R/W bit set to 1 (signifying a read). This time, the slave will acknowledge the read request, and the master releases the SDA bus, but will continue supplying the clock to the slave. During this part of the transaction, the master will become the master-receiver, and the slave will become the slave-transmitter.

The master will continue sending out the clock pulses, but will release the SDA line, so that the slave can transmit data. At the end of every byte of data, the master will send an ACK to the slave, letting the slave know that it is ready for more data. Once the master has received the number of bytes it is expecting, it will send a NACK, signaling to the slave to halt communications and release the bus. The master will follow this up with a STOP condition

i2c_read

Arbitration:

However, it may happen that two masters start a transfer at the same time. If two devices start to communicate at the same time the one writing more zeros to the bus (or the slower device) wins the arbitration and the other device immediately discontinues any operation on the bus. During the transfer, the masters constantly monitor SDA and SCL. If one of them detects that SDA is low when it should actually be high, it assumes that another master is active and immediately stops its transfer. This process is called arbitration. If the bus is busy, masters delay pending I2C transfers until a stop condition indicates that the bus is free again.

for example Master M1 like to transfer ‘0100 1000’ & Master M2 like to transfer ‘0001 0001’ when the twp master detects the stop condition. Both master would attempt to transfer MSB bit of 0. Since both device are pulling the bus low that shouldn’t be a problem. Now the second bit of M1 (‘1’)  which pull bus high & M2 (‘0’)  which pull bus low. Now M1 could realize that some other device is also making use of the bus since even though it attempted to send a 1 , the line remains low indicating some other device is making use of the bus. M1 immediately stops the data transfer , backs off and waits for the next stop condition to occur for re-initiating the data transfer.

Please find the attached c code for 8051 microcontroller uising general purpose I/O pins i2c_code.  Let me know if any issue in using this code.

Leave a Reply

Your email address will not be published. Required fields are marked *