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.

MSP430F67771A: UART ISR reads value of 0 from Interrupt Vector register

Part Number: MSP430F67771A

Below is a stripped down version (with some macros also partially expanded) of the ISR function used for USCI_A0 in UART mode. While running this through a BACnet MS/TP protocol, I'm seeing a few overrun errors, but I'm also seeing a larger number of interrupts where the IV value is 0 and falling to the default case. Our BACnet stack is occasionally failing received frames where there are a few bytes missing (generating an eventual CRC error), but I haven't been able to confirm if these failures are exclusively from the overrun errors.

  • How is it possible to trigger an interrupt with the IV register value at 0?
  • Is it possible that some received bytes are generating an interrupt without setting the USCI_UART_UCRXIFG flag (resulting in lost data)?
  • Is there a potential race condition scenario that I should be avoiding when enabling/disabling the interrupts in the UCA0IE register and clearing the flags in the UCA0IFG register?

void _uartISR(void)
{
    uint16_t err;

    switch(_even_in_range(HWREG16((0x05C0) + OFS_UCAxIV), USCI_UART_UCTXCPTIFG))
    {
        case USCI_UART_UCRXIFG:
            // Read the error status before unloading the rx buffer.
            err = HWREG16((0x05C0) + OFS_UCAxSTATW);
            *m_rx.pHead = HWREG16((0x05C0) + OFS_UCAxRXBUF);

            if(err & (UCFE | UCOE | UCPE))
            {
                if(err & UCFE)
                    g_nRxErrors.framing++;
                else if(err & UCOE)
                    g_nRxErrors.overrun++;
                else if(err & UCPE)
                    g_nRxErrors.parity++;
                else
                    g_nRxErrors.undefined++;
            }
            break;

        case USCI_UART_UCTXCPTIFG:
            if(!(HWREG16((0x05C0) + OFS_UCAxSTATW) & UCBUSY))
            {
                // Disable UART tx complete interrupt.
                do {HWREG16((0x05C0) + OFS_UCAxIE) &= ~((0x0008)); _nop();} while(0);
                do {HWREG16((0x05C0) + OFS_UCAxIFG) &= ~((0x0008)); _nop();} while(0);
                
                // Enable rx interrupt.
                do {HWREG16((0x05C0) + OFS_UCAxIFG) &= ~((0x0002)); _nop();} while(0);
                do {HWREG16((0x05C0) + OFS_UCAxIE) |= ((0x0001));} while(0);
            }
            break;

        default:
            g_nRxErrors.ivZero++;
            break;
    }
}

  • Are there other interrupts enabled? If so they could be responsible for reaching the default case. Also, the transmit complete interrupt happens after every stop bit is transmitted. (See USCI42 in the errata.) There was some discussion of workarounds when I exposed this error way back when but TI decided that there was no reliable workaround when they issued the errata.

  • No, just those two interrupts are enabled. I use the DMA peripheral to execute the Tx of a local buffer in RAM that triggers off of the other Tx interrupt flag USCI_UART_UCTXIFG, which should generate an interrupt when the UART peripheral's hardware buffer UCAxTXBUFTx becomes empty. When the DMA completes its operation, it generates its own interrupt and that ISR enables UCTXCPTIE, which enables the USCI_UART_UCTXCPTIFG flag as you can see from my code above. I check to see if the UART is busy to ensure that there is no data in either the hardware buffer UCAxTXBUF or the Tx shift register.

    All of this seems to work properly as far as I can tell. I'm able to Tx local buffers in RAM and can detect the end of that Tx.

  • Let me see if I understand what you say you are doing (code would help).

    Transmit data is moved via a DMAC. When the DMA completes the transfer, its transfer complete ISR sets UCTXCPTIE.

    This is a problem. When the DMAC finishes its work, there will be a byte in the UART shift register and a byte in TXBUF. UCTXCPTFG has of course been set by previous transfers.

    So when you enable UCTXCPTIE there is one interrupt pending and two more coming down the pike. I am pretty sure that TI considered the possibility of checking UCBUSY before deciding that there was no workaround.

    Since you do not clear UCTXCPTIE (or at least don't show where it is cleared), that is almost certainly the source of your unexpected interrupts.

  • Sorry for the confusion. Yes, I have accounted for that scenario. In my original post, the code on line 26 is checking the busy flag UCBUSY to ensure that we have indeed completed Txing every byte.

    For additional reference, here is the DMA ISR.

    __interrupt void _txDMAISR(void)
    {
        switch(_even_in_range(GET_DMA_IV(), DMAIV_DMA2IFG))
        {
            case DMAIV_DMA1IFG:
                // Clear and enable UART tx complete interrupt.
                do {HWREG16((0x05C0) + OFS_UCAxIFG) &= ~((0x0008)); _nop();} while(0);
                do {HWREG16((0x05C0) + OFS_UCAxIE) |= ((0x0008));} while(0);
                break;
    
            default:
                break;
        }
    }

  • Checking UCBUSY is not guaranteed to work or it would have been shown as a workaround in the errata.

    You do not know that the IV register has 0 in it if you hit the default case. There are other possibilities and you should at least put stubs in place for those cases.

    Oh, in your interrupt code you clear TXIFG just before enabling RXIE. Which seems odd.

  • I've been reducing parts of the code for brevity (and expanding the macros), so the code below would be more accurate to show that we're counting an empty IV register.

    After looking at USCI42 from the errata, I see your point about no workaround being specified. With that said, it at least "appears" to work correctly on line 26 where the if statement requires the UCBUSY flag to be 0. On line 29, we are toggling a GPIO pin to put an RS-485 transceiver into Rx mode, and by probing the UART signals and this GPIO pin with a scope it shows that the timing is accurate to the last stop bit of the last byte. From an application perspective, the BACnet protocol that we run over this UART appears to be working perfectly under our stress tests.

    If the errata is implying that this issue somehow contributes to a 0 value being read from the IV register, then we will assume that our application works fine and that these spurious interrupts can just be ignored.

    void _uartISR(void)
    {
        uint16_t err;
    
        switch(_even_in_range(HWREG16((0x05C0) + OFS_UCAxIV), USCI_UART_UCTXCPTIFG))
        {
            case USCI_UART_UCRXIFG:
                // Read the error status before unloading the rx buffer.
                err = HWREG16((0x05C0) + OFS_UCAxSTATW);
                *m_rx.pHead = HWREG16((0x05C0) + OFS_UCAxRXBUF);
    
                if(err & (UCFE | UCOE | UCPE))
                {
                    if(err & UCFE)
                        g_nRxErrors.framing++;
                    else if(err & UCOE)
                        g_nRxErrors.overrun++;
                    else if(err & UCPE)
                        g_nRxErrors.parity++;
                    else
                        g_nRxErrors.undefined++;
                }
                break;
    
            case USCI_UART_UCTXCPTIFG:
                if(!(HWREG16((0x05C0) + OFS_UCAxSTATW) & UCBUSY))
                {
                    // Disable RS-485 tx mode.
    			    do {HWREG16((GEN_PORT_BASE(3)) + OFS_PAOUT) &= \
                            ~((((3) & 1) ^ 1) ? (((0x0008)) << 8) : ((0x0008)));} while(0)
    
                    // Disable UART tx complete interrupt.
                    do {HWREG16((0x05C0) + OFS_UCAxIE) &= ~((0x0008)); _nop();} while(0);
                    do {HWREG16((0x05C0) + OFS_UCAxIFG) &= ~((0x0008)); _nop();} while(0);
                    
                    // Enable rx interrupt.
                    do {HWREG16((0x05C0) + OFS_UCAxIFG) &= ~((0x0002)); _nop();} while(0);
                    do {HWREG16((0x05C0) + OFS_UCAxIE) |= ((0x0001));} while(0);
                }
                break;
    
            case USCI_NONE:
                g_nRxErrors.ivZero++;
                break;
                
            case USCI_UART_UCTXIFG:
            case USCI_UART_UCSTTIFG:
            default:
                assert(0);
                break;
        }
    }

  • The problem might lie within the code that configures the UART and DMAC.

    The code I had this problem with also used the DMAC to transmit data over a RS485 link. I needed to turn off the transmitter so that something else could drive the bus. I ended up checking TXIFG. That worked for me but perhaps only because of the low speed (9600bps) I was using.

        case USCI_UART_UCTXCPTIFG:
          /*
            There is a hardware bug (not mentioned in the errata) causing this
            interrupt to happen early and often. I check the transmit buffer
            register empty flag as well. This seems to control the problem.
           */
          if(UCA0IFG & UCTXIFG)
            {
              P3OUT &= ~0x10;          // disable RS485 transmitter
            }
    

**Attention** This is a public forum