This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMS320F280025C: I2C bus busy, unknow reason to occupy the bus

Part Number: TMS320F280025C
Other Parts Discussed in Thread: C2000WARE

Driver lib: C2000Ware 4.02.00

Example referenced: i2c_ex5_master_slave_interrupt

I2C spec: 100kHz, 7-bit address, 8-bit data format. using FIFO and FIFO interrupt.

Brief:

  • 0025C as slave, test good and stable using Microchip pickit serial analyzer, also I2C master implemented by another F280025C control card, which means I2C bus is setup correctly.
  • Using linux system as master and run "i2cget" command, there is a chance to let 0025 keep SCL low, guess it's get into time stretch mode. Linux system didn't have timeout mechanism yet.
  • Want to know is the code not fast enough to handle the I2C request, or some interrupt preempted problem cause this issue.
  • This issue is a little complex...

Dear TI expert, 

As my previous work on I2C, I'm building an interrupt master/ slave to communicate.

Thanks for gurus as you, I know the I2C interrupt can trigger the master/ slave to change data direction itselfe and change the role from reciever to transmitter.

Consider using Linux system as master, 0025C is just like EEPROM, should be read/write the register(command) through "i2cget" command.

The problem is, when I using data analyzer and another 0025 as master to test the code, it's very stable, fast and correct.

But when I change the platform to customize platform, through the dataformat, electronic characteristic are the same, there is a chance to let slave keep SCL low.

I chacke the waveform when error occurred:

Though the Logic analyzer shows SCL is back to high, but for some reason, I use oscilloscope check the SCL is low (maybe it happened out of scope).

I also check is there another device on the bus cause this problem, but SCL only pull low when my device connect to the bus.

At this moment, I set a break point in while loop (do nothing loop), and check the register as below:

The BB bit is high, this cause the wrong state of I2C module, broke the finite state machine.

As I know, after I2C module detect the STARTcondition, BB=1 and until another STOP condition detected.

So unpredicable BB=1 means two senario: START happened accidently or STOP happened accidently, or there is something I missed?

But I2C module has build-in filter, I don't think it's noise issue.

To solve this dead-end as slave device, I can not reset it by sending start/stop condition, but reset whole I2C module.

Here are some waveform for reference:

Data reansmit fail but I2C get correst stop condition to reset BB bit.

Data transmit success, it include two bytes back and stop bit beautifully.

Success by customize Linux system

Success using Serial data analyzer

And the code is as below

I2C FIFO ISR

As I know, when device in slave transmiter mode, I2CCNT value is don't care, to transmit the data just use I2C_put().

I desig to use I2C_FFTX_INT only onetime, put two byte data into FIFO in for loop.

