A ring buffer helps store incoming UART data smoothly without losing any bytes. It works like a circular queue that wraps around when full.
0
0
Ring buffer for UART receive in Embedded C
Introduction
When receiving data from UART continuously without blocking the main program.
When you want to store incoming bytes temporarily before processing them.
When you need to handle data at different speeds between sender and receiver.
When you want to avoid losing data if the processor is busy.
When implementing serial communication in embedded systems.
Syntax
Embedded C
typedef struct {
uint8_t buffer[BUFFER_SIZE];
volatile uint16_t head;
volatile uint16_t tail;
} RingBuffer;
void ring_buffer_init(RingBuffer *rb);
int ring_buffer_put(RingBuffer *rb, uint8_t data);
int ring_buffer_get(RingBuffer *rb, uint8_t *data);The buffer array holds the data bytes.
Head points to where new data is added; tail points to where data is read.
Examples
Initialize the ring buffer by setting head and tail to zero.
Embedded C
RingBuffer uart_rx_buffer;
void ring_buffer_init(RingBuffer *rb) {
rb->head = 0;
rb->tail = 0;
}Add a byte to the buffer if there is space; return error if full.
Embedded C
int ring_buffer_put(RingBuffer *rb, uint8_t data) { uint16_t next = (rb->head + 1) % BUFFER_SIZE; if (next == rb->tail) { return -1; // Buffer full } rb->buffer[rb->head] = data; rb->head = next; return 0; }
Read a byte from the buffer if available; return error if empty.
Embedded C
int ring_buffer_get(RingBuffer *rb, uint8_t *data) { if (rb->head == rb->tail) { return -1; // Buffer empty } *data = rb->buffer[rb->tail]; rb->tail = (rb->tail + 1) % BUFFER_SIZE; return 0; }
Sample Program
This program creates a ring buffer for UART receive data. It simulates receiving 7 bytes and stores them in the buffer. Then it reads and prints all bytes from the buffer.
Embedded C
#include <stdio.h> #include <stdint.h> #define BUFFER_SIZE 8 typedef struct { uint8_t buffer[BUFFER_SIZE]; volatile uint16_t head; volatile uint16_t tail; } RingBuffer; void ring_buffer_init(RingBuffer *rb) { rb->head = 0; rb->tail = 0; } int ring_buffer_put(RingBuffer *rb, uint8_t data) { uint16_t next = (rb->head + 1) % BUFFER_SIZE; if (next == rb->tail) { return -1; // Buffer full } rb->buffer[rb->head] = data; rb->head = next; return 0; } int ring_buffer_get(RingBuffer *rb, uint8_t *data) { if (rb->head == rb->tail) { return -1; // Buffer empty } *data = rb->buffer[rb->tail]; rb->tail = (rb->tail + 1) % BUFFER_SIZE; return 0; } int main() { RingBuffer uart_rx_buffer; ring_buffer_init(&uart_rx_buffer); // Simulate receiving bytes from UART for (uint8_t i = 1; i <= 7; i++) { if (ring_buffer_put(&uart_rx_buffer, i) == 0) { printf("Received byte: %d\n", i); } else { printf("Buffer full, cannot receive byte: %d\n", i); } } // Read bytes from buffer uint8_t data; printf("Reading bytes from buffer:\n"); while (ring_buffer_get(&uart_rx_buffer, &data) == 0) { printf("Byte: %d\n", data); } return 0; }
OutputSuccess
Important Notes
Make sure the buffer size is a power of two for easier modulo operations (optional optimization).
Use volatile keyword for head and tail if they are changed in interrupt handlers.
Always check for buffer full before adding data to avoid overwriting unread data.
Summary
A ring buffer stores UART data in a circular way to avoid data loss.
Use head and tail pointers to track where to add and read data.
Check for full and empty conditions to manage buffer safely.