Using the I2C bus on Blackboard


The ZYNQ contains two version-2 I2C controllers that can operate from nearly DC to 400KHz. On the Blackboard, one I2C port is connected through MIO pins to a temperature sensor, and the other can be connected through the EMIO interface to the inertial module. Both controllers can use normal 7-bit addresses, or extended 10-bit addresses.

In master mode, a transfer is initiated by writing the slave address to the I2C address register. Then for reads, the processor can poll the controller to check when data has been received, or configure a “transfer complete” interrupt. For writes, Data can be written to the DATA register. Write data enters a 16-byte FIFO, and from there the I2C controller automatically serializes and transmits it until the FIFO is empty. The processor can poll to check when the write FIFO is empty, or configure a “transfer complete” interrupt.

Before the I2C controller can be used, it’s clock speed and addressing mode must be configured (you can chose the standard 7-bit addressing mode, or the extended 10-bit mode). The controllers can also be configured as slave devices by clearing the “MS” bit in the control register, and providing a slave address.

The available I2C configuration and status registers are shown in the table below. See the BBPR or ZYNQ TRM for more detailed information on the registers.

Table 1. ZYNQ's I2C Registers
Table 1. ZYNQ’s I2C Registers

The I2C uses a 16-byte FIFO for both reads and writes. I2C read/write status and the state of the FIFOs can be read from the status register, or interrupts can be setup to alert you when read/write or FIFO status changes. The tables below shows the status bits that are available in the status register, and the available interrupt sources.

Table 2. ZYNQ's I2C Status Register Bits
Table 2. ZYNQ’s I2C Status Register Bits
Table 3. ZYNQ's I2C Interrupts
Table 3. ZYNQ’s I2C Interrupts

The bit fields available in the Configuration Register are shown below.

Table 4. ZYNQ's I2C Configuration Register Bits
Table 4. ZYNQ’s I2C Configuration Register Bits


The I2C interface uses the main CPU clock source (111MHz on the Blackboard). The main clock is subjected to two programmable divisor values that are set in the Configuration Register to produce the “Clock Enable” signal. Clock Enable is used to sample the incoming SCL and SDA signals, and it is further divided by 22 to produce SCL.

Figure 1. I2C Clock Configuration
Figure 1. I2C Clock Configuration

The formulas used for creating the clocks signals are:

  • Clock Enable = 1 / (divisor A + 1) * (divisor B + 1);
  • SCL = Clock Enable / 22.

Configuring the I2C controller

Before configuring the I2C controller, you must perform a software reset to ensure it begins operating in the proper state. The I2C controller is a protected system resource, and it can only be reset after unlocking write control for the “System Level Control Registers” (see the ZYNQ TRM page 1578). After unlocking the SLCRs, you can write 1’s to bit fields in the I2C_RST_CNTL register to cause an I2C reset, and then write 0’s to release the reset. The function below illustrates the SPI reset procedure:

 #define MOD_RESET_BASEADDR 0xF8000000
void SPI_reset(){
	*((uint32_t *) MOD_RESET_BASEADDR+0x8/4) = 0x0000DF0D;  		// unlock the SLCRs
	*((uint32_t *) MOD_RESET_BASEADDR + 0x00000224/4) = 0x3;		// Reset SPI0
	*((uint32_t *) MOD_RESET_BASEADDR + 0x00000224/4) = 0;			// Release the reset

After the I2C controller has been reset, it can be configured by setting bit fields correctly in the Configuration Register. A summary of the bit fields is provided above, but you will need to reference the Blackboard Programmers Reference or the ZYNQ TRM for specific information on the bit values. Further, since different peripheral devices use different SPI configurations, you will also need to reference the data sheet of the device to learn what SPI configuration must be used.

To configure the I2C controller to read the temperature module, write bit values to the Configuration Register to:

  1. Set Clock Divisor A to 0 and Divisor B to 12 to generate a 400KHz SCL;

  2. Initialize the FIFO to all zeros by setting the Clear FIFO bit (bit6 = 1);

  3. Set slave monitor mode to normal operation (bit5 = 0);

  4. Do not hold the bus when transaction completed (bit4 = 0);

  5. Enable ACK (bit3 = 1);

  6. Set addressing mode normal 7-bit (bit2 = 1);

  7. Set overall mode to Master (bit1 = 1);

  8. Set direction as master receiver (bit0 = 1). After the I2C controller has been configured, you can create a C function to read the bus. The function should perform the following steps:

  9. Write the desired number of bytes (data count = 2) to be transferred to the I2C_T_Size (transfer size) register;

  10. Write the address of the temperature sensor (1001000) to the ADDR register;

  11. Poll on the RXDV bit from the I2C_Status Register (bit5) every time after reading a byte of data;

  12. Read data from the FIFO (DATA register) and decrement the read data count (TRANS_SIZE) until data count = 0