UART temporarily disable interrupts while receive large amounts of data.

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
ChVa_3548136
Level 2
Level 2
Welcome!

Hello All,

I'm having a bit of a difficulty.  I have a board with a PSoC 5 and a header with a Digi XBee Modem (Wifi or LTE-M, but it doesn't matter to the issue).  The PSoC and Digi talk over a UART at 9600 bps, with just the internal 4 byte FIFO.  I'm also running FreeRTOS.

Here's the scenario: I'm trying to download ~190 KB files over the XBee.  I have an ISR routine on the UART, that moves data into a FreeRTOS stream buffer, if there's room in (important point).  Then a task waits on the stream buffer, and moves data from it to another buffer, then writes that to the SD card when there's 64 bytes to write.

Mostly, this works just fine, with the stream buffer having on average 2 bytes in it at a time.  Until some other major task under FreeRTOS happens (notably a datalogging action every 10 minutes that formats several handfuls of variables and write almost 500 bytes to the SD card.  When this happens, the UART ISR will fill the stream buffer to capacity, and then things go south.  Because I don't want to lose data, my ISR doesn't move data from the FIFO to the stream buffer if there's no room.  But because I'm not moving data from the FIFO, the interrupt happens again immediately, without allowing any other code to run (or, if the interrupt is set to edge driven, the interrupt never happens again).

Here's some code snippets:

/*

* XBC Rx Interrupt Handler

*

* This passes data to the xbc stream buffer

*/

void XBC_Rx_ISR_Interrupt_InterruptCallback(){

   

    BaseType_t higherTaskWoken = pdFALSE;

    uint8 status = UART_XBC_Cellular_ReadRxStatus();

    size_t cap = xStreamBufferSpacesAvailable(xbcInputQueue);

    while (status & UART_XBC_Cellular_RX_STS_FIFO_NOTEMPTY && cap > 0){

       

        uint8 temp = UART_XBC_Cellular_ReadRxData();

       

        if (xStreamBufferSendFromISR(xbcInputQueue, &temp, 1, &higherTaskWoken) != 1){

            UART_232_PutStringConst("-<!ERR XBC UNKNOWN!>-\r\n");

        }

       

        status = UART_XBC_Cellular_ReadRxStatus();

        cap--;

    }

    portYIELD_FROM_ISR(higherTaskWoken)l

}

And the loop that reads the stream buffer:

                while (content_length > 0){

                    r = xStreamBufferReceive(xbcInputQueue, &c, 1, pdMS_TO_TICKS(2000));

                    if (r == pdFALSE){

                         //timeout

                    }

                    content_length--;

                    Buffer_xbc[Buffer_xbc_Ptr++] = c;

                    if (Buffer_xbc_Ptr == WRITE_TO_DISK_BLOCK_SIZE){

                            FS_Write(pFile, Buffer_xbc, WRITE_TO_DISK_BLOCK_SIZE);

                            Buffer_xbc_Ptr = 0;

                    }

               }

Is there a way to turn off or delay the Rx interrupt from the UART long enough for the task to catch up and empty the stream buffer, or do I need to work out another solution.  I believe I could switch the whole thing to polling (I don't get data from the XBee unless I'm expecting it) if that is the correct thing to do in this situation.  It just would be a lot of leg work.

Thanks in advance!!

0 Likes
1 Solution
ChVa_3548136
Level 2
Level 2
Welcome!

So I have the problem fixed, and it took some specific testing, digging through the forums, and careful reading of the Digi documentation.

The problem stems from the Digi XBee modem.  When RTS is dis-asserted (set high) as part of flow control, the XBee will send up to 4 bytes before it actually stops flow.  Relying on the UART to dis-assert RTS when the FIFO fills would result in lost bytes.

The solution was to wire a control register to the RTS pin through an OR gate, and set that Register high when the Stream buffer capacity drops below a certain threshold (in the ISR), then drop it when it raises above another threshold (checked in a 1 kHz timer).

View solution in original post

0 Likes
6 Replies