From 6212b63ac63d3de2bc8c5c5332add085fb16b551 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Sun, 23 Dec 2012 15:20:24 +0100 Subject: digital/ucoolib/ucoolib/hal/uart: implement uart module --- digital/ucoolib/ucoolib/hal/uart/test/Config | 4 + digital/ucoolib/ucoolib/hal/uart/test/Makefile | 2 +- digital/ucoolib/ucoolib/hal/uart/test/test_uart.cc | 100 ++++++++++++- digital/ucoolib/ucoolib/hal/uart/uart.stm32.cc | 162 ++++++++++++++++++++- digital/ucoolib/ucoolib/hal/uart/uart.stm32.hh | 27 +++- 5 files changed, 284 insertions(+), 11 deletions(-) create mode 100644 digital/ucoolib/ucoolib/hal/uart/test/Config diff --git a/digital/ucoolib/ucoolib/hal/uart/test/Config b/digital/ucoolib/ucoolib/hal/uart/test/Config new file mode 100644 index 00000000..4fd7571d --- /dev/null +++ b/digital/ucoolib/ucoolib/hal/uart/test/Config @@ -0,0 +1,4 @@ +[hal/uart] +rx_buffer = 32 +tx_buffer = 8 + diff --git a/digital/ucoolib/ucoolib/hal/uart/test/Makefile b/digital/ucoolib/ucoolib/hal/uart/test/Makefile index 40623e50..5d84850c 100644 --- a/digital/ucoolib/ucoolib/hal/uart/test/Makefile +++ b/digital/ucoolib/ucoolib/hal/uart/test/Makefile @@ -4,6 +4,6 @@ TARGETS = stm32f4 PROGS = test_uart test_uart_SOURCES = test_uart.cc -MODULES = hal/uart +MODULES = hal/uart base/test hal/usb include $(BASE)/build/top.mk diff --git a/digital/ucoolib/ucoolib/hal/uart/test/test_uart.cc b/digital/ucoolib/ucoolib/hal/uart/test/test_uart.cc index ec6f2cb6..3d37f340 100644 --- a/digital/ucoolib/ucoolib/hal/uart/test/test_uart.cc +++ b/digital/ucoolib/ucoolib/hal/uart/test/test_uart.cc @@ -23,9 +23,103 @@ // }}} #include "ucoolib/hal/uart/uart.hh" +#include "ucoolib/arch/arch.hh" +#include "ucoolib/base/test/test.hh" + +#include +#include + +static void +check_act (ucoo::Stream &ts, ucoo::Stream &u, char n) +{ + char buf[3 + 16 + 1]; + if (!u.poll ()) + return; + int r = u.read (buf + 3, 16); + if (r <= 0) + { + buf[3] = '#'; + r = 1; + } + buf[0] = '<'; + buf[1] = n; + buf[2] = ':'; + buf[3 + r] = '>'; + ts.write (buf, 3 + r + 1); +} + int -main () +main (int argc, const char **argv) { - ucoo::Uart u (0, 38400, ucoo::Uart::EVEN, 1); - return 0; + ucoo::arch_init (argc, argv); + ucoo::Stream &ts = ucoo::test_stream (); + ucoo::Uart u1 (0, 38400, ucoo::Uart::EVEN, 1); + ucoo::Uart u3 (2, 38400, ucoo::Uart::EVEN, 1); + ucoo::Uart u4 (3, 38400, ucoo::Uart::EVEN, 1); + // For this test, shorten B6 & B7 to have a loopback on UART1, shorten C10 + // & C11 to connect UART3 to UART4. + rcc_peripheral_enable_clock (&RCC_AHB1ENR, RCC_AHB1ENR_IOPBEN + | RCC_AHB1ENR_IOPCEN); + gpio_mode_setup (GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, + GPIO6 | GPIO7); + gpio_set_af (GPIOB, GPIO_AF7, GPIO6 | GPIO7); + gpio_mode_setup (GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, + GPIO10 | GPIO11); + gpio_set_af (GPIOC, GPIO_AF7, GPIO10); + gpio_set_af (GPIOC, GPIO_AF8, GPIO11); + // Loop to report any activity on ports and provide a simple UI. + char buf[64]; + int buf_i = 0; + ucoo::Uart *u = &u1; + while (1) + { + check_act (ts, u1, '1'); + check_act (ts, u3, '3'); + check_act (ts, u4, '4'); + while (ts.poll ()) + { + char c = ts.getc (); + switch (c) + { + case '?': + static const char help[] = + "? - help\n" + "1, 3, 4 - set output uart\n" + ": - reset output buffer index\n" + "! - send output buffer\n" + "O, E, N - change parity to Odd, Even or None\n" + "any - fill output buffer\n"; + ts.write (help, sizeof (help)); + break; + case '1': + u = &u1; + break; + case '3': + u = &u3; + break; + case '4': + u = &u4; + break; + case ':': + buf_i = 0; + break; + case '!': + u->write (buf, buf_i); + break; + case 'O': + u->setup (38400, ucoo::Uart::ODD, 1); + break; + case 'E': + u->setup (38400, ucoo::Uart::EVEN, 1); + break; + case 'N': + u->setup (38400, ucoo::Uart::NONE, 1); + break; + default: + if (buf_i < static_cast (sizeof (buf))) + buf[buf_i++] = c; + break; + } + } + } } diff --git a/digital/ucoolib/ucoolib/hal/uart/uart.stm32.cc b/digital/ucoolib/ucoolib/hal/uart/uart.stm32.cc index cadac3f6..0fc97efe 100644 --- a/digital/ucoolib/ucoolib/hal/uart/uart.stm32.cc +++ b/digital/ucoolib/ucoolib/hal/uart/uart.stm32.cc @@ -23,39 +23,193 @@ // }}} #include "uart.stm32.hh" -#include "config/hal/uart.hh" +#include +#include +#include + +#ifndef TARGET_stm32f4 +// Need RCC adaptations and USART6 different handling for F1. +# error "it's a trap, only implemented for F4 for the moment" +#endif + +namespace ucoo { + +static const int uart_nb = 6; + +/// Information on UART hardware structure. +struct uart_hardware_t +{ + /// UART base address. + uint32_t base; + /// APB number. + int apb; + /// RCC enable bit. + uint32_t rcc_en; + /// Corresponding IRQ. + int irq; +}; + +/// Information on UART hardware array, this is zero indexed, USART1 is at +/// index 0. +static const uart_hardware_t uart_hardware[uart_nb] = +{ + { USART1, 2, RCC_APB2ENR_USART1EN, NVIC_USART1_IRQ }, + { USART2, 1, RCC_APB1ENR_USART2EN, NVIC_USART2_IRQ }, + { USART3, 1, RCC_APB1ENR_USART3EN, NVIC_USART3_IRQ }, + { UART4, 1, RCC_APB1ENR_UART4EN, NVIC_UART4_IRQ }, + { UART5, 1, RCC_APB1ENR_UART5EN, NVIC_UART5_IRQ }, + { USART6, 2, RCC_APB2ENR_USART6EN, NVIC_USART6_IRQ }, +}; + +static Uart *uart_instances[uart_nb]; + +} // namespace ucoo + +extern "C" { + +void usart1_isr () { ucoo::Uart::isr (0); } + +void usart2_isr () { ucoo::Uart::isr (1); } + +void usart3_isr () { ucoo::Uart::isr (2); } + +void uart4_isr () { ucoo::Uart::isr (3); } + +void uart5_isr () { ucoo::Uart::isr (4); } + +void usart6_isr () { ucoo::Uart::isr (5); } + +} namespace ucoo { Uart::Uart (int n, int speed, Parity parity, int stop_bits) + : n_ (n), error_char_ (default_error_char) { + assert (n < uart_nb); + assert (!uart_instances[n]); + uart_instances[n] = this; + setup (speed, parity, stop_bits); } Uart::~Uart () { + setup (0); + uart_instances[n_] = 0; } void Uart::setup (int speed, Parity parity, int stop_bits) { + uint32_t base = uart_hardware[n_].base; + if (speed) + { + // Turn on. + rcc_peripheral_enable_clock + (uart_hardware[n_].apb == 1 ? &RCC_APB1ENR : &RCC_APB2ENR, + uart_hardware[n_].rcc_en); + // Set speed, rounded to nearest. + int apb_freq = uart_hardware[n_].apb == 1 ? rcc_ppre1_frequency + : rcc_ppre2_frequency; + USART_BRR (base) = (2 * apb_freq + speed) / (2 * speed); + // Set parameters and enable. + if (stop_bits == 1) + USART_CR2 (base) = USART_CR2_STOPBITS_1; + else if (stop_bits == 2) + USART_CR2 (base) = USART_CR2_STOPBITS_2; + else + assert_unreachable (); + USART_CR3 (base) = 0; + uint32_t cr1 = USART_CR1_UE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; + if (parity != NONE) + cr1 |= USART_CR1_M | USART_CR1_PCE; + if (parity == ODD) + cr1 |= USART_CR1_PS; + USART_CR1 (base) = cr1; + // Reset status. + (void) USART_SR (base); + (void) USART_DR (base); + // Enable interrupts. + nvic_enable_irq (uart_hardware[n_].irq); + } + else + { + // Stop UART. + nvic_disable_irq (uart_hardware[n_].irq); + USART_CR1 (base) = 0; + // Turn off. + rcc_peripheral_disable_clock + (uart_hardware[n_].apb == 1 ? &RCC_APB1ENR : &RCC_APB2ENR, + uart_hardware[n_].rcc_en); + } +} + +void +Uart::set_error_char (char c) +{ + error_char_ = c; } int Uart::read (char *buf, int count) { - return -1; + if (block_) + while (rx_fifo_.empty ()) + barrier (); + return rx_fifo_.read (buf, count); } int Uart::write (const char *buf, int count) { - return -1; + int left = count; + while (left) + { + int r = tx_fifo_.write (buf, left); + if (r) + { + USART_CR1 (uart_hardware[n_].base) |= USART_CR1_TXEIE; + buf += r; + left -= r; + } + if (!block_) + break; + } + return count - left; } int Uart::poll () { - return 0; + return rx_fifo_.empty () ? 0 : 1; +} + +void +Uart::isr (int n) +{ + uint32_t base = uart_hardware[n].base; + uint32_t sr = USART_SR (base); + uint32_t dr = USART_DR (base); + assert (uart_instances[n]); + Uart &uart = *uart_instances[n]; + if (sr & (USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE)) + { + dr = uart.error_char_; + // Datasheet is not really clear about this when error bits are set. + sr |= USART_SR_RXNE; + } + if (sr & USART_SR_RXNE) + { + if (!uart.rx_fifo_.full ()) + uart.rx_fifo_.push (dr); + } + if (sr & USART_SR_TXE) + { + if (!uart.tx_fifo_.empty ()) + USART_DR (base) = static_cast (uart.tx_fifo_.pop ()); + if (uart.tx_fifo_.empty ()) + USART_CR1 (base) &= ~USART_CR1_TXEIE; + } } } // namespace ucoo diff --git a/digital/ucoolib/ucoolib/hal/uart/uart.stm32.hh b/digital/ucoolib/ucoolib/hal/uart/uart.stm32.hh index d10d12d8..e807e9e9 100644 --- a/digital/ucoolib/ucoolib/hal/uart/uart.stm32.hh +++ b/digital/ucoolib/ucoolib/hal/uart/uart.stm32.hh @@ -24,28 +24,49 @@ // // }}} #include "ucoolib/intf/stream.hh" +#include "ucoolib/utils/fifo.hh" + +#include "config/hal/uart.hh" namespace ucoo { /// Universal asynchronous receiver transmitter (UART). +/// +/// When an error is detected on RX, error character is inserted in receive +/// FIFO. class Uart : public Stream { public: /// Parity setting. enum Parity { ODD, EVEN, NONE }; + /// Default error character. + static const char default_error_char = '~'; public: /// Initialise the Nth UART with given parameters. - Uart (int n, int speed, Parity parity, int stop_bits); + Uart (int n, int speed = 0, Parity parity = NONE, int stop_bits = 1); /// Shutdown UART. ~Uart (); - /// Change UART settings. - void setup (int speed, Parity parity, int stop_bits); + /// Change UART settings, use speed 0 to stop UART. + void setup (int speed, Parity parity = NONE, int stop_bits = 1); + /// Change the error character. + void set_error_char (char c); /// See Stream::read. int read (char *buf, int count); /// See Stream::write. int write (const char *buf, int count); /// See Stream::poll. int poll (); + /// Handle interrupts. + static void isr (int n); + private: + /// UART number. + int n_; + /// RX FIFO, filled by interrupt handler. + Fifo rx_fifo_; + /// TX FIFO, emptied by interrupt handler. + Fifo tx_fifo_; + /// Error character, inserted in case of error. + char error_char_; }; } // namespace ucoo -- cgit v1.2.3