From 0cc65db1b2799d2a759f7b29689c35b206d34618 Mon Sep 17 00:00:00 2001 From: Jon Thacker Date: Fri, 8 Jul 2016 18:49:34 -0500 Subject: [PATCH] cpu/atmega_common: Add support for double speed baud generator * Add support for U2X switch to double transmission speed * Provides lower error rates when needed * Add support for static baud rate calculation, reduces computation by 10x * adds support for statically enabling double speed baud --- cpu/atmega_common/periph/uart.c | 60 +++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/cpu/atmega_common/periph/uart.c b/cpu/atmega_common/periph/uart.c index 4736f57054..2d3bd52d00 100644 --- a/cpu/atmega_common/periph/uart.c +++ b/cpu/atmega_common/periph/uart.c @@ -16,6 +16,15 @@ * @author Hauke Petersen * @author Hinnerk van Bruinehsen * + * + * Support static BAUD rate calculation using UART_STDIO_BAUDRATE. + * Set UART_STDIO_BAUDRATE to the desired baud rate and pass it as a -D argument + * at compliation time (e.g. in the boards Makefile.include file). + * UART_BAUD_TOL can be set to guarantee a BAUD rate tolerance at compile time or + * to switch to double speed transmission (U2X) to achieve a lower tolerance. + * At runtime, this tolerance is not guaranteed to be met. + * However, an error message will be displayed at compile time. + * * @} */ @@ -25,6 +34,26 @@ #include "periph/uart.h" + +/** + * @brief Maximum percentage error in calculated baud before switching to double speed transmission (U2X) + * + * Takes whole numbers from 0 to 100, inclusive, with a default of 2. + */ +#if defined(UART_BAUD_TOL) +// BAUD_TOL is defined here as it is used by the setbaud.h utility +#define BAUD_TOL UART_BAUD_TOL +#else +#define BAUD_TOL 2 +#endif + +#if defined(UART_STDIO_BAUDRATE) +// BAUD and F_CPU are required by setbaud.h to calculated BRR +#define BAUD UART_STDIO_BAUDRATE +#define F_CPU CLOCK_CORECLOCK +#include +#endif + /** * @brief Configured device map * @{ @@ -54,6 +83,33 @@ static const mega_uart_t *dev[] = { NULL }; */ static uart_isr_ctx_t isr_ctx[UART_NUMOF]; +static void _update_brr(uart_t uart, uint16_t brr, bool double_speed) +{ + dev[uart]->BRR = brr; + if (double_speed) { + dev[uart]->CSRA |= (1 << U2X0); + } +} + +static void _set_brr(uart_t uart, uint32_t baudrate) +{ + uint16_t brr; +#if defined(UART_STDIO_BAUDRATE) + // UBRR_VALUE and USE_2X are statically computed from + if (baudrate == UART_STDIO_BAUDRATE) { + _update_brr(uart, UBRR_VALUE, USE_2X); + return; + } +#endif +#if defined(UART_DOUBLE_SPEED) + brr = (CLOCK_CORECLOCK + 4UL * baudrate) / (8UL * baudrate) - 1UL; + _update_brr(uart, brr, true); +#else + brr = (CLOCK_CORECLOCK + 8UL * baudrate) / (16UL * baudrate) - 1UL; + _update_brr(uart, brr, false); +#endif +} + int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) { /* make sure the given device is valid */ @@ -72,7 +128,7 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) /* configure UART to 8N1 mode */ dev[uart]->CSRC = (1 << UCSZ00) | (1 << UCSZ01); /* set clock divider */ - dev[uart]->BRR = CLOCK_CORECLOCK / (16 * baudrate); + _set_brr(uart, baudrate); /* enable RX and TX and the RX interrupt */ dev[uart]->CSRB = ((1 << RXCIE0) | (1 << RXEN0) | (1 << TXEN0)); @@ -82,7 +138,7 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) void uart_write(uart_t uart, const uint8_t *data, size_t len) { for (size_t i = 0; i < len; i++) { - while (!(dev[uart]->CSRA & (1 << UDRE0))); + while (!(dev[uart]->CSRA & (1 << UDRE0))) {}; dev[uart]->DR = data[i]; } }