I2C Tips

From Texas Instruments Wiki
Jump to: navigation, search

Overview

Most TI devices (c5000, c6000, OMAP, Davinci, etc.) use the same underlying I2C module. This wiki page is intended to be generic to all these devices. However, recognize the fact that there may be some device dependent subtleties to the I2C. The silicon errata for a specific device is a good place to look for such items. For example, the repeated start feature is broken on 5507.

Clock Problems

A very common mistake made by users of the I2C module is to incorrectly configure the clocks. The following diagram is an excerpt from page 14 of spru175d (c6000 I2C Reference Guide):

I2cTips screenshot1.JPG

Pay close attention to the pieces circled in red. The input clock to the I2C must be between 6.7 and 13.3 MHz for the case of the c6000 device family. This number could potentially change by device or process node, so please check the user guide for your specific device. For example, the 6424 specifies a range of 7-12 MHz. In general though, failure to properly configure the prescaler can result in "flaky" behavior from the I2C module.

Also notice how the frequency is calculated. Notice the value 'd' inside the calculation and make sure you correlate that value with the table below. Furthermore note that due to the nature of I2C and the requirement to observe (potential) clock stretching, the observed frequency will actually be a little slower since the I2C peripheral needs to for the clock to rise in case the slave wants to hold it low. So the slower the rise time the more deviation you'll see from this calculated frequency.

So in programming these clock frequencies, here are the steps you should take:

  1. Figure out the speed of the input clock to the I2C module. Let's take a 600 MHz DM642 for example. The datasheet shows the I2C module being clocked at CPU/4 (150 MHz in this case). For a 6424 the PLL diagram in the datasheet shows the I2C to be directly fed with the AUXCLK which is equivalent to CLKIN/1. So for example, the 6424 DSK uses a 27 MHz input clock and so that would directly feed the I2C periphal.
  2. Calculate the prescaler value. For the DM642 with the 150 MHz clock feeding the I2C we could choose I2CPSC=14 (/15) such that the I2C module receives a 10 MHz pre-scaled clock. For our 6424 example we should set I2CPSC=2 (/3) to get a 9 MHz pre-scaled clock.
  3. Calculate ICCL/ICCH. Normally one would want the SCL pin to operate at a specific frequency, generally 100 kHz or 400 kHz. Let's assume we want 50% duty cycle (e.g. ICCL=ICCH). Now let's plug that info into the formula shown in the figure above to calculate the ICCL/ICCH values. For both our DM642 example and our 6424 example we would use d=5 since our prescaler was greater than or equal to 2 for both of them. For the DM642 example this comes to ICCL=ICCH=45. For the 6424 this gives an answer of ICCL=ICCH=40.
  4. Program the values while the I2C is in reset. There are multiple mentions of this fact in the documentation, but it is easy to forget about. When programming the prescaler and the ISCL/ISCH registers you need to make sure the I2C module is being held in reset.
  5. Double-check yourself. You've done all the math. You've programmed all the registers. Now do yourself a favor and measure the resulting SCL frequency with an oscilloscope. If it does not match exactly what you programmed it to be then you have made a mistake in one of these steps. Be sure that everything works the way you intended it to work. If the frequency comes out to something different then you may have the I2C prescaler out of spec which could result in failures either at different temperatures or on different devices.

Detecting and handling NACK

A common mistake is to neglect checking for a NACK. When everything is working properly there is no problem. However, checking for a NACK and handling it can save you lots of time. For example, if you have a board with a hardware problem then the NACK detection can quickly help you identify those issues.

If you are operating the I2C through polling, you simply need to change your polling such that you poll XRDY/RRDY as well as ARDY.

So you might have started with code like this:

// BAD CODE!!! Would hand in case we get NACKed!
// Wait for "XRDY" flag to transmit data
while ( !(*I2C_STR & ICSTR_ICXRDY) );

However, you should change it to look like this:

// Wait for "XRDY" flag to transmit data or "ARDY" if we get NACKed
while ( !(*I2C_STR & (ICSTR_ICXRDY|ICSTR_ARDY)) );
 
// If a NACK occurred, SCL is held low and STP bit cleared
if ( *I2C_STR & ICSTR_NACK )
{
	*I2C_MDR |= ICMDR_STP;	// send STP to end transfer
	*I2C_STR = ICSTR_NACK;	// clear NACK bit
	return I2C_FAIL;
}

If you are using interrupts then you should be sure to enable the NACK interrupt and to check for it in the I2C ISR.

Hardware Response to a NACK

  • Clears the I2CMDR.STP
  • Holds SCL low
  • Sets I2CSTR.NACK

