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.
/* @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;
I2C_clearInterruptStatus(I2CA_BASE, I2C_INT_TXFF);
/* After sending data back to master, stop condition detected
* Then FIFOINT will be disabled,
* so the process should not go back to TXFFINT again.
*/
}
PieCtrlRegs.PIEACK.bit.ACK8 = 1;
}
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?
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;
}
}
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.
/* @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:
break;
case I2C_INTSRC_TX_DATA_RDY:
break;
case I2C_INTSRC_STOP_CONDITION:
// Disable I2C interrupt when I2C communication stop
I2C_disableInterrupt(I2CA_BASE, (I2C_INT_TXFF | I2C_INT_RXFF));
// Reset FIFO
I2C_disableFIFO(I2CA_BASE);
I2C_enableFIFO(I2CA_BASE);
break;
case I2C_INTSRC_ADDR_SLAVE:
// Slave called by master
I2C_setFIFOInterruptLevel(I2CA_BASE, I2C_FIFO_TX2, I2C_FIFO_RX1); // Prepare the FIFO level
// When master call the salve in read mode
if((I2C_getStatus(I2CA_BASE) & I2C_STS_SLAVE_DIR))
{
// Slave Transmitter (SDIR = 1)
I2C_setConfig(I2CA_BASE, I2C_SLAVE_SEND_MODE);
// Enable TX FIFO interrupt to send back data
// Disable RXFF interrupt
I2C_disableInterrupt(I2CA_BASE, I2C_INT_RXFF);
I2C_enableInterrupt(I2CA_BASE, I2C_INT_TXFF);
I2C_clearInterruptStatus(I2CA_BASE, (I2C_INT_TXFF|I2C_INT_RXFF));
}
// When master call the slave in write mode
else
{
// Slave Receiver (SDIR = 0)
I2C_setConfig(I2CA_BASE, I2C_SLAVE_RECEIVE_MODE);
// Enable RX FIFO interrupt waiting command
// Disable TXFF interrupt
I2C_disableInterrupt(I2CA_BASE, I2C_INT_TXFF);
I2C_enableInterrupt(I2CA_BASE, I2C_INT_RXFF);
I2C_clearInterruptStatus(I2CA_BASE, (I2C_INT_TXFF|I2C_INT_RXFF));
}
break;
default:
ESTOP0;
break;
}
PieCtrlRegs.PIEACK.bit.ACK8 = 1;
}
Init_I2C
GPIO setup and I2C module setup
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)
I2caRegs.I2CMDR.bit.IRS = 0; // Reset I2C module
I2caRegs.I2COAR.all = 0x60; // Configure I2C own address
I2caRegs.I2CSAR.all = 0x0; // Configure I2C slave address
I2caRegs.I2CMDR.bit.MST = 0; // Configure I2C as slave
I2caRegs.I2CMDR.bit.TRX = 0; // Configure I2C as receive mode
I2caRegs.I2CMDR.bit.BC = 0; // Set the bit count to 8 bits per data byte
I2caRegs.I2CCNT = 1; // Set data count = 1 bytes
// Setup TX FIFO
I2caRegs.I2CFFTX.bit.TXFFRST = 0; // Reset and hold TX FIFO
I2caRegs.I2CFFTX.bit.I2CFFEN = 1; // Enable I2C FIFO
I2caRegs.I2CFFTX.bit.TXFFIENA = 0; // Disable TXFF interrupt
I2caRegs.I2CFFTX.bit.TXFFIL = 0; // TX FIFO level = 0 bytes
I2caRegs.I2CFFTX.bit.TXFFINTCLR = 1; // Clear TXFF INT
I2caRegs.I2CFFTX.bit.TXFFRST = 1; // Enable TX FIFO
// Setup RX FIFO
I2caRegs.I2CFFRX.bit.RXFFRST = 0; // Reset and hold RX FIFO
I2caRegs.I2CFFRX.bit.RXFFIENA = 1; // Enable RXFIFO interrupt
I2caRegs.I2CFFRX.bit.RXFFIL = 1; // RX FIFO level = 1 byte
I2caRegs.I2CFFRX.bit.RXFFINTCLR = 1; // Clear RXFF INT
I2caRegs.I2CFFRX.bit.RXFFRST = 1; // Enable RX FIFO
// Setup interrupt source
I2caRegs.I2CIER.bit.AAS = 1; // Enable Address as slave interrupt
I2caRegs.I2CIER.bit.SCD = 1; // Enable Stop condition detected interrupt
I2caRegs.I2CIER.bit.XRDY = 0; // Enable Transmit-data-ready interrupt
I2caRegs.I2CIER.bit.RRDY = 0; // Enable Receive-data-ready interrupt
I2caRegs.I2CIER.bit.ARDY = 0; // Enable Register-access-ready interrupt
I2caRegs.I2CIER.bit.NACK = 0; // Enable Non-ack interrupt
I2caRegs.I2CIER.bit.ARBL = 0; // Enable Arbitration-lost interrupt
I2caRegs.I2CEMDR.bit.BC = 0;
// Clear interrupt flags
I2caRegs.I2CSTR.all = 0xFFFF; // Clear other I2C flags
I2caRegs.I2CMDR.bit.IRS = 1; // Release I2C module
DELAY_US(100);
}
Conclusion:
- I'm wondering is my usage of TTXFFINT is good or this interrupt is design to transmit data one-byte by one?
- Is there anything I missed can cause the I2C bus freeze (BB =1) ?
- If the interrupt be preempted by other higher priority ISR (such as ADCA1), it can cause I2C broken?
- The chance this problen belone to Linux system is small because it can correctly read other device correctly.
Thanks for your help!!










