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.

Compiler/MSP430FR5848: Losing UCRXIFG0 during I2C operation on eUSCI_B0 or is the 8th clock being stretched, and by master or slave?

Part Number: MSP430FR5848

Tool/software: TI C/C++ Compiler

Hi there,

I identified a problem in a programs execution where periodically when the program is servicing a lot of interrupts - the 8th bit of the read byte appears to get stretched, often by ~2.5ms, or until my timeout code rescues it. The included image shows the I2C SCL, SDA then a GPIO that is toggled on/off at the boundaries of the USCI_B0_VECTOR.

Although there are a quite a few interrupts being serviced over PORT1-4/J, TA0/A1, eUSCI_A0/A1, (~40,0000/sec), I have proven that they don't correlate with the missing interrupt or clock stretching. This is evident due to the fact that the missing 8th bit still occurs in modified version of my driver which operates off of TX/RX interrupts.

The operation is simple:

/// init
UCBxIE     = 0;
UCBxCTLW0 |= UCSWRST;
UCBxCTLW0 &= UCSWRST;
UCBxCTLW1  = 0;
UCBxCTLW0 |= (UCMODE_3 | UCMST | UCSYNC); /*!< I2C, Master, Sync */
UCBxCTLW0 |= UCSSEL_2;                    /*!< SMCLK */
UCBxBRW    = 0x00A0;                      /*!< 100k */
UCBxIFG    = 0;
UCBxCTLW0 |= UCTXSTP;
UCAxCTL1  &= ~UCSWRST;

/// enable all interrupts
UCBxIE    |= (UCNACKIE | UCSTPIE | UCRXIE | UCTXIE);

/// set for rx
UCBxI2CSA  = 0x1E;
UCBxTBCNT  = 0;
UCBxCTLW0 &= ~UCTXSTP;
UCBxIFG    = 0;
UCBxIE     = (UCNACKIE | UCSTPIE | UCTXIE);
UCBxCTLW0 |= UCTR;
UCBxCTLW0 |= UCTXSTT;

/// in rx isr
void rx_iisr(void) {
    char val = UCBxRXBUF;
    remainingRx--;

    // store data
    if (remainingRx != 0 || !getExtraByte)
        *rxPtr++ = val;

    // set send stop at second to last byte
    if (remainingRx == 1) {
        UCBxCTLW0 |= UCTXSTP;
    } else if (remainingRx == 0) {
        UCBxIE = 0;
        UCBxIFG = 0;
    }
}

/// in tx isr
void tx_iisr(void) {
    if (remainingTx != 0) {
        // deliver data
        remainingTx--;
        UCBxTXBUF = *txPtr++;
    } else if (remainingRx != 0) {
        // switch to receiving
        UCBxTBCNT = 0;
        UCBxCTLW0 &= ~UCTR;
        UCBxIE &= ~UCTXIE;
        UCBxIE |= UCRXIE;
        UCBxCTLW0 |=  UCTXSTT;
    } else {
        // only wanted to send
        UCBxCTLW0 &= ~UCTXSTT;
        UCBxCTLW0 |= UCTXSTP;
        UCBxIE     = 0;
        UCBxIFG    = 0
    }
}


void nack_iisr(void) { return; }

void stp_iisr(void) { return; }

The only thing I can see as being wrong is setting the count to zero while the UCSWRST bit isn't set. Of course I haven't set up code to operate solely with this I2C stuff going on but I also can't seem to show why it would miss the rx interrupt. I broke the code on the second entry in the area commented as 'switch to receiving' to inspect the peripherals and the correct interrupts are enabled, no pending interrupts, the pins are configured correctly still.

Another vital question: is the 8th bit being stretched by the master or slave (if it's obvious to anyone)? Inspecting the signals with a scope, the pull-down potential looks to be indistinguishable for both devices so I can't tell. It's not the 9th bit which is surprising.

This issue has been tripping me up for over a week now!

  • Sorry I should clarify that the actual interrupt service sets the global interrupt to allow nesting of other peripherals. It then reads the UCBxIV once and calls the associated IISR which for a nack or a stop are both empty routines.
  • Hi Matthew,

    Thanks for posting.  When you re-set GIE in your UCBx interrupt handler to allow nesting, do you disable the UCBx interrupts to prevent re-entry into your I2C handlers if you are already inside of them?

    Regards,
    Walter

  • The code behaves identically with or without the GIE being set inside the ISR.

    I have a timer detection that I break on after 5ms has elapsed (+- 1ms) and the state of the UCB0 register set is the following with the clock line still low from the missed rx interrupt:

    That indicates the following settings and IFG/IE:

    UCB0CTLW0:

    • 7-bit address mode
    • 7-bit slave address mode
    • single master environment
    • master mode
    • i2c mode
    • smclk source
    • not transmitting ack
    • receive select (receiver mode)
    • not transmitting nack
    • not transmitting stop
    • not transmitting start
    • not in software reset

    UCB0CTLW1:

    • no early UCTXIFG0 (slave mode only)
    • no clock low timeout
    • no acknowledge stop on last-byte
    • no software acknowledge
    • no automatic stop
    • deglitch 50ns (default)

    UCB0BRW:

    • set for 100khz @ 16MHz

    UCB0STATW:

    • count: 1
    • scl is low
    • no general call
    • bus is busy


    UCB0RXBUF:

    • <empty>

    UCB0TXBUF:

    • <0x60>

    UCB0I2COA0-3:

    • <empty>

    UCB0ADDRX:

    • <0x1E>

    UCB0ADDMASK:

    • <0x03FF>

    UCB0I2CSA:

    • <0x1E>

    UCB0IE:

    • NACK set
    • STP set
    • RX set

    UCB0IFG:

    • UCBIT9IFG set

    I understand that the eUSCI_B0 module generates flags for interrupts that are not enabled, however will not generate IRQs.

    The UCBIT9IFG being set is indicative of the 9th clock bit being sent, however the 8th hasn't even been sent at this stage.

    I have tested this with four identical chips and they behave in an identical fashion.

    I should also add that I saved a snapshot of the Port 1 registers at the time (P1.6-7 being of interest) and the SELx bits were set correctly for eUSCI_B0 in I2C mode.

    The in/out values of them I can't recall, I check tomorrow - but were a mix of 1s and 0s.

  • I still don't have a solution for this besides compiling with higher optimisations turned on. This hints at a race condition of some sort. But I am regardless, lost as to why the Rx interrupt disappears and the rx flag is never set in that instance.
  • Hello,

    What is the device you are using as a slave, and does it have any information in the datasheet about clock stalling? We believe that it's not a problem of the Rx interrupt not triggering upon the completion of a receive, but that the interrupt doesn't get a change to trigger because the bus is busy and the receive never completes.

    You mentioned that the byte gets stretched for about 2.5ms or until the operation times out; does the operation still eventually finish when you take out the timeout timer? And does the Rx interrupt trigger then? That could be an indication that the bus is busy on the slave side.

    Also, the UCBIT9IFG flag is a flag that you have to manually clear, so it being set might be an artifact from an earlier transaction that never gets cleared.

    All the best,

    Colin Adema

  • Hello,

    How has the issue been? Has it been resolved?

    All the best,

    Colin Adema

  • Wrote recovery routines instead that detect if the I2C has failed for more than a millisecond (the peripheral based recovery takes around 30ms to trigger a clock low interrupt which is a little slow) - it happens only when I compile with low optimisation and generate a lot of port 1 interrupts like I said earlier.

    I'll have to investigate further in my free time.

**Attention** This is a public forum