User Response to a NACK

  • Set I2CMDR.STP which will send a "stop" bit and release SCL.
  • Write I2CSTR.NACK=1 to clear the flag (it's a write-1-to-clear bit)
  • Wait for I2CMDR.MST to self-clear before initiating any further I2C transactions. When MST clears the controller has finished sending the stop bit. This can be done most efficiently by checking for I2CMDR.MST==0 at the start of the function. That will allow the processor to do other stuff during that period and still prevents new transactions before it has completed.

Repeated Start

A typical use case for a repeated start is doing a read from an EEPROM. In order to do so you would normally first do a write of the address from which you want to read, followed by a repeated start and then a read of that address. A repeated start can be accomplished by writing to the start bit (STT). The only "trick" is to set the bit at the correct time. The bit of interest in this case is the ARDY bit. You can either poll for this bit or get an interrupt. In either case, you should wait for ARDY before you re-program any of the I2C registers (e.g. switch from write to read and do the repeated start).

Here's an example that was written on DM355 (note this was not using Linux). It was to read a register from an AIC3204 data converter, so it is very similar to reading from an EEPROM. That is, first you write the address of the register and then perform a repeated start and then perform the read operation:

  /* Check for Bus Busy */
  while ((*I2C_ICSTR & ICSTR_BB));
 
  /* Disable I2C during configuration */
  *I2C_ICMDR = 0;
 
  /* Set the I2C controller to write len bytes */
  *I2C_ICCNT = write_len;
  *I2C_ICSAR = slaveaddr;
  *I2C_ICMDR = ICMDR_IRS | ICMDR_STT | ICMDR_TRX | ICMDR_MST | ICMDR_FREE;
 
  /* Transmit data */
  for (i = 0; i < write_len; i++)
  {
	// Wait for "XRDY" flag to transmit data or "ARDY" if we get NACKed
	while ( !(*I2C_ICSTR & (ICSTR_ICXRDY|ICSTR_ARDY)) );
 
	// If a NACK occurred then SCL is held low and STP bit cleared
	if ( *I2C_ICSTR & ICSTR_NACK )
	{
			*I2C_ICMDR = 0;		// reset I2C so SCL isn't held low
			return RRET__FAIL;
	}
    *I2C_ICDXR = write_data[i];
  }
 
  // wait for ARDY before beginning the read phase of the transaction
  while ( !(*I2C_ICSTR & ICSTR_ARDY) );
 
  /* Set the I2C controller to read len bytes */
  *I2C_ICCNT = read_len;
  *I2C_ICMDR = ICMDR_IRS | ICMDR_STT | ICMDR_STP | ICMDR_MST | ICMDR_FREE;
 
  /* Receive data */
  for (i = 0; i < read_len; i++)
  {
	// Wait for I2C to read data or or "ARDY" if we get NACKed
    while( !(*I2C_ICSTR & (ICSTR_ICRRDY|ICSTR_ARDY)) ) {};
 
	// If a NACK occurred then SCL is held low and STP bit cleared
	if ( *I2C_ICSTR & ICSTR_NACK )
	{
			*I2C_ICMDR = 0;		// reset I2C so SCL isn't held low
			return RRET__FAIL;
	}
 
    // Make sure that you got the RRDY signal
    while( !(*I2C_ICSTR & ICSTR_ICRRDY) ) {};
 
	read_data[i] = *I2C_ICDRR;
  }

Zero Length Transfer

A zero length transfer is useful for doing "bus probing". That is, you can check to see if a slave of a given address is on the bus without actually doing any transfers to/from that slave.

When using the I2C peripheral in the non repeat mode (i.e. RM=0) there is a hardware counter for keeping track of the number of elements being transferred. In this mode you must specify a value for the count register that is greater than 0 in order for the transfer to take place. Therefore, a zero-length transfer is not possible with RM=0.

If using the repeat mode (RM=1) then you can do as many transfers as you want, including none at all! In the case of RM=1, as soon as you set the start bit (STT) then the hardware will respond by outputting a start bit, the slave address, R/W, and then will look for an ACK. Once those bits have gone out on the bus you can immediately end the transaction by sending a stop bit (STP). Just make sure you don't set STT and STP simultaneously. You need to set STT, wait for the corresponding bus activity (i.e. wait for ARDY), and then set STP.

Back-to-Back Transfers

If you do multiple transfers in a row you may notice that you need some amount of delay between the transfers. That is, once the stop bit has been sent for one transfer you need to wait a specific amount of time before starting the next transfer. This is similar to the NACK handling where you issue a STOP condition and then retry. In both of these scenarios you need to poll for I2CMDR.MST==0 before you attempt to initiate the next START condition.

Subtleties of the Master Receiver Mode

When acting as a master receiver, the I2C spec requires that a NACK be sent by the master upon receipt of the last byte of data from the slave transmitter. This is in order to tell the slave transmitter not to try and send any more data. Otherwise, if the slave tried transmitting more data and the first bit happened to be a 0 you would lock up the bus because the slave would be holding SDA low which would prevent the master from sending the STOP bit!

So how does this special condition get handled by the I2C peripheral? There are a couple ways to deal with it:

  1. The simple scenario is when using RM=0 since there is a hardware counter of how much data you are receiving. In this scenario the peripheral knows when you want to receive the last byte of data and so it automatically NACKs the slave on that transaction (assuming that STT and STP were both set at the beginning of the transaction).
  2. A more complicated scenario exists where you are trying to use RM=1, i.e. in the case where the amount of data you receive is dependent upon the data you are receiving (variable length transfer). In this scneario you should set the I2CMDR.STP bit before reading your last data byte from I2CDRR. Additionally you need to done one extra "dummy read" to get the stop bit generated.

External Slave Device Hanging the Bus by Holding SDA Low

A problematic scenario can arise if the processor/I2C module gets reset while it is in the middle of mastering a transfer. In this scenario the external slave might be holding SDA low to transmit a 0 (or ACK). In this case it will not release SDA until it gets another falling edge on SCL. Even in this case it's not until it tries to transmit a '1' that it will actually release SDA after seeing SCL fall. The end result is that the bus will hang. If the I2C tries to initiate a new transfer it will hit an "arbitration lost" condition because SDA won't match the address it's sending.

There are a couple ways to recover from this scenario.

  1. For master devices that mux the SCL/SDA pins with GPIO, the easiest thing is to configure the pins for GPIO operation and toggle SCL until the slave releases SDA. At this point you should be able to resume normal operation.
  2. Many master devices don't mux SCL/SDA with GPIO since the I2C I/O cells are often special open drain cells. A workaround has been reported to work even on these devices. By configuring the I2C for "free data format" and then reading a byte the I2C will immediately start sending clocks to input data (rather than trying to send an address). This can be used to free up the bus.
  3. Some slave devices can reset their I2C interface when the bus is hanging (e.g. the LTC4151, after 33 ms). Switch it on if that is not the default behaviour.

For devices that actually boot from I2C you need to be careful about this scenario. Since the above workarounds rely on software they will not be possible if the device cannot boot! For this case you need to do something different. For example, if you can control the timing of the reset you could make sure you only reset the device when no I2C transaction is in progress. A different option would be to build your board such that the processor reset power cycles the I2C EEPROM/flash (or simply resets it, but most EEPROMs/flashes don't have a reset pin).

Common Mistake Clearing Interrupts

The interrupts get logged into a special kind of register. It's special in that the interrupt bits are "write-1-to-clear". For these bits, writing a zero does nothing and writing a 1 to a bit that is already set will clear that bit.

When trying to modify a specific bit, programmers often use a read-modify-write construct. For example:

// typical example of clearing bit 4
// read-modify-write
// Don't do this for write-1-to-clear registers!
my_register &= ~(1<<4);

If you do something like the above for a write-1-to-clear register you will clear all bits except the one you're trying to clear! Here's another mistake sometimes observed with these registers:

// Another improper way of trying to clear a bit in a write-1-to-clear register
// read-modify-write
// Don't do this for write-1-to-clear registers!
my_register |= (1<<4);

The above example would end up clearing all bits that are set in the register.

Here's how it should look:

// Correct method of clearing bit 4 in a write-1-to-clear register
// do a straight write to the register
my_register = (1<<4);

Using I2C in a Multi-Threaded Context

In a multi-threaded applicaton (e.g. using an RTOS where multiple threads might attempt to perform an I2C transaction) it is critical that your driver be thread safe. Some people incorrectly think that checking for Bus Busy is sufficient for this condition, but it is not. Here's a scenario that can occur:

  • Thread 1 starts an I2C transaction. We program slave address, number of bytes, <interrupt>
  • Thread 2 now starts an I2C transaction. Because Thread 1 never hit the “STT” bit, the bus is not busy. Thread 2 programs slave address, number of bytes, starts transaction, sends data.
  • Thread 2 has completed its transfer correctly.
  • Eventually we have a context switch back to Thread 1. The slave address and number of bytes are now left in the state as specified by Thread 2, but that’s unknown to Thread 1. It moves forward with setting the STT bit and performing its transfer. Only now it is to the wrong address and potentially for the wrong number of bytes!

The solution to this issue is to use a mutex (e.g. GateMutex from TI-RTOS). Each instance of the I2C driver (i.e. corresponding to the physical controllers in a device) should have its own mutex. Upon starting a transaction a thread locks the mutex, and upon completing the transaction it unlocks the mutex. It may not be a good idea to block a thread just because and as long as I2C is busy. In that case, a thread dedicated to I2C operation could swiftly accept orders for transmission.

Related articles

For an example of programming the I2C peripheral using the CSL 2.x functional layer you should read this article.