
Photo Pete Prodoehl
I like to use various Arduino boards for AVR development. What I do not like are the Arduino libraries. They are often just wrappers around libc functions or rewrites of functions libc already provides. Serial communications is one good example. Arduino provides you with its own implementation of Serial.print(), Serial.println() and Serial.read() methods. At the same time AVR Libc has proven printf(), puts() and getchar() functions. This article explains easy implementation of libc functions used for serial communications.
AVR microcontrollers have three control and status registers. Register UCSR0A mostly contains status data. UCSR0B and UCSR0C contain all the configuration settings. See the tables in the end of article for all possible values.
AVR Libc provides helper macros for baud rate calculations. Header file requires F_CPU and BAUD to be defined. After including the header file UBRRL_VALUE, UBRRH_VALUE and USE_2X are defined. First two are used to set UART speed. Last one is used to determine if UART has to be configured to run in double speed mode with given baud rate.
UCSZ20 UCSZ01 and UCSZ00 control the data size. Possible sizes are 5-bit (000), 6-bit (001), 7-bit (010), 8-bit (011) and 9-bit (111). Most common used data size is 8-bit.
With above bits we can set most common configuration: no parity, 8 data bits, 1 stop bit.
#define F_CPU 16000000UL
#define BAUD 9600
#include <util/setbaud.h>
void uart_init(void) {
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
#if USE_2X
UCSR0A |= _BV(U2X0);
#else
UCSR0A &= ~(_BV(U2X0));
#endif
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */
UCSR0B = _BV(RXEN0) | _BV(TXEN0); /* Enable RX and TX */
}
You can transmit data to UART by writing a byte to USART Data Register UDR0. First you have to make sure UART is ready to transmit new data. You can wait until USART Data Register Empty UDRE flag is set. Alternatively you can wait after each byte to transmission be ready. USART Transmit Complete TXC0 is set when transmission is ready.
void uart_putchar(char c) {
loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
UDR0 = c;
}
void uart_putchar(char c) {
UDR0 = c;
loop_until_bit_is_set(UCSR0A, TXC0); /* Wait until transmission ready. */
}
You can receive data from UART by reading a byte from USART Data Register UDR0. USART Receive Complete RXC0 flag is set if to unread data exists in data register.
char uart_getchar(void) {
loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */
return UDR0;
}
FDEV_SETUP_STREAM macro can be used to setup a buffer which is valid for stdio operations. Initialized buffer will be of type FILE. You can define separate buffers for input and output. Alternatively you can define only one buffer which works for both input and output. First and second parameters are names of the functions which will be called when data is either read from or written to the buffer.
FILE uart_output = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE); FILE uart_input = FDEV_SETUP_STREAM(NULL, uart_getchar, _FDEV_SETUP_READ);
FILE uart_io FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
To prepare our uart_putchar and uart_getchar function to be used with streams we have to change the definition a bit. To properly format output we also force adding a carriage return after newline has been sent.
void uart_putchar(char c, FILE *stream) {
if (c == '\n') {
uart_putchar('\r', stream);
}
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = c;
}
char uart_getchar(FILE *stream) {
loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */
return UDR0;
}
Now we can redirect both STDIN and STDOUT to UART. This enables us to use AVR Libc provided functions to read and write to serial port.
int main(void) {
uart_init();
stdout = &uart_output;
stdin = &uart_input;
char input;
while(1) {
puts("Hello world!");
input = getchar();
printf("You wrote %c\n", input);
}
return 0;
}
| UCSR0A Bit # | Name | Description |
|---|---|---|
| bit 7 | RXC0 |
USART Receive Complete. Set when data is available and the data register has not be read yet. |
| bit 6 | TXC0 |
USART Transmit Complete. Set when all data has transmitted. |
| bit 5 | UDRE0 |
USART Data Register Empty. Set when the UDR0 register is empty and new data can be transmitted. |
| bit 4 | FE0 |
Frame Error. Set when next byte in the UDR0 register has a framing error. |
| bit 3 | DOR0 |
Data OverRun. Set when the UDR0 was not read before the next frame arrived. |
| bit 2 | UPE0 |
USART Parity Error. Set when next frame in the UDR0 has a parity error. |
| bit 1 | U2X0 |
USART Double Transmission Speed. When set decreases the bit time by half doubling the speed. |
| bit 0 | MPCM0 |
Multi-processor Communication Mode. When set incoming data is ignored if no addressing information is provided. |
| UCSR0B Bit # | Name | Description |
|---|---|---|
| bit 7 | RXCIE0 |
RX Complete Interrupt Enable. Set to allow receive complete interrupts. |
| bit 6 | TXCIE0 |
TX Complete Interrupt Enable. Set to allow transmission complete interrupts. |
| bit 5 | UDRIE0 |
USART Data Register Empty Interrupt Enable. Set to allow data register empty interrupts. |
| bit 4 | RXEN0 |
Receiver Enable. Set to enable receiver. |
| bit 3 | TXEN0 |
Transmitter enable. Set to enable transmitter. |
| bit 2 | UCSZ20 |
USART Character Size 0. Used together with UCSZ01 and UCSZ00 to set data frame size. Available sizes are 5-bit (000), 6-bit (001), 7-bit (010), 8-bit (011) and 9-bit (111). |
| bit 1 | RXB80 |
Receive Data Bit 8. When using 8 bit transmission the 8th bit received. |
| bit 0 | TXB80 |
Transmit Data Bit 8. When using 8 bit transmission the 8th bit to be submitted. |
| UCSR0C Bit # | Name | Description |
|---|---|---|
| bit 7 bit 6 |
UMSEL01UMSEL00
|
USART Mode Select 1 and 0. UMSEL01 and UMSEL00 combined select the operating mode. Available modes are asynchronous (00), synchronous (01) and master SPI (11). |
| bit 5 bit 4 |
UPM01UPM00
|
USART Parity Mode 1 and 0. UPM01 and UPM00 select the parity. Available modes are none (00), even (10) and odd (11). |
| bit 3 | USBS0 |
USART Stop Bit Select. Set to select 1 stop bit. Unset to select 2 stop bits. |
| bit 2 bit 1 |
UCSZ01UCSZ00
|
USART Character Size 1 and 0. Used together with with UCSZ20 to set data frame size. Available sizes are 5-bit (000), 6-bit (001), 7-bit (010), 8-bit (011) and 9-bit (111). |
| bit 0 | UCPOL0 |
USART Clock Polarity. Set to transmit on falling edge and sample on rising edge. Unset to transmit on rising edge and sample on falling edge. |
Full source code of this article. USART entry in QEEWiki by Q. Serial Communication Using AVR Microcontroller USART by Engineers Garage. Serial Communication Notes by Rod Byrne.
Tagged with: AVR Electronics