I am writing for the C2000 I2C bus and having some issues in the Master-Receiver operation mode. I am using polling instead of an interrupt-based method.
Every few minutes my I2C communication times out when receiving data from a slave device. It times out in the highlighted area in the code below, with the I2caRegs.I2CSTR.all == 0x2430 (NACKSNT, XSMT, SCD,XRDY). I am not sure if I programed the registers correctly when switching from Master-Transmitter to Master-Receiver modes. I don't see any NACKs from slave devices. I do not experience any issues when I am just in Master-Transmiter mode (code not shown).
The timer is clocked at 150MHZ, so 200,000 cycles is a 133 ms timeout, which is very long since the largest read is 6-bytes.
void I2C_Read(Uint8 addr, Uint16 regPtr, Uint16 numBytes, Uint16 * data, enum I2C_DEF_ERROR *err, bool msbFirst, Uint32 timeoutMicroSecs)
{
*err = I2C_OK;
i2cMutex.Lock();
DINT;
Uint32 lo = CtoMIpcRegs.MIPCCOUNTERL;
Uint32 hi = CtoMIpcRegs.MIPCCOUNTERH;
EINT;
Uint32 timed_out = 0;
// The Master bit will self clear, so it is a good thing to wait on to ensure
// the bus is free and ready for a new Master
while(I2caRegs.I2CMDR.bit.MST && !(timed_out=I2C_TimedOut(lo, hi, 20000000))) {};
if(timed_out)
{
DbgInfo("Got Timed out in Read TX 1. Status: 0x%x", I2caRegs.I2CSTR.all);
*err = I2C_ERROR_TIMEOUT;
i2cMutex.Unlock();
return;
}
//I2caRegs.I2CMDR.bit.IRS = 0; // this is needed sometimes, but I would prefer not to reset it all the time
I2caRegs.I2CCNT = 1; // In non-repeat mode (RM = 0), this dictates the number of bytes to send. Just 1 for reading (the register)
I2caRegs.I2CSAR = addr; // The address of the slave device
I2caRegs.I2CDXR = regPtr; // The first data byte to transmit, which is the remote register to address
I2caRegs.I2CMDR.bit.TRX = 1; // Transmitting data to the slave
I2caRegs.I2CMDR.bit.MST = 1; // Set the master bit to start the SCL line.
I2caRegs.I2CMDR.bit.FREE = 1; // Allow I2C to run while in a breakpoint
I2caRegs.I2CMDR.bit.STP = 0; // Since we are receiving data from the slave, we want to do a repeat-start so disable the Stop bit
I2caRegs.I2CMDR.bit.STT = 1; // Start a new transaction
I2caRegs.I2CMDR.bit.IRS = 1; // Pull the I2C out of reset
// Wait for the registers to be free before starting the read phase of the transaction
while(!I2caRegs.I2CSTR.bit.ARDY && !(timed_out=I2C_TimedOut(lo, hi, 20000000)));
if(timed_out)
{
DbgInfo("Got Timed out in Read TX 2. Status: 0x%x", I2caRegs.I2CSTR.all);
*err = I2C_ERROR_TIMEOUT;
i2cMutex.Unlock();
return;
}
// Check if we got a No-Acknowledgment
if(I2caRegs.I2CSTR.bit.NACK)
{
DbgInfo("Got NACK in Read TX. Status: 0x%x", I2caRegs.I2CSTR.all);
I2caRegs.I2CMDR.all = 0;
//I2caRegs.I2CMDR.bit.STP = 1; // We need to force a stop condition in this case
I2caRegs.I2CSTR.bit.NACK = 1; // Clear the NACK bit (by writing 1 to it)
*err = I2C_NACK;
i2cMutex.Unlock();
return;
}
// I2caRegs.I2CMDR.all = 0;
I2caRegs.I2CCNT = numBytes; // In non-repeat mode (RM = 0), this dictates the number of bytes to read.
I2caRegs.I2CMDR.bit.TRX = 0; // We are the Master-receiver, so turn off the transmit bit
I2caRegs.I2CMDR.bit.MST = 1; // Still the master (probably not needed)
I2caRegs.I2CMDR.bit.FREE = 1; // Allow I2C to run while in a breakpoint
I2caRegs.I2CMDR.bit.STP = 1; // Stop bit will be applied after I2CCNT reaches 0
I2caRegs.I2CMDR.bit.STT = 1; // Repeat start for the receiving part of the transaction
I2caRegs.I2CMDR.bit.IRS = 1; // Pull the I2C out of reset (probably not needed)
Uint16 i(0);
for(i=0; i < numBytes; i++)
{
while(!(I2caRegs.I2CSTR.bit.RRDY || I2caRegs.I2CSTR.bit.ARDY) && !(timed_out=I2C_TimedOut(lo, hi, 20000000)));
if(timed_out)
{
DbgInfo("Got Timed out in Read RX 1. Status: 0x%x", I2caRegs.I2CSTR.all);
*err = I2C_ERROR_TIMEOUT;
i2cMutex.Unlock();
return;
}
// Check if we got a No-Acknowledgment
if(I2caRegs.I2CSTR.bit.NACK)
{
DbgInfo("Got NACK in Read RX. Status: 0x%x", I2caRegs.I2CSTR.all);
I2caRegs.I2CMDR.all = 0;
//I2caRegs.I2CMDR.bit.STP = 1; // We need to force a stop condition in this case
I2caRegs.I2CSTR.bit.NACK = 1; // Clear the NACK bit (by writing 1 to it)
*err = I2C_NACK;
break;
}
// Make sure the Receive Ready signal was triggered
while(!I2caRegs.I2CSTR.bit.RRDY && !(timed_out=I2C_TimedOut(lo, hi, 20000000)));
if(timed_out)
{
DbgInfo("Got Timed out in Read RX 2. Status: 0x%x", I2caRegs.I2CSTR.all);
*err = I2C_ERROR_TIMEOUT;
i2cMutex.Unlock();
return;
}
// Read the next data byte into the buffer, in the appropriate index based on MSB/LSB first.
__byte((int*)data, (msbFirst) ? numBytes - i - 1 : i) = I2caRegs.I2CDRR;
}
i2cMutex.Unlock();
}