aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/source/lang/api/serial.rst5
-rw-r--r--examples/test-serial-flush.cpp29
-rw-r--r--examples/test-session.cpp62
-rw-r--r--libmaple/usart.c302
-rw-r--r--libmaple/usart.h315
-rw-r--r--libmaple/util.c53
-rw-r--r--wirish/comm/HardwareSerial.cpp86
-rw-r--r--wirish/comm/HardwareSerial.h50
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