The I2C module should send data out automatically.

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* @brief Process I2C FIFO interrupt source
* @param none
* @return none
* @disc FIFO ISR need to process RX data and send data back to master
*/
__interrupt void i2c_FIFO_isr(void)
{
uint32_t intSource_FIFO = (uint32_t)I2C_getInterruptStatus(I2CA_BASE);
// If RX FIFO interrupt flag is set, read command
if((intSource_FIFO & I2C_INT_RXFF) != 0)
{
I2c_cmd = I2C_getData(I2CA_BASE);
I2C_clearInterruptStatus(I2CA_BASE, I2C_INT_RXFF);
}
// Slave transmitter mode, send back data asked by master
if(((intSource_FIFO & I2C_INT_TXFF) && I2c_cmd) != 0 )
{
i2c_cmd_process(I2c_cmd); // process the i2c command
I2c_cmd = 0;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

i2c_cmd_process

This function switch to different command, send different data.

I think this could slowdown the whole process because it's in interrupt?

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void i2c_cmd_process(uint16_t cmd){
uint16_t i;
switch(cmd){
case(Vin1_cmd):
sData[0] = (Vin1_adc_val >> 8) & 0x00ff;
sData[1] = (Vin1_adc_val) & 0x00ff;
for(i = 0; i < 2; i ++)
I2C_putData(I2CA_BASE, sData[i]);
break;
default:
// Unrecognize command, send 0x87 twice
for(i = 0; i < 2; i ++)
I2C_putData(I2CA_BASE, 0x87);
break;
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

I2C INT ISR

Simply chage the data direction and reset FIFO when stop condition detected.

Also the stop bit happened on other device's communication will cause this interrupt, just keep FIFO clear.

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* @brief Process I2C interrupt source
* @param none
* @return none
* @disc As I2C slave, ISR happened when master call it, this will trigger AASINT.
* Also, when master stop communication, STPINT triggered, slave reset I2C FFINT.
*/
__interrupt void i2c_isr(void){
I2C_InterruptSource intSource = I2C_getInterruptSource(I2CA_BASE);
// Go into different interrupt case
switch (intSource)
{
case I2C_INTSRC_ARB_LOST:
break;
case I2C_INTSRC_NO_ACK:
break;
case I2C_INTSRC_REG_ACCESS_RDY:
break;
case I2C_INTSRC_RX_DATA_RDY:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Init_I2C

GPIO setup and I2C module setup

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void Init_I2C(void)
{
// Enable I2C-A on GPIO32 - GPIO33
EALLOW;
GpioCtrlRegs.GPBPUD.bit.GPIO32 = 0; // Enable pull-up for GPIO32 (SDAA)
GpioCtrlRegs.GPBPUD.bit.GPIO33 = 0; // Enable pull-up for GPIO33 (SCLA)
GpioCtrlRegs.GPBQSEL1.bit.GPIO32 = 3; // Asynch input GPIO32 (SDAA)
GpioCtrlRegs.GPBQSEL1.bit.GPIO33 = 3; // Asynch input GPIO33 (SCLA)
GpioCtrlRegs.GPBGMUX1.bit.GPIO32 = 0; // Configure GPIO32 for SDAA operation
GpioCtrlRegs.GPBGMUX1.bit.GPIO33 = 0; // Configure GPIO33 for SCLA operation
GpioCtrlRegs.GPBMUX1.bit.GPIO32 = 1; // Configure GPIO32 for SDAA operation
GpioCtrlRegs.GPBMUX1.bit.GPIO33 = 1; // Configure GPIO33 for SCLA operation
GpioCtrlRegs.GPBODR.bit.GPIO32 = 0;
GpioCtrlRegs.GPBODR.bit.GPIO33 = 0;
EDIS;
// Initialize I2C (Slave receiver mode)
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Conclusion:

  1. I'm wondering is my usage of TTXFFINT is good or this interrupt is design to transmit data one-byte by one?
  2. Is there anything I missed can cause the I2C bus freeze (BB =1) ?
  3. If the interrupt be preempted by other higher priority ISR (such as ADCA1), it can cause I2C broken?
  4. The chance this problen belone to Linux system is small because it can correctly read other device correctly.

Thanks for your help!! 

  • Chen,

    It is highly likely that linux based I2C is not giving enough setup time for STOP condition.

    For Standard mode, you need a minimum of 4us of setup time for STOP condition to be detected.

    For Fast mode, you need a minimum of 0.6 us of setup time for STOP condition to be detected. Both these parameters are mentioned in device datasheet

    Regards,

    Manoj

  • Thanks for your fast reply.

    By taking your advice, I check the waveform of transmission.

    I think stop setup time maybe not the main reason because when data send successful,

    waveform shows there have 8uS under 100kHz clock.

    When bus-busy error occurred,It seems like missing some clock, like this:

    p.s The final state out of scope is SCL = 0, SDA = 1.

    Then I check this using oscilloscope, hookup the SCL and SDA line, and found clock signal seems "attenuate", or pull low by slave:

    Zoom little bit there is some "ripple like" waveform, we can see some trace of SCL signal.

    I think the problem may caused by Linux system driver didn't support time-stretch function.

    But I still need your opinion about this issue.

  • Noisy glitch shown here is a concern. I'm wondering whether glitch is causing this issue on I2C (target / slave)? Are you able to reproduce this issue reliably.

    Did you try adding series resistor and bypass cap on I2C pins to mitigate noisy glitch effects?

  • Thanks for the reply.

    I found out that the spike is not noise, it's normal SCL it should be, but pull low by slave.

    Due to the Linux driver I used didn't have time-stretching ability, the master read the message as usual no metter whether the slave need to be wait.

    On the other words, the master ignore the time-stretch request by slave, so it blindly generate clock signal, and after preset time, it stop the teansmission.

    But on the other side, the slave generate time-stretch request, so it think the master will wait it until it release SCL line, which it didn't.

    So in this senario, master misread the data, also slave analyze the bus state wrongly, so cause this problem.

    Also the glitch actually is the moment when the normal clock signal released by slave.

    I hooked up GPIO and analyze the relation about ISR and I2C.

    When ISR not collision with the transmission, it works fine, CPU can fully process the I2C request.

    When ISR1 or ISR2 collision with I2C transmist process, the slave pull low asking for more time, but the master didn't wait for it, this cause the problem.

    That's what I found,

    Thanks your help and hope this can help others.