I have a script which bangs on one of the I2C buses which we are using to communicate with a smart battery. Usually our driver works perfectly, however once every several thousand transcations, our driver hangs reading from the battery while waiting for the START bit to clear in UCB1CTL1. Here is the main code for our I2C0_TransmitReceive2() function:
if((countTX > 0) && (countRX > 0)){
UCB1CTL1 |= UCSWRST; // hold in reset
UCB1I2CSA = address; // Set slave address
gI2CState[1].pBufferTX = gI2C0TXBuffer2;
gI2CState[1].countTX = countTX;
gI2CState[1].pBufferRX = pbufferRX;
gI2CState[1].countRX = countRX;
gI2CState[1].pCallBack = pCallBack;
UCB1CTL1 &= ~UCSWRST; // Clear SW reset to enable port
UCB1IE = UCNACKIE + UCSTPIE + UCTXIE + UCRXIE; // Enable NAK, STOP, TX and RX interrupts
UCB1CTL1 |= UCTR + UCTXSTT; // set to transmit and start bit
result = true;
}
And heres the ISR code:
#pragma vector = USCI_B1_VECTOR
__interrupt void I2C1_ISR(void)
{
uint32 value = 0;
switch(__even_in_range(UCB1IV,12))
{
case 0: // No interrupts
break;
case USCI_I2C_UCALIFG: // Arbitration Lost, not used
break;
case USCI_I2C_UCNACKIFG: // Not acknowledged
UCB1CTL1 |= UCTXSTP; // send stop condition
UCB1IFG &= ~UCNACKIFG;
//!todo MARK may need to SWI here as well to release resources
break;
case USCI_I2C_UCSTTIFG: // START Condition
break;
case USCI_I2C_UCSTPIFG: // STOP Condition
break;
case USCI_I2C_UCRXIFG: // complete character recieved
gI2CState[1].countRX--; // Decrement RX byte counter
if (gI2CState[1].countRX) {
value = UCB1RXBUF;
*gI2CState[1].pBufferRX++ = value;
if (gI2CState[1].countRX == 1) { // send stop on before last receive
UCB1CTL1 |= UCTXSTP;
}
}
else {
value = UCB1RXBUF;
*gI2CState[1].pBufferRX = value;
I2C0_SWI();
}
break;
case USCI_I2C_UCTXIFG: // transmit
if (gI2CState[1].countTX) { // Check TX byte counter
gI2CState[1].countTX--; // Decrement TX byte counter
UCB1TXBUF = *gI2CState[1].pBufferTX++; // Load TX buffer
}
else { // terminate when all bytes sent, do not Break before restart sent
if(gI2CState[1].countRX) {
UCB1CTL1 &= ~UCTR; // change to receive
SetConditionalFlag(kErrI2CTimeout);
while(UCB1CTL1 & UCTXSTP);
ResetConditionalFlag(kErrI2CTimeout);
UCB1CTL1 |= UCTXSTT; // I2C re-start condition
//single bytes need to be polled to ensure proper stop
if ( gI2CState[1].countRX == 1 ) {
SetConditionalFlag(kErrI2CTimeout);
while (UCB1CTL1 & UCTXSTT); // poll for start clear
ResetConditionalFlag(kErrI2CTimeout);
UCB1CTL1 |= UCTXSTP; // I2C stop condition
}
} else {
UCB1CTL1 |= UCTXSTP; // send stop condition
UCB1IFG &= ~UCTXIFG; // Clear USCI_B0 TX int flag
I2C0_SWI();
}
}
break;
default:
break;
}
}
When the bus hangs, it hangs in the "while (UCB1CTL1 & UCTXSTT); " line in the "USCI_I2C_UCTXIFG" case. When this happens the bus indicates the NACK flag and SCL low flags are set. I have setup a spare GPIO to go high before the loop and go low after the loop and setup my scope to trigger if the pulse width is too wide. What I'm seeing is that it looks like that when everything works, the bus changes to RX mode while the last data byte is being TX but when it hangs, the last data byte gets retransmitted before thew bus switches to RX mode. I'm guessing another higher priority interrupt is firing. I have attached captures of when everything works and when it hangs below. I would appreciate if anyone would have some insight as to what I can do to prevent this from happening. TIA.