USART_D is a high-performance, interrupt-driven USART communication library specifically designed for 8-bit AVR micro-controllers. It offers efficient memory management through circular buffers and a flexible abstraction layer that supports both modern (e.g., ATmega328P) and legacy (e.g., ATmega8) AVR architectures. It provides a robust alternative to standard blocking I/O by utilizing circular buffers and background interrupt processing, allowing your application logic to remain responsive during heavy serial communication.
See
/examplesdirectory for comprehensive implementation templates.
Unlike standard putchar functions that "spin-lock" (wait) until the hardware buffer is empty, USART_D uses a circular buffer. When you call a print function:
- Data is pushed to the
tx_buf. - The UDRE (Data Register Empty) interrupt is enabled.
- The CPU immediately returns to your main code.
- The ISR (Interrupt Service Routine) feeds the hardware
UDRregister one byte at a time whenever it is ready, disabling itself only when the buffer is exhausted.
USART_getString() implements a "Watchdog-style" timeout mechanism using Timer2. This is critical for bare-metal systems to prevent the CPU from hanging indefinitely if a sender stops transmitting mid-string.
- Flow: It flushes hardware noise, enables the RXC (Receive Complete) interrupt, and starts Timer2.
- Termination: The process ends if a Carriage Return (
\r) is detected, the buffer limit is reached, or the timer exceeds the~2.4sinactivity threshold (150ticks at16ms).
The library solves the "Legacy vs. Modern" AVR problem. Older chips like the ATmega8 share I/O addresses for the Baud Rate High (UBRRH) and Control (UCSRC) registers. USART_D automatically handles the URSEL register-selection bit and maps inconsistent bit names (e.g., UCSZ0 vs UCSZ00) to a unified internal API.
- Interrupt-Driven Transmission: Non-blocking data transfer using the
UDRE(Data Register Empty) interrupt. Which in turn maximize CPU cycles by handling I/O in the background. - Circular Buffering: Efficient use of memory for
RXandTXbuffers to prevent data loss. And uses Bitwise masking (& (BUFFER_SIZE - 1)) for ultra-fast pointer wrapping (avoiding the division/function which is expensive on 8-bit microcontrollers). - Architecture Agnostic: Uses
reg_defs.hto abstract register names, making it compatible across various AVR MCUs. Uses single codebase forATmega8,16,32,168,328P,2560, and more. - String Formatting: Includes
USART_print, a lightweightprintf-like function supporting%d,%s,%c, and%%without the massive overhead ofstdio.h. - Input Handling:
USART_getStringincludes built-in timeout support (viaTimer2) and rigid backspace/delete handling. - Resource Efficient: Optimized for bare-metal development with direct register manipulation.
- Memory Safe: Explicit bounds checking prevents buffer overflows and memory corruption.
The library dynamically detects your MCU architecture. Supported hardware includes:
- Modern AVR:
ATmega328P,ATmega168,ATmega2560, etc. - Legacy AVR:
ATmega8,ATmega16,ATmega32, etc.
| File | Description |
|---|---|
USART_D.c |
Core implementation of USART initialization, interrupts, and I/O functions. |
USART_D.h |
Public API, configuration macros, and function prototypes. |
reg_defs.h |
Internal hardware abstraction layer for cross-MCU portability. |
Before compiling, ensure you define your clock frequency and desired baud rate in your project settings, makefile or header. Most Makefiles provide F_CPU automatically.
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#ifndef BAUD
#define BAUD 9600
#endif
#include "USART_D.h"
#include <util/setbaud.h>Default buffer sizes are defined in USART_D.c. You can adjust these based on your RAM constraints.
Note: Sizes must be a power of 2 (e.g., 16, 32, 64, 128) to maintain bitwise masking efficiency.
- TX_BUFFER: Default 32 bytes.
- RX_BUFFER: Default 64 bytes.
Initialization
#include "USART_D.h"
int main(void) {
USART_begin(); // Initializes Baud rate, RX/TX enables, and set global interrupts
USART_print("USART_D System Online\r\n");
while(1) {
// Your logic
}
}int sensorValue = 1024;
char unit[] = "mV";
int16_t temperature = -15;
char status[] = "STABLE";
USART_print("Reading: %d %s\r\n", sensorValue, unit);
// Handles negative numbers and strings efficiently
USART_print("Temp: %d C | Status: %s\r\n", temperature, status);USART_print("Enter Name: ");
const char* input = USART_getString();
USART_print("\r\nHello, %s!", input);Licensed under the MIT License.
Copyright (c) 2026 Dauda Muazu Sulaiman & KibrisOrder.
See the LICENSE file for more details.
Author: Dauda Muazu Sulaiman Organization: KibrisOrder
Special thanks to KibrisOrder for supporting the development of high-performance embedded drivers.
Website: https://ss.kibrisorder.com
"Good and rigid answers are the foundation of stable systems."
(I hope this will be useful to the public and the open source community)