A ring buffer helps store incoming UART data smoothly without losing any bytes. It works like a circular queue that wraps around when full.
Ring buffer for UART receive in 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.
RingBuffer uart_rx_buffer;
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; }
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.
#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; }
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.
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.
Practice
ring buffer for UART receive data?Solution
Step 1: Understand UART data flow
UART data arrives asynchronously and can be lost if not stored properly.Step 2: Role of ring buffer
A ring buffer stores data in a circular way so new data overwrites old only when full, preventing data loss.Final Answer:
To store incoming data in a circular way to prevent data loss -> Option DQuick Check:
Ring buffer = circular storage for UART data [OK]
- Confusing ring buffer with baud rate control
- Thinking it encrypts data
- Assuming it converts data format
Solution
Step 1: Understand array declaration syntax
In C, arrays are declared with type, name, and size in square brackets.Step 2: Check each option
char ringBuffer[64]; correctly declares a char array of size 64. Others are invalid syntax or wrong types.Final Answer:
char ringBuffer[64]; -> Option AQuick Check:
Array declaration = type name[size] [OK]
- Using parentheses instead of brackets
- Assigning size to variable instead of array
- Leaving size empty or incorrect
uint8_t buffer[8]; uint8_t head = 0; uint8_t tail = 0; // Add data buffer[head] = 0x55; head = (head + 1) % 8; // Add data buffer[head] = 0xAA; head = (head + 1) % 8; // Read data uint8_t data = buffer[tail]; tail = (tail + 1) % 8;What is the value of
data after this code runs?Solution
Step 1: Track buffer writes
First, 0x55 is stored at buffer[0], head moves to 1. Then 0xAA stored at buffer[1], head moves to 2.Step 2: Read from tail
Tail is 0, so reading buffer[0] returns 0x55, then tail moves to 1.Final Answer:
0x55 -> Option BQuick Check:
Read at tail=0 returns first stored value 0x55 [OK]
- Reading from head instead of tail
- Forgetting modulo wrap-around
- Assuming data is 0xAA instead of 0x55
if ((head + 1) % BUFFER_SIZE == tail) {
// Buffer full
return;
}
buffer[head] = data;
head = (head + 1) % BUFFER_SIZE;
Solution
Step 1: Understand buffer full condition
Buffer is full when next head position equals tail, meaning no space to write.Step 2: Analyze given condition
The condition uses (head + 1) % BUFFER_SIZE == tail, which correctly detects full buffer.Final Answer:
Buffer full condition is correct, no bug -> Option AQuick Check:
Full when next head equals tail [OK]
- Using wrong comparison operator
- Forgetting modulo operation
- Checking head == tail for full (actually empty)
uart_receive_byte() that reads one byte from the ring buffer if available, or returns -1 if empty. Which code snippet correctly implements this?Solution
Step 1: Check empty condition
Buffer is empty when head equals tail, so return -1 in that case.Step 2: Read data and update tail
Read from buffer[tail], then move tail forward with modulo wrap.Final Answer:
Function returns -1 if empty, else data from tail and updates tail -> Option CQuick Check:
Empty if head==tail; read from tail pointer [OK]
- Checking wrong empty condition
- Reading from head instead of tail
- Returning 0 instead of -1 when empty
Ready to prove it?
Pass the test and this pattern moves unknown
