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
PSBU_2325551
Level 4
Level 4
First like received

You can try changing the RX Buffer Size parameter which  defines how many bytes of RAM to allocate for an RX buffer. Data is moved from the receive registers into this buffer.

Four bytes of hardware FIFO are used as a buffer when the buffer size selected is equal to 4 bytes. Buffer sizes greater than 4 bytes require the use of interrupts to handle moving the data from the receive FIFO into this buffer. The UART_GetChar() or UART_ReadRXData() functions get data from the correct source without any changes to your top-level firmware.

When the RX buffer size is greater than 4 bytes, the Internal RX Interrupt ISR is automatically enabled and the RX – On Byte Received interrupt source is selected and disabled

0 Likes

Increasing the buffer size will not solve problem by itself; the stream buffer started as 128 bytes, and was increased to 256 bytes as part of the effort to find/solve the problem.

Will shifting the interrupt handling from my code to inside the PSoC code handle a full buffer better?  Doing so will require moving the code to a polling model, which will be bit of work.

0 Likes

Can you please elaborate which PSoC Code handle are you referring?

0 Likes

The word "handle" is being used as a verb in that sentence.

The questions is: If I switch the UART from a 4 byte receive FIFO to a larger buffer, and the On Byte received interrupt is handled inside the UART code, will that setup recover from the receive buffer becoming full and staying full for 50-100 ms?

0 Likes

chris,

I suggest to use a circular buffer of some large size, external to the UART component. Please check this example of how to arrange such buffer in PSoC5

UART string reception garbage value

In the example, the receiver (PSoC) is waiting for a separator symbols (CR,LF) to extract a complete message, but it seems that in your case it need to be replaced with a length of the packet. So you have to modify that portion of the code.

/odissey1

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).

0 Likes