I2C Controller

Configuring and using the I2C bus on ZYNQ

10056

The Inter-integrated circuit (I2C) bus is a synchronous, multi-master/multi-slave, packet switched serial bus that is widely used for low-speed communications in digital systems. The bus uses two bidirectional, open-drain signals (serial data, or SDA, and serial clock, or SCK) that can carry data at up to 400Kbits/sec, and up to 3.4MHz in some newer, high speed implementations. I2C data is sent in “packets” of bytes that include an address, framing bits, and a variable-length payload. You can learn more about the I2C bus here: The I2C BUS

Blackboards I2C Bus

ZYNQ’s includes two I2C Version 2 controllers that can operate up to 400KHz. Only one port (I2C1) is connected on Blackboard – the other port (I2C0) can optionally be connected to a Pmod port using the EMIO interface. The I2C1 port is connected through MIO pins to the LM75B temperature sensor using address 0b1001000. You can read more about the temperature sensor here: The LM75B Temperature Sensor

Figure 1. ZYNQ I2C Bus connections on Blackboard
Figure 1. ZYNQ I2C Bus connections on Blackboard

ZYNQ’s I2C Controllers

ZYNQ’s I2C controllers are documented in chapter 20 of the ZYNQ TRM. Several of the most useful features and register definitions are also described here.

The I2C controllers contain a programmable clock generator and read/write FIFOs, and they can be used in master mode or slave mode. In master mode, a write transfer is initiated by writing a slave address to the I2C address register. All data previously placed in the 16-byte write FIFO will be automatically serialized and sent, until the FIFO is empty. The CPU can poll a status bit to check when the write transfer is complete, or interrupts can be configured to alert the processor at the end of the transfer.

A read transfer is initiated by writing the number of requested bytes into the transfer size register, and then writing a slave address to the I2C address register. Incoming SDA data will be parallelized and loaded into the 16-byte read FIFO, until the number of requested bytes has been received. Read status can be monitored by a polling loop that checks a read status flag, or a transfer-complete interrupt can be configured. The processor can fetch the data when the read is complete.

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.

Clock Management

Each I2C controller receives a 111MHz master clock input from the processing system. The clock input is routed to a 2-bit programmable divider (DIVA), and then to a 6-bit programmable divider (DIVB) to produce an “I2C_Sample_clock”. The I2C_Sample_clock is used to sample the incoming SDA data signal, and it is fed to a third fixed divider (divide by 22) that produces the lower frequency SCL I2C bus clock. Both programmable clock divider values are bit fields in the I2Cconfiguration register.

Figure 2. I2C Clock Management
Figure 2. I2C Clock Management

The I2C_Sample_clock uses a higher frequency so it can sample the SDA signal many times during the data window defined by the SCK. This “oversampling” of the SDA signal allows the I2C controller to do a better job of sampling SDA in the middle of the data window, and also to apply a simple filter to do a better job of rejecting possible signal noise.

FIFOs

Both the transmit and receive data paths include a 16-byte FIFO, and both FIFOs are accessed using the same 8-bit data port. Several FIFO status bits are automatically produced, and those bits can be polled or they can drive interrupts. You can read more about managed FIFOs here:

Transmit data written to the FIFO will automatically be serialized and sent out the SDA pin after an address is written to the slave address register. Data is sent as a packet containing some number of “payload” bytes immediately after the slave address (and read/write mode bit) is sent. After each byte, the slave must respond with an ACK bit or the write is interrupted. Once the write FIFO is empty, new write data can be placed in the FIFO and a new write transfer can be initiated. The processor can poll status bits or setup interrupts to check with the write FIFO has been emptied.

During a read transfer, the I2C controller oversamples and filters the SDA signal, and places received data into the read FIFO. At the end of a read transfer (when a stop condition is detected), a status bit will be set, and an optional interrupt can be generated.

Figure 3. I2C FIFOs
Figure 3. I2C FIFOs
Registers

The available I2C configuration and status registers are shown below. Note the status register bits can be polled, and they can also drive interrupts.

Figure 4. I2C Controller Registers
Figure 4. I2C Controller Registers

I2C_CR (0x0000): Configuration register for overall configuration settings (read/write)

