Correct way of implementing a uart receive buffer in a small ARM microcontroller?

11,051

Solution 1

ISR should fill a FIFO. Main should consume it.

Bellow a very simple fifo algorithm:

#define RINGFIFO_SIZE (1024)              /* serial buffer in bytes (power 2)   */
#define RINGFIFO_MASK (RINGFIFO_SIZE-1ul) /* buffer size mask                   */

/* Buffer read / write macros                                                 */
#define RINGFIFO_RESET(ringFifo)      {ringFifo.rdIdx = ringFifo.wrIdx = 0;}
#define RINGFIFO_WR(ringFifo, dataIn) {ringFifo.data[RINGFIFO_MASK & ringFifo.wrIdx++] = (dataIn);}
#define RINGFIFO_RD(ringFifo, dataOut){ringFifo.rdIdx++; dataOut = ringFifo.data[RINGFIFO_MASK & (ringFifo.rdIdx-1)];}
#define RINGFIFO_EMPTY(ringFifo)      (ringFifo.rdIdx == ringFifo.wrIdx)
#define RINGFIFO_FULL(ringFifo)       ((RINGFIFO_MASK & ringFifo.rdIdx) == (RINGFIFO_MASK & (ringFifo.wrIdx+1)))
#define RINGFIFO_COUNT(ringFifo)      (RINGFIFO_MASK & (ringFifo.wrIdx - ringFifo.rdIdx))

/* buffer type                                                                */
typedef struct{
    uint32_t size;
    uint32_t wrIdx;
    uint32_t rdIdx;
    uint8_t data[RINGFIFO_SIZE];
} RingFifo_t;
RingFifo_t gUartFifo;

(Care must be taken with this FIFO algorithm, size MUST be power of 2)

The ISR should behave like this:

void ISR_Handler()
{
    uint8_t c;
    while(UART_NotEmpty()) {
        c = UART_GetByte();
        RINGFIFO_WR(gUartFifo, c);
    }
}

And the Main:

while(1)
{
    if (!RINGFIFO_EMPTY(gUartFifo)) {
        /* consume fifo using RINGFIFO_RD */
    }    
}

This algorithm reads the FIFO directly from the main loop, you should use a intermediate layer that checks if there is a full packet in the buffer, and deals with it, in such a manner that main would be like this:

uint8_t ptrToPacket;
uint32_t packetSize;
while(1)
{
    if (!Uart_HasValidPacket()) {
        Uart_GetPacket(&ptrToPacket, &packetSize)
        /* Process packet using ptrToPacket and packetSize */
    }    
}

Solution 2

The approach you suggest would probably be workable if the uartindex is never written in the main loop (except to initialize it while interrupts are disabled), and localindex is never touched by the interrupt routine.

I would suggest that you make your buffer size be a power of 2, use unsigned integers for the two indices, and allow them to count freely over their full 32-bit size; use bit masking when indexing your buffer in both the "stuff" and "fetch" routines. If you do that, then

(unsigned)(uartindex-localindex) 

should indicate how many characters are in the buffer, even when it's completely full, without requiring special-case behavior in the buffer-full case and without limiting an N-byte buffer to holding N-1 items.

Note that while the typecast in the aforementioned expression isn't strictly necessary, I would recommend including it since it makes obvious that the wrapping behavior when subtracting unsigned quantities is deliberate and expected.

Share:
11,051

Related videos on Youtube

Meter Code
Author by

Meter Code

Updated on June 04, 2022

Comments

  • Meter Code
    Meter Code almost 2 years

    I am looking for ideas for a receive buffer for a small application dealing with 15 byte packets at 921.6Kbaud over rs485. I am thinking of using a circular buffer as the interface between the UART ISR and main. As it is a microprocessor I was wanting to put

    while (uartindex!=localindex) { do stuff } 
    

    in the

    while (;;) {do forever} 
    

    part of main but I have been told this is not acceptable.

    How do people deal with their uarts under similar circumstances?

    • Michael Dorgan
      Michael Dorgan over 12 years
      Lol - I'm an ARM asm guy and this made me giggle out of my own ignorance. I'll watch this to enlighten myself going forward :)
    • old_timer
      old_timer over 12 years
      I have done things like that and had no problems, so am curious to see what folks say about this and why it is wrong.
    • artless noise
      artless noise about 11 years
      The only thing that can go wrong is not to get the concept of a memory barrier.