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.

TMS320F280039C: SCI FIFO Interrupt Framing Error

Part Number: TMS320F280039C

Hey, 

In my application, I have one microcontroller whose code cannot be changed sending a continuous stream of data to the TMS320F280039C. I am reading this data 1 byte at a time until I see the start character and then read 47 bytes of data consecutively. 

I have this working reliably if I do a blocking read of all data as below when I get an Rx interrupt: 

However, at a 57600 baud rate this takes 8ms and my application needs a fast 250us interrupt to control a motor so I cannot block for 8ms. Therefore, like I do on my SPI on this MCU, I switched to the below process: 

1) Read 1 byte at a time until I see the stop char

2) Set FIFO size to 16 bytes, wait for FIFO interrupt for Rx FIFO full

3) Store bytes in array

4) Get another FIFO full of 16 bytes

5) Store bytes in array

6) Get another FIFO of 15 bytes

7) Store bytes in array

8) Now I have complete packet, go back to reading 1 byte a time until I see the stop char. 

The issue now is that with the continuous data stream coming in I get an Rx framing error. What concerns me is that looking on TI e2e this seems to be a bug in the SCI driver where it needs 2 stop bits if you're using a FIFO with a continuous stream of data? If this is the case this will be a major issue with us using this MCU. We cannot change the device sending a continuous stream of data to send 2 stop bits. Polling is not an option because my application has many interrupts that must be serviced. 

This is my initialization

void UcapBox_InitRS485(GE_Primary_Container_t *pContainer)
{
    // Register interrupt ISRs
    Interrupt_register(INT_SCIA_TX, &sciATxISR);
    Interrupt_register(INT_SCIA_RX, &sciARxISR);

    // Initialize SCIA and its FIFO.
    SCI_performSoftwareReset(REPJ_REPC_RS485_UART);

    // Configure SCIA
    SCI_setConfig(REPJ_REPC_RS485_UART, DEVICE_LSPCLK_FREQ, REPJ_REPC_BAUDRATE,
                        ( SCI_CONFIG_WLEN_8 |
                          SCI_CONFIG_STOP_ONE |
                          SCI_CONFIG_PAR_NONE ) );

    SCI_resetChannels(REPJ_REPC_RS485_UART);

    SCI_clearInterruptStatus(REPJ_REPC_RS485_UART, SCI_INT_RXFF | SCI_INT_FE | SCI_INT_RXERR);
    SCI_enableInterrupt(REPJ_REPC_RS485_UART, SCI_INT_RXFF); // We don't transmit to REPJ
    SCI_enableInterrupt(REPJ_REPC_RS485_UART, SCI_INT_FE);
    SCI_enableInterrupt(REPJ_REPC_RS485_UART, SCI_INT_RXERR);

    SCI_resetRxFIFO(REPJ_REPC_RS485_UART);
    SCI_resetTxFIFO(REPJ_REPC_RS485_UART);
    SCI_enableFIFO(REPJ_REPC_RS485_UART);
    SCI_enableModule(REPJ_REPC_RS485_UART);

    // Set how many bytes to trigger interrupt on
    SCI_setFIFOInterruptLevel(REPJ_REPC_RS485_UART, SCI_FIFO_TX0, SCI_FIFO_RX1);
    SCI_performSoftwareReset(REPJ_REPC_RS485_UART);

    // Enable interrupts
    Interrupt_enable(INT_SCIA_RX);

    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
}

Here is how I am changing FIFO size based on whether I want to read 1 or 47 bytes. 

int32_t UcapBox_StartUartTransfer(GE_Primary_Container_t *pContainer)
{
    pContainer->debugCnt++;

    // Perform software reset of SCI in case of any errors
    SCI_performSoftwareReset(REPJ_REPC_RS485_UART);

    pContainer->debugCnt++;

    if((dataSize < 16) && (dataSize > 0))
    {
        SCI_setFIFOInterruptLevel(REPJ_REPC_RS485_UART, SCI_FIFO_TX0, (SCI_RxFIFOLevel) dataSize);
    }
    else if(dataSize > 0)
    {
        SCI_setFIFOInterruptLevel(REPJ_REPC_RS485_UART, SCI_FIFO_TX0, SCI_FIFO_RX16);
    }

    bytesRemaining = dataSize;

    pContainer->debugCnt++;

    // Reset Rx FIFO
    SCI_clearInterruptStatus(REPJ_REPC_RS485_UART, SCI_INT_RXFF | SCI_INT_FE | SCI_INT_RXERR);
    SCI_clearOverflowStatus(REPJ_REPC_RS485_UART);
    SCI_resetRxFIFO(REPJ_REPC_RS485_UART);

    EINT; // Enable interrupts
    SCI_enableInterrupt(REPJ_REPC_RS485_UART, SCI_INT_RXFF);
    pContainer->debugCnt++;
    return 1;
}

Here is my Rx interrupt - 

