diff options
-rw-r--r-- | docs/source/lang/api/serial.rst | 5 | ||||
-rw-r--r-- | examples/test-serial-flush.cpp | 29 | ||||
-rw-r--r-- | examples/test-session.cpp | 62 | ||||
-rw-r--r-- | libmaple/usart.c | 302 | ||||
-rw-r--r-- | libmaple/usart.h | 315 | ||||
-rw-r--r-- | libmaple/util.c | 53 | ||||
-rw-r--r-- | wirish/comm/HardwareSerial.cpp | 86 | ||||
-rw-r--r-- | wirish/comm/HardwareSerial.h | 50 |
8 files changed, 582 insertions, 320 deletions
diff --git a/docs/source/lang/api/serial.rst b/docs/source/lang/api/serial.rst index 58002e3..29c70a2 100644 --- a/docs/source/lang/api/serial.rst +++ b/docs/source/lang/api/serial.rst @@ -12,6 +12,9 @@ devices. Introduction ------------ +.. FIXME remove Maple-specific documentation +.. FIXME Serial4, Serial5 updates for high-density devices + The Maple has three serial ports (also known as a UARTs or USARTs): ``Serial1``, ``Serial2``, and ``Serial3``. They communicate using the pins summarized in the following table: @@ -221,7 +224,7 @@ Arduino Compatibility Note Unlike the Arduino, none of the Maple's serial ports is connected to the USB port on the Maple board (for that, use :ref:`SerialUSB <lang-serialusb>`). Thus, to use these pins to communicate with your -personal computer, you will need an additional USB-to-serial adaptor. +personal computer, you will need an additional USB-to-serial adapter. .. TODO LATER port these examples over diff --git a/examples/test-serial-flush.cpp b/examples/test-serial-flush.cpp index 1cd82b6..6c4e100 100644 --- a/examples/test-serial-flush.cpp +++ b/examples/test-serial-flush.cpp @@ -1,21 +1,26 @@ -// Tests the "flush" Serial function +/* + * Tests the "flush" Serial function. + */ #include "wirish.h" +#define COMM Serial1 + void setup() { - /* Send a message out USART2 */ - Serial2.begin(9600); - Serial2.println("Hello world!"); + COMM.begin(9600); + COMM.println("Hello world!"); } void loop() { - Serial2.println("Waiting for multiple input..."); - while(Serial2.available() < 5) { } - Serial2.println(Serial2.read()); - Serial2.println(Serial2.read()); - Serial2.flush(); - if(Serial2.available()) { - Serial2.println("FAIL! Still had junk in the buffer..."); + COMM.println("Waiting for multiple input..."); + while (COMM.available() < 5) + ; + COMM.println(COMM.read()); + COMM.println(COMM.read()); + COMM.flush(); + + if (COMM.available()) { + COMM.println("FAIL! Still had junk in the buffer..."); } } @@ -28,7 +33,7 @@ __attribute__((constructor)) void premain() { int main(void) { setup(); - while (1) { + while (true) { loop(); } return 0; diff --git a/examples/test-session.cpp b/examples/test-session.cpp index a4128ac..01d0fe9 100644 --- a/examples/test-session.cpp +++ b/examples/test-session.cpp @@ -23,6 +23,7 @@ void cmd_adc_stats(void); void cmd_stressful_adc_stats(void); void cmd_everything(void); void cmd_serial1_serial3(void); +void cmd_serial1_echo(void); void cmd_gpio_monitoring(void); void cmd_sequential_adc_reads(void); void cmd_gpio_qa(void); @@ -35,7 +36,8 @@ void cmd_board_info(void); // Helper functions void measure_adc_noise(uint8 pin); void fast_gpio(int pin); -void usart_baud_test(HardwareSerial **serials, int n, unsigned baud); +void serial_baud_test(HardwareSerial **serials, int n, unsigned baud); +void serial_echo_test(HardwareSerial *serial, unsigned baud); void init_all_timers(uint16 prescale); void enable_usarts(void); void disable_usarts(void); @@ -104,6 +106,10 @@ void loop () { cmd_serial1_serial3(); break; + case 'E': + cmd_serial1_echo(); + break; + case '.': while (!SerialUSB.available()) { Serial1.print("."); @@ -225,8 +231,8 @@ void loop () { break; default: // ------------------------------- - SerialUSB.print("Unexpected: "); - SerialUSB.print(input); + SerialUSB.print("Unexpected byte: 0x"); + SerialUSB.print((int)input, HEX); SerialUSB.println(", press h for help."); } @@ -243,11 +249,13 @@ void cmd_print_help(void) { SerialUSB.println("\th: print this menu"); SerialUSB.println("\tw: print Hello World on all 3 USARTS"); SerialUSB.println("\tn: measure noise and do statistics"); - SerialUSB.println("\tN: measure noise and do statistics with background stuff"); + SerialUSB.println("\tN: measure noise and do statistics with background " + "stuff"); SerialUSB.println("\ta: show realtime ADC info"); SerialUSB.println("\t.: echo '.' until new input"); SerialUSB.println("\tu: print Hello World on USB"); - SerialUSB.println("\t_: do as little as possible for a couple seconds (delay)"); + SerialUSB.println("\t_: do as little as possible for a couple seconds " + "(delay)"); SerialUSB.println("\tp: test all PWM channels sequentially"); SerialUSB.println("\tW: dump data as fast as possible on all 3 USARTS"); SerialUSB.println("\tU: dump data as fast as possible on USB"); @@ -256,7 +264,9 @@ void cmd_print_help(void) { SerialUSB.println("\tf: toggle pin 4 as fast as possible in bursts"); SerialUSB.println("\tr: monitor and print GPIO status changes"); SerialUSB.println("\ts: output a sweeping servo PWM on all PWM channels"); - SerialUSB.println("\tm: output data on USART1 and USART3 with various rates"); + SerialUSB.println("\tm: output data on USART1 and USART3 at various " + "baud rates"); + SerialUSB.println("\tE: echo data on USART1 at various baud rates"); SerialUSB.println("\tb: print information about the board."); SerialUSB.println("\t+: test shield mode (for quality assurance testing)"); @@ -316,17 +326,17 @@ void cmd_serial1_serial3(void) { SerialUSB.println("Testing 57600 baud on USART1 and USART3. " "Press any key to stop."); - usart_baud_test(serial_1_and_3, 2, 57600); + serial_baud_test(serial_1_and_3, 2, 57600); SerialUSB.read(); SerialUSB.println("Testing 115200 baud on USART1 and USART3. " "Press any key to stop."); - usart_baud_test(serial_1_and_3, 2, 115200); + serial_baud_test(serial_1_and_3, 2, 115200); SerialUSB.read(); SerialUSB.println("Testing 9600 baud on USART1 and USART3. " "Press any key to stop."); - usart_baud_test(serial_1_and_3, 2, 9600); + serial_baud_test(serial_1_and_3, 2, 9600); SerialUSB.read(); SerialUSB.println("Resetting USART1 and USART3..."); @@ -334,6 +344,27 @@ void cmd_serial1_serial3(void) { Serial3.begin(BAUD); } +void cmd_serial1_echo(void) { + SerialUSB.println("Testing serial echo at various baud rates. " + "Press any key for next baud rate, or ESC to quit " + "early."); + while (!SerialUSB.available()) + ; + SerialUSB.read(); + + SerialUSB.println("Testing 115200 baud on USART1."); + serial_echo_test(&Serial1, 115200); + if (SerialUSB.read() == ESC) return; + + SerialUSB.println("Testing 57600 baud on USART1."); + serial_echo_test(&Serial1, 57600); + if (SerialUSB.read() == ESC) return; + + SerialUSB.println("Testing 9600 baud on USART1."); + serial_echo_test(&Serial1, 9600); + if (SerialUSB.read() == ESC) return; +} + void cmd_gpio_monitoring(void) { SerialUSB.println("Monitoring pin state changes. Press any key to stop."); @@ -615,7 +646,7 @@ void cmd_board_info(void) { // TODO print more information SerialUSB.println("Board information"); SerialUSB.println("================="); - SerialUSB.print("* Clock speed (cycles/us): "); + SerialUSB.print("* Clock speed (MHz): "); SerialUSB.println(CYCLES_PER_MICROSECOND); SerialUSB.print("* BOARD_LED_PIN: "); @@ -693,7 +724,7 @@ void fast_gpio(int maple_pin) { gpio_toggle_bit(dev, bit); } -void usart_baud_test(HardwareSerial **serials, int n, unsigned baud) { +void serial_baud_test(HardwareSerial **serials, int n, unsigned baud) { for (int i = 0; i < n; i++) { serials[i]->begin(baud); } @@ -708,6 +739,15 @@ void usart_baud_test(HardwareSerial **serials, int n, unsigned baud) { } } +void serial_echo_test(HardwareSerial *serial, unsigned baud) { + serial->begin(baud); + while (!SerialUSB.available()) { + if (!serial->available()) + continue; + serial->print(serial->read()); + } +} + static uint16 init_all_timers_prescale = 0; static void set_prescale(timer_dev *dev) { diff --git a/libmaple/usart.c b/libmaple/usart.c index 494a29f..df5e2c5 100644 --- a/libmaple/usart.c +++ b/libmaple/usart.c @@ -23,212 +23,214 @@ *****************************************************************************/ /** + * @file usart.c * @brief USART control routines */ -#include "libmaple.h" -#include "rcc.h" -#include "nvic.h" #include "usart.h" -#define USART1_BASE 0x40013800 -#define USART2_BASE 0x40004400 -#define USART3_BASE 0x40004800 -#define UART4_BASE 0x40004C00 // High-density devices only (Maple Native) -#define UART5_BASE 0x40005000 // High-density devices only (Maple Native) - -#define USART_UE BIT(13) -#define USART_M BIT(12) -#define USART_TE BIT(3) -#define USART_RE BIT(2) -#define USART_RXNEIE BIT(5) // read data register not empty interrupt enable -#define USART_TC BIT(6) - -/* usart descriptor table */ -struct usart_dev usart_dev_table[] = { - [USART1] = { - .base = (usart_port*)USART1_BASE, - .rcc_dev_num = RCC_USART1, - .nvic_dev_num = NVIC_USART1 - }, - [USART2] = { - .base = (usart_port*)USART2_BASE, - .rcc_dev_num = RCC_USART2, - .nvic_dev_num = NVIC_USART2 - }, - [USART3] = { - .base = (usart_port*)USART3_BASE, - .rcc_dev_num = RCC_USART3, - .nvic_dev_num = NVIC_USART3 - }, -#ifdef STM32_HIGH_DENSITY - /* TODO test */ - [UART4] = { - .base = (usart_port*)UART4_BASE, - .rcc_dev_num = RCC_UART4, - .nvic_dev_num = NVIC_UART4 - }, - [UART5] = { - .base = (usart_port*)UART5_BASE, - .rcc_dev_num = RCC_UART5, - .nvic_dev_num = NVIC_UART5 - }, -#endif -}; - /* - * Usart interrupt handlers. + * Devices */ -static inline void usart_irq(int usart_num) { -#ifdef USART_SAFE_INSERT - /* Ignore old bytes if the user defines USART_SAFE_INSERT. */ - rb_safe_insert(&(usart_dev_table[usart_num].rb), - (uint8)((usart_dev_table[usart_num].base)->DR)); -#else - /* By default, push bytes around in the ring buffer. */ - rb_push_insert(&(usart_dev_table[usart_num].rb), - (uint8)((usart_dev_table[usart_num].base)->DR)); -#endif -} - -/* TODO: Check the disassembly for the following functions to make - sure GCC inlined properly. */ - -void __irq_usart1(void) { - usart_irq(USART1); -} - -void __irq_usart2(void) { - usart_irq(USART2); -} - -void __irq_usart3(void) { - usart_irq(USART3); -} +static ring_buffer usart1_rb; +static usart_dev usart1 = { + .regs = USART1_BASE, + .rb = &usart1_rb, + .max_baud = 4500000UL, + .clk_id = RCC_USART1, + .irq_num = NVIC_USART1 +}; +usart_dev *USART1 = &usart1; + +static ring_buffer usart2_rb; +static usart_dev usart2 = { + .regs = USART2_BASE, + .rb = &usart2_rb, + .max_baud = 2250000UL, + .clk_id = RCC_USART2, + .irq_num = NVIC_USART2 +}; +usart_dev *USART2 = &usart2; + +static ring_buffer usart3_rb; +static usart_dev usart3 = { + .regs = USART3_BASE, + .rb = &usart3_rb, + .max_baud = 2250000UL, + .clk_id = RCC_USART3, + .irq_num = NVIC_USART3 +}; +usart_dev *USART3 = &usart3; #ifdef STM32_HIGH_DENSITY -void __irq_uart4(void) { - usart_irq(UART4); -} - -void __irq_uart5(void) { - usart_irq(UART5); -} +static ring_buffer uart4_rb; +static usart_dev uart4 = { + .regs = UART4_BASE, + .rb = &uart4_rb, + .max_baud = 2250000UL, + .clk_id = RCC_UART4, + .irq_num = NVIC_UART4 +}; +usart_dev *UART4 = &uart4; + +static ring_buffer uart5_rb; +static usart_dev uart5 = { + .regs = UART5_BASE, + .rb = &uart5_rb, + .max_baud = 2250000UL, + .clk_id = RCC_UART5, + .irq_num = NVIC_UART5 +}; +usart_dev *UART5 = &uart5; #endif /** - * @brief Enable a USART in single buffer transmission mode, multibuffer - * receiver mode. - * @param usart_num USART to be initialized - * @param baud Baud rate to be set at + * @brief Initialize a serial port. + * @param dev Serial port to be initialized */ -void usart_init(uint8 usart_num, uint32 baud) { -#ifdef STM32_HIGH_DENSITY - ASSERT(usart_num <= UART5); -#else - ASSERT(usart_num <= USART3); -#endif - usart_port *port; - ring_buffer *ring_buf; +void usart_init(usart_dev *dev) { + rb_init(dev->rb, USART_RX_BUF_SIZE, dev->rx_buf); + rcc_clk_enable(dev->clk_id); + nvic_irq_enable(dev->irq_num); +} - uint32 clk_speed; +/** + * @brief Configure a serial port's baud rate. + * + * @param dev Serial port to be configured + * @param clock_speed MCU clock speed, in megahertz. + * @param baud Baud rate for transmit/receive. + */ +void usart_set_baud_rate(usart_dev *dev, uint32 clock_speed, uint32 baud) { uint32 integer_part; uint32 fractional_part; uint32 tmp; - port = usart_dev_table[usart_num].base; - rcc_clk_enable(usart_dev_table[usart_num].rcc_dev_num); - nvic_irq_enable(usart_dev_table[usart_num].nvic_dev_num); - - /* usart1 is mad fast */ - clk_speed = (usart_num == USART1) ? 72000000UL : 36000000UL; - - /* Initialize rx ring buffer */ - rb_init(&usart_dev_table[usart_num].rb, - sizeof (usart_dev_table[usart_num].rx_buf), - usart_dev_table[usart_num].rx_buf); - - /* Set baud rate */ - integer_part = ((25 * clk_speed) / (4 * baud)); + /* See ST RM0008 for the details on configuring the baud rate register */ + integer_part = (25 * clock_speed) / (4 * baud); tmp = (integer_part / 100) << 4; - fractional_part = integer_part - (100 * (tmp >> 4)); tmp |= (((fractional_part * 16) + 50) / 100) & ((uint8)0x0F); - port->BRR = (uint16)tmp; - - port->CR1 = USART_TE | // transmitter enable - USART_RE | // receiver enable - USART_RXNEIE; // receive interrupt enable - - - /* Enable the USART and set mode to 8n1 */ - port->CR1 |= USART_UE; + dev->regs->BRR = (uint16)tmp; } /** - * @brief Turn off all USARTs. + * @brief Enable a serial port. + * + * USART is enabled in single buffer transmission mode, multibuffer + * receiver mode, at the given baud rate, 8n1. + * + * Serial port must have a baud rate configured to work properly. + * + * @param dev Serial port to enable. + * @see usart_set_baud_rate() */ -void usart_disable_all() { - usart_disable(USART1); - usart_disable(USART2); - usart_disable(USART3); -#ifdef STM32_HIGH_DENSITY - usart_disable(UART4); - usart_disable(UART5); -#endif +void usart_enable(usart_dev *dev) { + usart_reg_map *regs = dev->regs; + regs->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE; + regs->CR1 |= USART_CR1_UE; } /** - * @brief Turn off a USART. - * @param USART to be disabled + * @brief Turn off a serial port. + * @param dev Serial port to be disabled */ -void usart_disable(uint8 usart_num) { - usart_port *port = usart_dev_table[usart_num].base; +void usart_disable(usart_dev *dev) { + /* FIXME this misbehaves if you try to use PWM on TX afterwards */ + usart_reg_map *regs = dev->regs; - /* TC bit must be high before disabling the usart */ - while((port->CR1 & USART_UE) && !(port->SR & USART_TC)) + /* TC bit must be high before disabling the USART */ + while((regs->CR1 & USART_CR1_UE) && !(regs->SR & USART_SR_TC)) ; /* Disable UE */ - port->CR1 = 0; + regs->CR1 = 0; /* Clean up buffer */ - usart_reset_rx(usart_num); + usart_reset_rx(dev); } +/** + * @brief Call a function on each USART. + * @param fn Function to call. + */ +void usart_foreach(void (*fn)(usart_dev *dev)) { + fn(USART1); + fn(USART2); + fn(USART3); +#ifdef STM32_HIGH_DENSITY + fn(UART4); + fn(UART5); +#endif +} /** - * @brief Print a null terminated string to the specified USART - * - * @param usart_num usart to send on - * @param str string to send + * @brief Print a null-terminated string to the specified serial port. + * @param dev Serial port to send the string on + * @param str String to send */ -void usart_putstr(uint8 usart_num, const char* str) { +void usart_putstr(usart_dev *dev, const char* str) { char ch; - while((ch = *(str++)) != '\0') { - usart_putc(usart_num, ch); + while ((ch = *(str++)) != '\0') { + usart_putc(dev, ch); } } /** - * @brief Print an unsigned integer to the specified usart + * @brief Print an unsigned integer to the specified serial port. * - * @param usart_num usart to send on - * @param val number to print + * @param dev Serial port to send on + * @param val Number to print */ -void usart_putudec(uint8 usart_num, uint32 val) { +void usart_putudec(usart_dev *dev, uint32 val) { char digits[12]; - int i; + int i = 0; - i = 0; do { digits[i++] = val % 10 + '0'; val /= 10; } while (val > 0); + while (--i >= 0) { - usart_putc(usart_num, digits[i]); + usart_putc(dev, digits[i]); } } + +/* + * Interrupt handlers. + */ + +static inline void usart_irq(usart_dev *dev) { +#ifdef USART_SAFE_INSERT + /* Ignore new bytes if the user defines USART_SAFE_INSERT. */ + rb_safe_insert(dev->rb, (uint8)dev->regs->DR); +#else + /* By default, push bytes around in the ring buffer. */ + rb_push_insert(dev->rb, (uint8)dev->regs->DR); +#endif +} + +void __irq_usart1(void) { + usart_irq(USART1); +} + +void __irq_usart2(void) { + usart_irq(USART2); +} + +void __irq_usart3(void) { + usart_irq(USART3); +} + +#ifdef STM32_HIGH_DENSITY +void __irq_uart4(void) { + usart_irq(UART4); +} + +void __irq_uart5(void) { + usart_irq(UART5); +} +#endif diff --git a/libmaple/usart.h b/libmaple/usart.h index 90b3415..7a93da0 100644 --- a/libmaple/usart.h +++ b/libmaple/usart.h @@ -30,94 +30,293 @@ #ifndef _USART_H_ #define _USART_H_ +#include "libmaple_types.h" +#include "util.h" +#include "rcc.h" +#include "nvic.h" #include "ring_buffer.h" #ifdef __cplusplus extern "C"{ #endif -#define USART_TXE BIT(7) +/* + * Register maps and devices + */ + +/** USART register map type */ +typedef struct usart_reg_map { + __io uint32 SR; /**< Status register */ + __io uint32 DR; /**< Data register */ + __io uint32 BRR; /**< Baud rate register */ + __io uint32 CR1; /**< Control register 1 */ + __io uint32 CR2; /**< Control register 2 */ + __io uint32 CR3; /**< Control register 3 */ + __io uint32 GTPR; /**< Guard time and prescaler register */ +} usart_reg_map; -/* usart device numbers */ -enum { - USART1, - USART2, - USART3, +/** USART1 register map base pointer */ +#define USART1_BASE ((struct usart_reg_map*)0x40013800) +/** USART2 register map base pointer */ +#define USART2_BASE ((struct usart_reg_map*)0x40004400) +/** USART3 register map base pointer */ +#define USART3_BASE ((struct usart_reg_map*)0x40004800) #ifdef STM32_HIGH_DENSITY - UART4, - UART5, +/** UART4 register map base pointer */ +#define UART4_BASE ((struct usart_reg_map*)0x40004C00) +/** UART5 register map base pointer */ +#define UART5_BASE ((struct usart_reg_map*)0x40005000) #endif -}; - -/* peripheral register struct */ -typedef struct usart_port { - volatile uint32 SR; // Status register - volatile uint32 DR; // Data register - volatile uint32 BRR; // Baud rate register - volatile uint32 CR1; // Control register 1 - volatile uint32 CR2; // Control register 2 - volatile uint32 CR3; // Control register 3 - volatile uint32 GTPR; // Guard time and prescaler register -} usart_port; - -/* usart descriptor */ -struct usart_dev { - usart_port *base; - ring_buffer rb; - uint8 rx_buf[64]; - const uint8 rcc_dev_num; - const uint8 nvic_dev_num; -}; - -extern struct usart_dev usart_dev_table[]; + +/* + * Register bit definitions + */ + +/* Status register */ + +#define USART_SR_CTS_BIT 9 +#define USART_SR_LBD_BIT 8 +#define USART_SR_TXE_BIT 7 +#define USART_SR_TC_BIT 6 +#define USART_SR_RXNE_BIT 5 +#define USART_SR_IDLE_BIT 4 +#define USART_SR_ORE_BIT 3 +#define USART_SR_NE_BIT 2 +#define USART_SR_FE_BIT 1 +#define USART_SR_PE_BIT 0 + +#define USART_SR_CTS BIT(USART_SR_CTS_BIT) +#define USART_SR_LBD BIT(USART_SR_LBD_BIT) +#define USART_SR_TXE BIT(USART_SR_TXE_BIT) +#define USART_SR_TC BIT(USART_SR_TC_BIT) +#define USART_SR_RXNE BIT(USART_SR_RXNE_BIT) +#define USART_SR_IDLE BIT(USART_SR_IDLE_BIT) +#define USART_SR_ORE BIT(USART_SR_ORE_BIT) +#define USART_SR_NE BIT(USART_SR_NE_BIT) +#define USART_SR_FE BIT(USART_SR_FE_BIT) +#define USART_SR_PE BIT(USART_SR_PE_BIT) + +/* Data register */ + +#define USART_DR_DR 0xFF + +/* Baud rate register */ + +#define USART_BRR_DIV_MANTISSA (0xFFF << 4) +#define USART_BRR_DIV_FRACTION 0xF + +/* Control register 1 */ + +#define USART_CR1_UE_BIT 13 +#define USART_CR1_M_BIT 12 +#define USART_CR1_WAKE_BIT 11 +#define USART_CR1_PCE_BIT 10 +#define USART_CR1_PS_BIT 9 +#define USART_CR1_PEIE_BIT 8 +#define USART_CR1_TXEIE_BIT 7 +#define USART_CR1_TCIE_BIT 6 +#define USART_CR1_RXNEIE_BIT 5 +#define USART_CR1_IDLEIE_BIT 4 +#define USART_CR1_TE_BIT 3 +#define USART_CR1_RE_BIT 2 +#define USART_CR1_RWU_BIT 1 +#define USART_CR1_SBK_BIT 0 + +#define USART_CR1_UE BIT(USART_CR1_UE_BIT) +#define USART_CR1_M BIT(USART_CR1_M_BIT) +#define USART_CR1_WAKE BIT(USART_CR1_WAKE_BIT) +#define USART_CR1_WAKE_IDLE (0 << USART_CR1_WAKE_BIT) +#define USART_CR1_WAKE_ADDR (1 << USART_CR1_WAKE_BIT) +#define USART_CR1_PCE BIT(USART_CR1_PCE_BIT) +#define USART_CR1_PS BIT(USART_CR1_PS_BIT) +#define USART_CR1_PS_EVEN (0 << USART_CR1_PS_BIT) +#define USART_CR1_PS_ODD (1 << USART_CR1_PS_BIT) +#define USART_CR1_PEIE BIT(USART_CR1_PEIE_BIT) +#define USART_CR1_TXEIE BIT(USART_CR1_TXEIE_BIT) +#define USART_CR1_TCIE BIT(USART_CR1_TCIE_BIT) +#define USART_CR1_RXNEIE BIT(USART_CR1_RXNEIE_BIT) +#define USART_CR1_IDLEIE BIT(USART_CR1_IDLEIE_BIT) +#define USART_CR1_TE BIT(USART_CR1_TE_BIT) +#define USART_CR1_RE BIT(USART_CR1_RE_BIT) +#define USART_CR1_RWU BIT(USART_CR1_RWU_BIT) +#define USART_CR1_RWU_ACTIVE (0 << USART_CR1_RWU_BIT) +#define USART_CR1_RWU_MUTE (1 << USART_CR1_RWU_BIT) +#define USART_CR1_SBK BIT(USART_CR1_SBK_BIT) + +/* Control register 2 */ + +#define USART_CR2_LINEN_BIT 14 +#define USART_CR2_CLKEN_BIT 11 +#define USART_CR2_CPOL_BIT 10 +#define USART_CR2_CPHA_BIT 9 +#define USART_CR2_LBCL_BIT 8 +#define USART_CR2_LBDIE_BIT 6 +#define USART_CR2_LBDL_BIT 5 + +#define USART_CR2_LINEN BIT(USART_CR2_LINEN_BIT) +#define USART_CR2_STOP (0x3 << 12) +#define USART_CR2_STOP_BITS_1 (0x0 << 12) +/* Not on UART4, UART5 */ +#define USART_CR2_STOP_BITS_POINT_5 (0x1 << 12) +/* Not on UART4, UART5 */ +#define USART_CR2_STOP_BITS_1_POINT_5 (0x3 << 12) +#define USART_CR2_STOP_BITS_2 (0x2 << 12) +#define USART_CR2_CLKEN BIT(USART_CR2_CLKEN_BIT) +/* Not on UART4, UART5 */ +#define USART_CR2_CPOL BIT(USART_CR2_CPOL_BIT) +#define USART_CR2_CPOL_LOW (0x0 << USART_CR2_CLKEN_BIT) +#define USART_CR2_CPOL_HIGH (0x1 << USART_CR2_CLKEN_BIT) +/* Not on UART4, UART5 */ +#define USART_CR2_CPHA BIT(USART_CR2_CPHA_BIT) +#define USART_CR2_CPHA_FIRST (0x0 << USART_CR2_CPHA_BIT) +#define USART_CR2_CPHA_SECOND (0x1 << USART_CR2_CPHA_BIT) +/* Not on UART4, UART5 */ +#define USART_CR2_LBCL BIT(USART_CR2_LBCL_BIT) +#define USART_CR2_LBDIE BIT(USART_CR2_LBDIE_BIT) +#define USART_CR2_LBDL BIT(USART_CR2_LBDL_BIT) +#define USART_CR2_LBDL_10_BIT (0 << USART_CR2_LBDL_BIT) +#define USART_CR2_LBDL_11_BIT (1 << USART_CR2_LBDL_BIT) +#define USART_CR2_ADD 0xF + +/* Control register 3 */ + +#define USART_CR3_CTSIE_BIT 10 +#define USART_CR3_CTSE_BIT 9 +#define USART_CR3_RTSE_BIT 8 +#define USART_CR3_DMAT_BIT 7 +#define USART_CR3_DMAR_BIT 6 +#define USART_CR3_SCEN_BIT 5 +#define USART_CR3_NACK_BIT 4 +#define USART_CR3_HDSEL_BIT 3 +#define USART_CR3_IRLP_BIT 2 +#define USART_CR3_IREN_BIT 1 +#define USART_CR3_EIE_BIT 0 + +/* Not on UART4, UART5 */ +#define USART_CR3_CTSIE BIT(USART_CR3_CTSIE_BIT) +/* Not on UART4, UART5 */ +#define USART_CR3_CTSE BIT(USART_CR3_CTSE_BIT) +/* Not on UART4, UART5 */ +#define USART_CR3_RTSE BIT(USART_CR3_RTSE_BIT) +/* Not on UART5 */ +#define USART_CR3_DMAT BIT(USART_CR3_DMAT_BIT) +/* Not on UART5 */ +#define USART_CR3_DMAR BIT(USART_CR3_DMAR_BIT) +/* Not on UART4, UART5 */ +#define USART_CR3_SCEN BIT(USART_CR3_SCEN_BIT) +/* Not on UART4, UART5 */ +#define USART_CR3_NACK BIT(USART_CR3_NACK_BIT) +#define USART_CR3_HDSEL BIT(USART_CR3_HDSEL_BIT) +#define USART_CR3_IRLP BIT(USART_CR3_IRLP_BIT) +#define USART_CR3_IRLP_NORMAL (0 << USART_CR3_IRLP_BIT) +#define USART_CR3_IRLP_LOW_POWER (1 << USART_CR3_IRLP_BIT) +#define USART_CR3_IREN BIT(USART_CR3_IREN_BIT) +#define USART_CR3_EIE BIT(USART_CR3_EIE_BIT) + +/* Guard time and prescaler register */ + +/* Not on UART4, UART5 */ +#define USART_GTPR_GT (0xFF << 8) +/* Not on UART4, UART5 */ +#define USART_GTPR_PSC 0xFF + +/* + * Devices + */ + +#define USART_RX_BUF_SIZE 64 + +/** USART device type */ +typedef struct usart_dev { + usart_reg_map *regs; + ring_buffer *rb; + uint32 max_baud; + uint8 rx_buf[USART_RX_BUF_SIZE]; + rcc_clk_id clk_id; + nvic_irq_num irq_num; +} usart_dev; + +/** USART1 device */ +extern usart_dev *USART1; +/** USART2 device */ +extern usart_dev *USART2; +/** USART3 device */ +extern usart_dev *USART3; +#ifdef STM32_HIGH_DENSITY +/** UART4 device */ +extern usart_dev *UART4; +/** UART5 device */ +extern usart_dev *UART5; +#endif + +#ifdef STM32_MEDIUM_DENSITY +#define NR_USARTS 3 +#elif defined(STM32_HIGH_DENSITY) +#define NR_USARTS 5 +#else +#warn "Only medium and high density devices are currently supported" +#endif + +void usart_init(usart_dev *dev); +void usart_set_baud_rate(usart_dev *dev, uint32 clock_speed, uint32 baud); +void usart_enable(usart_dev *dev); +void usart_disable(usart_dev *dev); +void usart_foreach(void (*fn)(usart_dev *dev)); +void usart_putstr(usart_dev *dev, const char*); +void usart_putudec(usart_dev *dev, uint32 val); /** - * @brief send one character on a usart - * @param usart_num usart to send on - * @param byte byte to send + * @brief Disable all serial ports. */ -static inline void usart_putc(uint8 usart_num, uint8 byte) { - usart_port *port = usart_dev_table[usart_num].base; +static inline void usart_disable_all(void) { + usart_foreach(usart_disable); +} - /* Wait for the buffer to empty */ - while ((port->SR & USART_TXE) == 0) +/** + * @brief Transmit one character on a serial port. + * @param dev Serial port to send on. + * @param byte Byte to transmit. + */ +static inline void usart_putc(usart_dev* dev, uint8 byte) { + usart_reg_map *regs = dev->regs; + + while ((regs->SR & USART_SR_TXE) == 0) ; - port->DR = byte; + regs->DR = byte; } /** - * @brief read one character from a usart - * @param usart_num usart to read from + * @brief Read one character from a serial port. + * + * It's not safe to call this function if the serial port has no data + * available. + * + * @param dev Serial port to read from * @return byte read + * @see usart_data_available() */ -static inline uint8 usart_getc(uint8 usart_num) { - return rb_remove(&usart_dev_table[usart_num].rb); +static inline uint8 usart_getc(usart_dev *dev) { + return rb_remove(dev->rb); } /** - * @brief return the amount of data available in the rx buffer - * @param usart_num which usart to check - * @return number of bytes in the rx buffer + * @brief Return the amount of data available in a serial port's RX buffer. + * @param dev Serial port to check + * @return Number of bytes in dev's RX buffer. */ -static inline uint32 usart_data_available(uint8 usart_num) { - return rb_full_count(&usart_dev_table[usart_num].rb); +static inline uint32 usart_data_available(usart_dev *dev) { + return rb_full_count(dev->rb); } /** - * @brief removes the contents of the rx fifo - * @param usart_num which usart to reset + * @brief Discard the contents of a serial port's RX buffer. + * @param dev Serial port whose buffer to empty. */ -static inline void usart_reset_rx(uint8 usart_num) { - rb_reset(&usart_dev_table[usart_num].rb); +static inline void usart_reset_rx(usart_dev *dev) { + rb_reset(dev->rb); } -void usart_init(uint8 usart_num, uint32 baud); -void usart_disable(uint8 usart_num); -void usart_disable_all(); -void usart_putstr(uint8 usart_num, const char*); -void usart_putudec(uint8 usart_num, uint32 val); - #ifdef __cplusplus } // extern "C" #endif diff --git a/libmaple/util.c b/libmaple/util.c index 77af5b8..4a234ae 100644 --- a/libmaple/util.c +++ b/libmaple/util.c @@ -24,7 +24,7 @@ /** * @brief Utility procedures for debugging, mostly an error LED fade - * and messages dumped over a uart for failed asserts. + * and messages dumped over a UART for failed asserts. */ #include "libmaple.h" @@ -34,38 +34,38 @@ #include "adc.h" #include "timer.h" -/* Failed asserts send out a message on this USART. */ -#ifndef ERROR_USART_NUM -#define ERROR_USART_NUM USART2 +/* Failed ASSERT()s send out a message using this USART config. */ +#ifndef ERROR_USART +#define ERROR_USART USART2 +#define ERROR_USART_CLK_SPEED 72000000UL #define ERROR_USART_BAUD 9600 #define ERROR_TX_PORT GPIOA #define ERROR_TX_PIN 2 #endif /* If you define ERROR_LED_PORT and ERROR_LED_PIN, then a failed - assert will also throb an LED connected to that port an pin. - FIXME this should work together with wirish somehow. */ + * ASSERT() will also throb() an LED connected to that port and pin. + */ #if defined(ERROR_LED_PORT) && defined(ERROR_LED_PIN) #define HAVE_ERROR_LED #endif /** * @brief Disables all peripheral interrupts except USB and fades the - * error LED. - * - * Called from exc.S with global interrupts disabled. + * error LED. */ +/* (Called from exc.S with global interrupts disabled.) */ void __error(void) { /* Turn off peripheral interrupts */ nvic_irq_disable_all(); - /* Turn off timers */ + /* Turn off timers */ timer_disable_all(); /* Turn off ADC */ adc_disable_all(); - /* Turn off all usarts */ + /* Turn off all USARTs */ usart_disable_all(); /* Turn the USB interrupt back on so the bootloader keeps on functioning */ @@ -78,33 +78,33 @@ void __error(void) { } /** - * @brief Prints an error message on a uart upon a failed assertion - * and error throbs. + * @brief Print an error message on a UART upon a failed assertion + * and throb the error LED, if there is one defined. * @param file Source file of failed assertion * @param line Source line of failed assertion * @param exp String representation of failed assertion * @sideeffect Turns of all peripheral interrupts except USB. */ void _fail(const char* file, int line, const char* exp) { - /* Initialize the error usart */ + /* Initialize the error USART */ gpio_set_mode(ERROR_TX_PORT, ERROR_TX_PIN, GPIO_AF_OUTPUT_PP); - usart_init(ERROR_USART_NUM, ERROR_USART_BAUD); + usart_init(ERROR_USART); + usart_set_baud_rate(ERROR_USART, ERROR_USART_CLK_SPEED, ERROR_USART_BAUD); /* Print failed assert message */ - usart_putstr(ERROR_USART_NUM, "ERROR: FAILED ASSERT("); - usart_putstr(ERROR_USART_NUM, exp); - usart_putstr(ERROR_USART_NUM, "): "); - usart_putstr(ERROR_USART_NUM, file); - usart_putstr(ERROR_USART_NUM, ": "); - usart_putudec(ERROR_USART_NUM, line); - usart_putc(ERROR_USART_NUM, '\n'); - usart_putc(ERROR_USART_NUM, '\r'); + usart_putstr(ERROR_USART, "ERROR: FAILED ASSERT("); + usart_putstr(ERROR_USART, exp); + usart_putstr(ERROR_USART, "): "); + usart_putstr(ERROR_USART, file); + usart_putstr(ERROR_USART, ": "); + usart_putudec(ERROR_USART, line); + usart_putc(ERROR_USART, '\n'); + usart_putc(ERROR_USART, '\r'); /* Error fade */ __error(); } - /** * @brief Fades the error LED on and off * @sideeffect Sets output push-pull on ERROR_LED_PIN. @@ -117,7 +117,7 @@ void throb(void) { uint32 i = 0; gpio_set_mode(ERROR_LED_PORT, ERROR_LED_PIN, GPIO_OUTPUT_PP); - /* Error fade */ + /* Error fade. */ while (1) { if (CC == TOP_CNT) { slope = -1; @@ -138,9 +138,8 @@ void throb(void) { i++; } #else - /* No error LED is connected; do nothing. */ + /* No error LED is defined; do nothing. */ while (1) ; #endif } - diff --git a/wirish/comm/HardwareSerial.cpp b/wirish/comm/HardwareSerial.cpp index 8398878..aa8855b 100644 --- a/wirish/comm/HardwareSerial.cpp +++ b/wirish/comm/HardwareSerial.cpp @@ -24,69 +24,85 @@ /** * @file HardwareSerial.cpp - * - * @brief Wiring-like serial api + * @brief Wirish serial port implementation. */ -#include "wirish.h" +#include "gpio.h" +#include "timer.h" + #include "HardwareSerial.h" -#include "usart.h" - -// FIXME: High density device ports, usages of BOARD_USARTx_yX_PIN -// instead of holding onto a gpio_dev, tx_pin, rx_pin, timer_dev, and -// channel_num -- that stuff is all in the PIN_MAP. -HardwareSerial Serial1(USART1, 4500000UL, GPIOA, 9, 10, TIMER1, 2); -HardwareSerial Serial2(USART2, 2250000UL, GPIOA, 2, 3, TIMER2, 3); -HardwareSerial Serial3(USART3, 2250000UL, GPIOB, 10, 11, NULL, 0); - -HardwareSerial::HardwareSerial(uint8 usart_num, - uint32 max_baud, - gpio_dev *gpio_device, +#include "boards.h" + +#define TX1 BOARD_USART1_TX_PIN +#define RX1 BOARD_USART1_RX_PIN +#define TX2 BOARD_USART2_TX_PIN +#define RX2 BOARD_USART2_RX_PIN +#define TX3 BOARD_USART3_TX_PIN +#define RX3 BOARD_USART3_RX_PIN +#if defined STM32_HIGH_DENSITY && !defined(BOARD_maple_RET6) +#define TX4 BOARD_UART4_TX_PIN +#define RX4 BOARD_UART4_RX_PIN +#define TX5 BOARD_UART5_TX_PIN +#define RX5 BOARD_UART5_RX_PIN +#endif + +HardwareSerial Serial1(USART1, TX1, RX1); +HardwareSerial Serial2(USART2, TX2, RX2); +HardwareSerial Serial3(USART3, TX3, RX3); +#if defined(STM32_HIGH_DENSITY) && !defined(BOARD_maple_RET6) +HardwareSerial Serial4(UART4, TX4, RX4); +HardwareSerial Serial5(UART5, TX5, RX5); +#endif + +HardwareSerial::HardwareSerial(usart_dev *usart_device, uint8 tx_pin, - uint8 rx_pin, - timer_dev *timer_device, - uint8 channel_num) { - this->usart_num = usart_num; - this->max_baud = max_baud; - this->gpio_device = gpio_device; + uint8 rx_pin) { + this->usart_device = usart_device; this->tx_pin = tx_pin; this->rx_pin = rx_pin; - this->timer_device = timer_device; - this->channel_num = channel_num; } uint8 HardwareSerial::read(void) { - return usart_getc(usart_num); + return usart_getc(usart_device); } uint32 HardwareSerial::available(void) { - return usart_data_available(usart_num); + return usart_data_available(usart_device); } void HardwareSerial::write(unsigned char ch) { - usart_putc(usart_num, ch); + usart_putc(usart_device, ch); } void HardwareSerial::begin(uint32 baud) { - if (baud > max_baud) { + ASSERT(baud <= usart_device->max_baud); + + if (baud > usart_device->max_baud) { return; } - gpio_set_mode(gpio_device, tx_pin, GPIO_AF_OUTPUT_PP); - gpio_set_mode(gpio_device, rx_pin, GPIO_INPUT_FLOATING); + const stm32_pin_info *txi = &PIN_MAP[tx_pin]; + const stm32_pin_info *rxi = &PIN_MAP[rx_pin]; + + gpio_set_mode(txi->gpio_device, txi->gpio_bit, GPIO_AF_OUTPUT_PP); + gpio_set_mode(rxi->gpio_device, rxi->gpio_bit, GPIO_INPUT_FLOATING); - if (timer_device != NULL) { - /* turn off any pwm if there's a conflict on this usart */ - timer_set_mode(timer_device, channel_num, TIMER_DISABLED); + if (txi->timer_device != NULL) { + /* Turn off any PWM if there's a conflict on this GPIO bit. */ + timer_set_mode(txi->timer_device, txi->timer_channel, TIMER_DISABLED); } - usart_init(usart_num, baud); + usart_init(usart_device); + usart_set_baud_rate(usart_device, + CYCLES_PER_MICROSECOND * 1000000UL, + baud); + usart_enable(usart_device); } void HardwareSerial::end(void) { - usart_disable(usart_num); + usart_disable(usart_device); } void HardwareSerial::flush(void) { - usart_reset_rx(usart_num); + usart_reset_rx(usart_device); } diff --git a/wirish/comm/HardwareSerial.h b/wirish/comm/HardwareSerial.h index 7852d51..5e86f79 100644 --- a/wirish/comm/HardwareSerial.h +++ b/wirish/comm/HardwareSerial.h @@ -24,43 +24,33 @@ /** * @file HardwareSerial.h - * - * @brief Wirish interface to hardware serial communications. + * @brief Wirish serial port interface. */ #ifndef _HARDWARESERIAL_H_ #define _HARDWARESERIAL_H_ #include "libmaple_types.h" -#include "gpio.h" -#include "timer.h" +#include "usart.h" #include "Print.h" -/* NB: this class documented "by hand" (i.e., not using Doxygen) in: - - libmaple/docs/source/lang/serial.rst - - If you alter the public HardwareSerial interface, you must update - the documentation accordingly. */ +/* + * IMPORTANT: + * + * This class documented "by hand" (i.e., not using Doxygen) in: + * + * libmaple/docs/source/lang/api/serial.rst + * + * If you alter the public HardwareSerial interface, you MUST update + * the documentation accordingly. + */ class HardwareSerial : public Print { - private: - uint8 usart_num; - uint32 max_baud; - gpio_dev *gpio_device; - uint8 tx_pin; - uint8 rx_pin; - timer_dev *timer_device; - uint8 channel_num; - public: - HardwareSerial(uint8 usart_num, - uint32 max_baud, - gpio_dev *gpio_device, +public: + HardwareSerial(usart_dev *usart_device, uint8 tx_pin, - uint8 rx_pin, - timer_dev *timer_device, - uint8 channel_num); + uint8 rx_pin); void begin(uint32 baud); void end(void); uint32 available(void); @@ -68,10 +58,18 @@ class HardwareSerial : public Print { void flush(void); virtual void write(unsigned char); using Print::write; +private: + usart_dev *usart_device; + uint8 tx_pin; + uint8 rx_pin; }; + extern HardwareSerial Serial1; extern HardwareSerial Serial2; extern HardwareSerial Serial3; -// TODO: high density device ports +#if defined(STM32_HIGH_DENSITY) && !defined(BOARD_maple_RET6) +extern HardwareSerial Serial4; +extern HardwareSerial Serial5; #endif +#endif |