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

As with the SPI module, the I2C module needs to be reset before configuration and use. The sequence below is similar to the sequence defined in the document covering the Zynq’s SPI module. The only difference being, the module being reset (so a different address for the reset register).

//Necessary SLCR Registers
#define SLCR_LOCK		*( (uint32_t *) 0xF8000004)
#define SLCR_UNLOCK		*( (uint32_t *) 0xF8000008)
#define SLCR_IIC_RST 	*( (uint32_t *) 0xF8000224)

//lock and unlock keys
#define UNLOCK_KEY	0xDF0D
#define LOCK_KEY	0x767B

void reset_iic(void)
	int i=0; //i for delay
	SLCR_IIC_RST = 0x3;	//assert I2C reset
	for(i=0;i<1000;i++); //wait
	SLCR_IIC_RST = 0;	//deassert

The I2C Control register

The upper 8 bits of the Zynq I2C module’s Configuration are for configuring the clock divider; Bits 15:14 define the stage A divider, and bits 13:8 control the stage B divider.

Bit 7 is reserved and must always be clear.

The lower 7 bits define the behavior of the I2C module.

Bit 6 is the FIFO clear bit. Setting this bit clears out both the RX and TX FIFOs as well as setting the transfer size to 0. This bit will clear automatically if set.

The Slave monitor enable bit (bit 5) enables slave monitor mode. This mode has limited usefulness and thus, the bit should be left clear.

The Hold bit (bit 4) defines the I2C module’s behavior after the number of bytes defined in the transfer size register have been sent/received. If the hold bit is set, the I2C module (bus master) will hold SCL low instead of ending the I2C transaction. This will allow the processor to send more data or request more data without starting a new transaction.

Bit 3 enables the generation of Acknowedgement (ACK) from the bus master when acting as a receiver. This signifies the data received from a slave was accepted. For most cases this bit should be set to 1.

Bit 2 defines which I2C Addressing mode the Device will use. This bit should always be set, as the Zynq’s technical reference manual states only 7-bit addresses are supported.

Bit 1 defines whether the Zynq’s I2C module acts as a slave or a master on the I2C bus. Since there are no other devices on the blackboard that can be bus masters, this bit should be set.

Bit 0 defines the direction of the transfer; 1 performs reads on the I2C bus (receives data from slave devices) and 0 sets the I2C module as a transmitter (writes)

Configuring the I2C Module

Below an example configuration for communicating with the I2C connected NXP Temperature sensor is covered.

I2C is commonly operated at 400KHz. The temperature sensor connected via I2C1 supports this speed. To get this clock value, Divisor A can be set to zero and Divisor B can be 12.

An example of the lower 7 bits of the I2C control register is given below.

  • Slave monitor mode disabled
  • Bus hold disabled
  • Acknowledge enabled
  • 7-bit addressing
  • Configured for Master mode

The transfer direction bit should be set prior to a transaction and reflect the direction desired for the transfer. The FIFO can also be cleared (via CR bit 6) prior to starting bus transfers.

Starting an I2C Transaction

The I2C module module works differently than the SPI and UART modules, where transfers are not started by writing to FIFO’s. Instead the Zynq’s I2C module starts a bus transfer when an address is written to the address regsiter. Note: The transfer size register’s function will act differently based on the type of transfer, these differences are detailed below.

I2C Transaction as a receiver (I2C Read)

If the I2C module is configured to be a reciever, the Transfer Size Register will define how many bytes are expected to be received. This should be set prior to starting the transfer. After the transaction is started, the module will continue receiving bytes until the number of entries in the RX FIFO matches the value in the transfer size register. If a byte is read from the RX FIFO prior to the transaction completed, the value in the transfer size register must be decremented. Otherwise an extra byte will be read.

To read a number of bytes over I2C on Zynq, you can follow this sequence.

  1. Configure The I2C module as a Master receiver
  2. Initialize the FIFO by writing a 1 to bit 6 of the I2C control register
  3. Write the number of bytes requested to the transfer size register.
  4. Write the address of the I2C slave being contacted in the address register.

After writing the address, the I2C module will take control of the bus and send the requested slave’s address over the bus (as well as a read bit). The module will then run the I2C clock for 8*N clock cycles (N being the number of bytes requested). Each byte in order will appear in the I2C’s RX FIFO. If more than 16 bytes are requested, the processor must read the FIFO before it is full. Otherwise receive data will be lost. If the FIFO is read before the transaction completes, the value in the transfer size register must be decremented every time a byte is read.

Transfer Complete bit in the I2C’s Interrupt Status Register (ISR) can be checked to see if the bus transaction has completed. If you use this bit to determine transfer status, make sure you clear the ISR status before starting a I2C transaction. You can do this by writing the register’s value to itself.

I2C Transaction as transmitter (I2C Write)

To write a number of bytes over I2C on Zynq, you can use the following sequence

  1. Configure the I2C module as a Master transmitter
  2. Initialize the FIFO by writing a 1 to bit 6 of the control register.
  3. Load transmit data into the TX FIFO
  4. Write the address of the I2C slave being written to in the address register.

After step 4 the transfer will commence. The I2C module takes control of the bus and sends the slave’s address. This is followed by the master (the I2C module) sending data over the bus. The master will continue to send bytes as long as there is data in the TX FIFO. If more than 16 bytes are being sent, the TX FIFO must be filled with the additional data before it underflows.

During an I2C Write, the transfer size register will display how many bytes are yet to be written. This number will change based on how many entries are in the TX FIFO.

You can use the transfer complete bit in the ISR as detailed in the read process written above. Again, make sure you clear the ISR status before starting a transfer.