void sciARxISRFunc(GE_Primary_Container_t *pContainer)
{
    uint32_t sciInterruptStatus = SCI_getInterruptStatus(REPJ_REPC_RS485_UART);

    pContainer->debugCnt++;

    // Rx error occurred
    if((sciInterruptStatus & SCI_INT_FE) || (sciInterruptStatus & SCI_INT_RXERR))
    {
        errorCnt++;

        SCI_clearInterruptStatus(REPJ_REPC_RS485_UART, SCI_INT_RXFF | SCI_INT_FE | SCI_INT_RXERR);
        SCI_clearOverflowStatus(REPJ_REPC_RS485_UART);
        SCI_resetRxFIFO(REPJ_REPC_RS485_UART);

        // Restart transfer
        UcapBox_SetTransferSize(1); // go back to reading 1 byte at a time
        UcapBox_StartUartTransfer(pContainer);

        pContainer->debugCnt++;
    }
    else if (sciInterruptStatus & SCI_INT_RXFF) // we have a full FIFO
    {
        // Calculate bytes remaining
        if ((bytesRemaining < 16) && (bytesRemaining > 0))
        {
            bytesRemaining = 0;
        }
        else if (bytesRemaining > 0)
        {
            bytesRemaining -= 16;
        }

        uint32_t tempUpperLim = 0;

        if(dataSize == bytesRemaining)
        {
            SCI_RxFIFOLevel RxFifoLevel;
            SCI_TxFIFOLevel TxFifoLevel;

            // Get Fifo level
            SCI_getFIFOInterruptLevel(REPJ_REPC_RS485_UART, &TxFifoLevel, &RxFifoLevel);

            tempUpperLim = (uint32_t) RxFifoLevel;
        }
        else
        {
            tempUpperLim = dataSize - bytesRemaining;
        }

        // Store data read
        int i = bytesRead;
        for(i = bytesRead; i < tempUpperLim; i++)
        {
            pContainer->mpUcapModule->mReceiveBuffer[i] = SCI_readCharNonBlocking(REPJ_REPC_RS485_UART);
            bytesRead++; // Increment bytes read
        }

        // Transfer complete
        if(bytesRemaining == 0)
        {
            // Reset bytesRead
            bytesRead = 0;

            // Call library callback function
            GE_UcapModule_Callback(pContainer, 0, 0);

            // Disable interrupt until we want another one
            SCI_disableInterrupt(REPJ_REPC_RS485_UART, SCI_INT_RXFF);
        }

        else if (bytesRemaining < 16)
        {
            SCI_setFIFOInterruptLevel(REPJ_REPC_RS485_UART, SCI_FIFO_TX0, (SCI_RxFIFOLevel) bytesRemaining);
        }

        else
        {
            SCI_setFIFOInterruptLevel(REPJ_REPC_RS485_UART, SCI_FIFO_TX0, SCI_FIFO_RX16);
        }

    //    uint32_t start = GetProfilerCycles();
    //
    //    // Read data coming in
    //    pContainer->mpCpuUtilization->mDebugTime1 = CalcProfilerTime(start, GetProfilerCycles());
    }

    SCI_clearOverflowStatus(REPJ_REPC_RS485_UART);
    SCI_clearInterruptStatus(REPJ_REPC_RS485_UART, SCI_INT_RXFF | SCI_INT_FE | SCI_INT_RXERR);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
}

Thanks for the help!

  • Hi Derek,

    Our SCI expert will get back on this today.

  • Hi Derek,

    Thanks for your question. Unfortunately, the SCI bug you are referring to is real, in that it requires ~2 bit periods between interrupts to trigger an SCI interrupt.

    At this time there methods of mitigation are:

    1. Have the other device send a small delay after each packet of 16 bytes

    2. Use 2 stop bits in the other transmitting device

    3. Use polling instead of interrupts

      

    An alternative to this given the device you are using is that there is LIN with DMA connections on this device. LIN can act as an SCI in SCI mode, so you can theoretically completely offload the SCI tasks to the DMA using this method.

    If it is possible for your system, using the LIN in SCI mode with DMA would be my recommendation for this case.

    Regards,

    Vince

  • Thanks for the reply! That is unfortunate. We are unable to change anything on the transmitting side, but I have a workaround idea that might work. 

    Our application has a 1ms interrupt and every 1ms of data I get 6 bytes from transmitter. I can use a 16 byte FIFO and then every 1ms store whatever is in the FIFO into my own software FIFO. Let me try this and I will let you know if I run into any issues! 

  • Hey Vince, 

    I just want to confirm that using polling works for me! What I did is this: 

    1) At the beginning of my slow 1ms interrupt I check how many bytes are in the SCI Rx FIFO

    2) I store those bytes in a big software FIFO

    3) Once I receive the amount of bytes I want, I process the data

    This works well! The polling only takes up 220 clock cycles of my slow interrupt so around 2us is all this takes! 

    Thanks for the help!