Serial Communication via UART
The Universal Asynchronous Receiver Transmitter (UART) is a hardware peripheral that converts data between parallel byte streams and asynchronous serial lines. It is widely used to transfer debugging logs and sensor data between microcontrollers and PCs.
UART Frame Formats (8N1)
Because UART is asynchronous (lacks a shared clock line), sender and receiver must agree on a timing rate (baud rate) and frame format. The standard configuration is 8N1:
- Start Bit: 1 low bit indicating the start of a frame.
- Data Bits: 8 bits of payload data.
- Parity Bit: None (no parity check).
- Stop Bit: 1 high bit indicating the end of a frame.
UART Peripheral Registers
On the ATmega2560, the USART0 peripheral is configured using several memory-mapped registers:
UBRR0H&UBRR0L(Baud Rate Registers): A 16-bit register pair (split into High and Low bytes) that holds the clock divisor factor.UCSR0A(Control & Status Register A): Tracks transceiver flags:UDRE0(USART Data Register Empty): Set to1by hardware when the write buffer is empty and ready for a new byte.RXC0(USART Receive Complete): Set to1by hardware when there is unread data waiting in the receive buffer.
UCSR0B(Control & Status Register B): Enables transmitter (TXEN0), receiver (RXEN0), and interrupts.UCSR0C(Control & Status Register C): Configures frame format (data bits size, stop bits, parity).UDR0(USART Data Register): The physical buffer. Writing a byte toUDR0transmits it; readingUDR0retrieves the received byte.
Baud Rate Divisor Calculation
The divisor UBRR is calculated from the system clock frequency () and the target baud rate:
For a standard ATmega2560 clock frequency of 16 MHz () and a target baud rate of 9600:
Example Transceiver Driver Implementation
#include <avr/io.h>
#include <stdint.h>
void uart_init(uint32_t baud) {
// 1. Calculate and set baud rate registers
uint16_t ubrr_val = F_CPU / 16 / baud - 1;
UBRR0H = (uint8_t)(ubrr_val >> 8);
UBRR0L = (uint8_t)ubrr_val;
// 2. Enable receiver and transmitter
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
// 3. Set frame format: 8 data bits, 1 stop bit, no parity (8N1)
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}
void uart_send_char(char data) {
// Wait until transmit buffer is empty (UDRE0 bit in UCSR0A is 1)
while (!(UCSR0A & (1 << UDRE0)));
// Write byte to data register to trigger transmission
UDR0 = data;
}
char uart_receive_char(void) {
// Wait until receive buffer has data (RXC0 bit in UCSR0A is 1)
while (!(UCSR0A & (1 << RXC0)));
// Read and return data register
return UDR0;
}
Which bit inside the status register UCSR0A must be polled before writing a new byte to UDR0, and why?
Baud Rate Register Math
// Calculate UBRR divisor for a 16MHz clock and 115200 baud // UBRR = 16000000 / (16 * 115200) - 1 = 8.68 -> rounds to 8 uint16_t ubrr = / (16UL * ) - 1;
References & Further Reading
- Microchip Technology. ATmega2560 8-bit AVR Microcontroller Datasheet. Section 22 (USART).
- VIA University College: Embedded Software 1 (ESW1) Lecture Notes - Section 8 (UART Serial Driver).