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?
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.
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.
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.
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.
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 */
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With