DIVA: Divisor A – divides clock by DIVA + 1
DIVB: Divisor B – divides the output clock produced by DIVA by DIVB + 1
CF: Clear FIFO to all zero’s (1 to clear – automatically reset to 0)
SM: Slave monitor (1 to enable monitor mode)
HB: Hold bus (1 to hold sck low, 0 to allow sck to float high to terminate cycle)
ACK: Acknowledge enable (1 to enable ACK and transmit acknowledge)
AM: Addressing mode (1 for normal 7-bit mode)
M: Master mode select (1 for master mode, 0 for slave mode)
D: Direction (1 for master receiver, 0 for master transmitter)


I2C_SR (0x0004): Status register (read only)

BA: Bus Active (1 if ongoing bus traffic)
RO: Receive Overflow (1 if FIFO is full when new data arrives; new data is ignored)
TV Transmit Data Valid (1 if data being transmitted)
RV: Receiver Data Valid (1 if new data to be read)
RM: Receiver mode (1 if transmission mode received from a master)


I2C_ADDR (0x0008): Slave address register (read/write)

ADDR: 7-bit address
ExADD: Additional bits for 10-bit address


I2C_DATA (0x000C): Data register (read/write)

DATA: Data. When written, starts data transmission. When read, gives last received byte


I2C_ISR (0x0010): Interrupt Status register (write to clear)

AL: Arbitration Lost (1 if master loses bus ownership due to arbitration)
FU: FIFO Underflow (1 if host attempts to read more times than TRANS_SIZE + 1)
FO: FIFO Overflow (1 if host tries to write more data than FIFO depth)
RO: Receive Overflow (1 if FIFO full and new byte received – new byte ignored)
SR: Slave Ready (1 if slave returned ACK)
TO: Transfer Time Out (1 if sck kept low for longer time)
NA: Transfer Not Acknowledged (1 if slave responds with NACK)
MD: More Data (1 if data currently being sent or received)
TC: Transfer Complete (1 if transfer complete)


I2C_TRANS_SIZE (0x0014): Transfer size (read/write).

TSIZE: Transfer size – mode dependent. Master transmitter = number of bytes not yet transmitted minus one; Master receiver = number of bytes still expected; Slave transmitter = number of bytes still in FIFO after master terminates transfer; Slave receiver = number of bytes in FIFO


I2C_PAUSE (0x0018): Pause interval (read/write)

PAUSE: Pause interval


I2C_TIMEOUT (0x001C): Time out register (read/write)

TIMEOUT: Timeout register (31 – 255)


I2C_IMR (0x0020): Interrupt Mask Register (read only). Bits in this regsiter are set by writing the IER and/or IDE.

AL: Arbitration Lost
FU: FIFO Underflow
FO: FIFO Overflow
RO: Receive Overflow
SR: Slave Ready
TO: Transfer Time Out
NA: Transfer Not Acknowledged
MD: More Data
TC: Transfer Complete


I2C_IER (0x0024): Interrupt Enable Register (write only). Write 1 to enable corresponding interrupt.

AL: Arbitration Lost
FU: FIFO Underflow
FO: FIFO Overflow
RO: Receive Overflow
SR: Slave Ready
TO: Transfer Time Out
NA: Transfer Not Acknowledged
MD: More Data
TC: Transfer Complete


I2C_IDR (0x0028): Interrupt Disable Register (write only). Write 1 to disable corresponding interrupt.

AL: Arbitration Lost
FU: FIFO Underflow
FO: FIFO Overflow
RO: Receive Overflow
SR: Slave Ready
TO: Transfer Time Out
NA: Transfer Not Acknowledged
MD: More Data
TC: Transfer Complete

Configuring the I2C controller

ZYNQ’s I2C controller should be reset prior to configuration to ensure it begins operating in the proper state. Software reset of the I2C controller is accomplished by writing to a protected system level control register (SLCR), and like all SLCR, the I2C reset register must be unlocked before it can be accessed. To unlock an SLCR, you must write an unlock code to the unlock register as shown below. After accessing the intended SLCR, access should be relocked.

//SLCR Register addresses and lock/unlock key values
#define SLCR_LOCK	*( (uint32_t *) 0xF8000004)
#define SLCR_UNLOCK	*( (uint32_t *) 0xF8000008)
#define SLCR_IIC_RST 	*( (uint32_t *) 0xF8000224)
#define UNLOCK_KEY	0xDF0D
#define LOCK_KEY	0x767B

