Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 953
Meter Code Avatar asked Jul 25 '11 21:07

Meter Code


People also ask

What is UART buffer?

A buffer is an area of memory in the UART in which to store incoming data. It allows data to be stored physically in the UART which can later be serviced by a software driver.

What is the size of receive and transmit buffer in UART?

RX Buffer Size (bytes)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.

What is the receive buffer?

It is defined as the variance of packet arrival times at the end-user buffer. It occurs when packets travel on different network paths to reach the same destination.

What is UART FIFO?

A FIFO (First In First Out) is a UART buffer that forces each byte of your serial communication to be passed on in the order received. For an 8250 or 16450 UART, for example, the FIFO has a size of only one byte.


2 Answers

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 */
    }    
}
like image 71
Felipe Lavratti Avatar answered Sep 30 '22 11:09

Felipe Lavratti


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.

like image 31
supercat Avatar answered Sep 30 '22 10:09

supercat