void reset_iic(void)
{

	SLCR_UNLOCK = UNLOCK_KEY;	//unlock SLCRs
	SLCR_IIC_RST = 0x3;		//assert I2C reset
	SLCR_IIC_RST = 0;		//deassert I2C reset
	SLCR_LOCK = LOCK_KEY;		//relock SLCRs
}

After the I2C controller has been reset, it can be configured by setting bit fields correctly in the Configuration Register. To configure the I2C controller to read the temperature module, you can follow the discussion below. Note that the settings discussed are specific to the Blackboard and the LM75B temperature IC – different I2C slaves will likely have different configuration requirements.

The upper byte of the CR defines I2C clocking. The LM75B can communicate at 100KHz or 400KHz (we may as well use the faster speed of 400KHz). There are a few choices of DIVA and DIVB that result at a 400KHz SCL – one choice is DIVA = 0 and DIVB = 12.

The lower CR byte defines the remaining settings:

  • Bit 7 is reserved and must always be ‘0’.

  • Bit 6 clears the FIFOs – writing a ‘1’ to bit 6 clears the FIFOs and transfer size setting, and the bit is automatically cleared (set back to 0) when the reset is complete;

  • Bit 5 enables slave monitor mode, which is not relevant here, so it can be cleared to a 0;

  • Bit 4 defines behavior after a transfer has completed – if it is set, the master holds SCK low to prevent other possible masters from driving the bus, so that it can immediately do another transaction. Since the ZYNQ is the only master, bit 4 can be cleared to a 0;

  • Bit 3 enables the generation of an ACK from the master when acting as a receiver. Although not relevant here, in most cases this feature should be enabled (and so, set bit 3 to a 1);

  • Bit 2 defines the addressing mode (1 for 7-bit). ZYNQ only supports 7-bit addressing, so this bit should be set to a 1;

  • Bit 1 defines whether the ZYNQ I2C port acts as master or slave. Since there are no other masters on the Blackboard, this bit should be set to a 1;

  • Bit0 defines transfer direction and must be set prior to reads or writes (1 to receive/read, 0 to transmit/write). Since we will just read data from the LM75B, bit 0 can be cleared to 0.

Read and write transfers can commence after the I2C bus has been configured. I2C transfers are imitated by writing the slaves address to the address register (the LM75B address is set to 0b1001000).

I2C Read

Prior to writing the slave address to start a read transaction, you must set the direction (bit 0 in the CR) and write the number of bytes to be received in the TRANS_SIZE register (for the LM75B, two bytes will generally be read). After the slave address is written, the I2C controller will operate the bus until there are TRANS_SIZE bytes in the FIFO (note that if a byte is read before the transfer is complete, the TRANS_SIZE value should be decremented, or else an extra byte will be read). The transferred bytes will be available in the RXD FIFO in the order they were received (if more than 16 bytes are requested, the FIFO must be read “in flight” or data will be lost). The Transfer Complete bit the interrupt status register (ISR) can be polled to see when the transfer is complete (or interrupts can be enabled). If you use polling, the TC bit in the ISR must be manually cleared (by writing a 1) between transactions.

In summary, to do an I2C read transaction:

  • Configure the I2C module as a master receiver (setup the CR once at the outset);
  • Clear the FIFO (write CR bit 6 once at the outset);
  • Define the transfer direction as read (write CR bit 0 to 0);
  • Write the number of bytes to read in the TRANS_SIZE register (and update this value if different read cycles need different numbers of bytes);
  • Write the I2C slave address to the ADDR register;
  • Set up interrupts to be alerted when the transfer is complete, or check the TC bit in the ISR.

I2C Write

Prior to writing the slave address to start a write transaction, you must set the direction (bit 0 in the CR). Once the write has started, it will continue until the FIFO is empty. If more than 16 bytes are to be sent, the FIFO must be refilled in flight (before it empties). During a write, the TRANS_SIZE register will show the number of bytes still in the FIFO. The Transfer Complete bit the interrupt status register (ISR) can be polled to see when the transfer is complete (or interrupts can be enabled). If you use polling, the TC bit in the ISR must be manually cleared (by writing a 1) between transactions.

In summary, to do an I2C write transaction:

  • Configure the I2C module as a master receiver (setup the CR once at the outset);
  • Clear the FIFO (write CR bit 6 once at the outset);
  • Define the transfer direction as write (write CR bit 0 to 1);
  • Write the I2C slave address to the ADDR register;
  • Set up interrupts to be alerted when the transfer is complete, or check the TC bit in the ISR.