From 60f745a0334ba40000d0b4c03255fabde510e56e Mon Sep 17 00:00:00 2001 From: Yegor Yefremov <yegorslists@googlemail.com> Date: Tue, 8 Jan 2019 11:10:49 +0100 Subject: [PATCH] cpu/stm32_common: add support for uart_mode routine Add support for specifying data bits, stop bits and parity at runtime. Introduce feature periph_uart_modecfg for uart_mode() till all other CPUs implement it. STM32 L1, F1, F2, F4 supports following modes: * 7E1, 7E2 * 7O1, 7O2 * 8N1, 8N2 * 8E1, 8E2 * 8O1, 8O2 STM32 L0, L4, F0, F3, F7 supports following modes: * 6E1, 6E2 * 6O1, 6O2 * 7E1, 7E2 * 7O1, 7O2 * 7N1, 7N2 * 8N1, 8N2 * 8E1, 8E2 * 8O1, 8O2 Use USART_CR1_M1 macro to detect 7-bit support because even inside one family there could be devices that don't support 7-bit mode. So just using a family macro is not enough. As stated in the datasheets for L0, L4, F0, F3, F7 devices, data bits can only be changed when UART is disabled (UE=0). Introduce uart_stop() routine to satisfy this requirement. STM32 UART adds parity to the MSB of a byte to send. The same also applies to the received bytes. As a result this bit must be masked in order to get the pure data. Signed-off-by: Yegor Yefremov <yegorslists@googlemail.com> --- cpu/stm32_common/Makefile.features | 1 + cpu/stm32_common/include/periph_cpu_common.h | 50 +++++++++++++++ cpu/stm32_common/periph/uart.c | 67 ++++++++++++++++++-- 3 files changed, 113 insertions(+), 5 deletions(-) diff --git a/cpu/stm32_common/Makefile.features b/cpu/stm32_common/Makefile.features index 13c07298d7..f54d184a87 100644 --- a/cpu/stm32_common/Makefile.features +++ b/cpu/stm32_common/Makefile.features @@ -1,6 +1,7 @@ FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_gpio periph_gpio_irq FEATURES_PROVIDED += puf_sram +FEATURES_PROVIDED += periph_uart_modecfg ifneq (,$(filter $(BOARDS_WITHOUT_HWRNG),$(BOARD))) FEATURES_PROVIDED := $(filter-out periph_hwrng,$(FEATURES_PROVIDED)) diff --git a/cpu/stm32_common/include/periph_cpu_common.h b/cpu/stm32_common/include/periph_cpu_common.h index b1f83a032f..fd97fb25a2 100644 --- a/cpu/stm32_common/include/periph_cpu_common.h +++ b/cpu/stm32_common/include/periph_cpu_common.h @@ -370,6 +370,56 @@ typedef enum { STM32_LPUART, /**< STM32 Low-power UART (LPUART) module type */ } uart_type_t; +/** + * @brief Invalid UART mode mask + * + * This mask is also used to force data_bits_t to be uint32_t type + * since it may be assigned a uint32_t variable in uart_mode + */ +#define UART_INVALID_MODE (0x8000000) + +/** + * @brief Override parity values + * @{ + */ +#define HAVE_UART_PARITY_T +typedef enum { + UART_PARITY_NONE = 0, /**< no parity */ + UART_PARITY_EVEN = USART_CR1_PCE, /**< even parity */ + UART_PARITY_ODD = (USART_CR1_PCE | USART_CR1_PS), /**< odd parity */ + UART_PARITY_MARK = UART_INVALID_MODE | 4, /**< not supported */ + UART_PARITY_SPACE = UART_INVALID_MODE | 5 /**< not supported */ +} uart_parity_t; +/** @} */ + +/** + * @brief Override data bits length values + * @{ + */ +#define HAVE_UART_DATA_BITS_T +typedef enum { + UART_DATA_BITS_5 = UART_INVALID_MODE | 1, /**< not supported */ + UART_DATA_BITS_6 = UART_INVALID_MODE | 2, /**< not supported unless parity is set */ +#if defined(USART_CR1_M1) + UART_DATA_BITS_7 = USART_CR1_M1, /**< 7 data bits */ +#else + UART_DATA_BITS_7 = UART_INVALID_MODE | 3, /**< not supported unless parity is set */ +#endif + UART_DATA_BITS_8 = 0, /**< 8 data bits */ +} uart_data_bits_t; +/** @} */ + +/** + * @brief Override stop bits length values + * @{ + */ +#define HAVE_UART_STOP_BITS_T +typedef enum { + UART_STOP_BITS_1 = 0, /**< 1 stop bit */ + UART_STOP_BITS_2 = USART_CR2_STOP_1, /**< 2 stop bits */ +} uart_stop_bits_t; +/** @} */ + /** * @brief Structure for UART configuration data */ diff --git a/cpu/stm32_common/periph/uart.c b/cpu/stm32_common/periph/uart.c index 1358300517..7a932b088c 100644 --- a/cpu/stm32_common/periph/uart.c +++ b/cpu/stm32_common/periph/uart.c @@ -53,8 +53,15 @@ /** * @brief Allocate memory to store the callback functions + * + * Extend standard uart_isr_ctx_t with data_mask field. This is needed + * in order to mask parity bit. */ -static uart_isr_ctx_t isr_ctx[UART_NUMOF]; +static struct { + uart_rx_cb_t rx_cb; /**< data received interrupt callback */ + void *arg; /**< argument to both callback routines */ + uint8_t data_mask; /**< mask applied to the data register */ +} isr_ctx[UART_NUMOF]; static inline USART_TypeDef *dev(uart_t uart) { @@ -105,8 +112,9 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) assert(uart < UART_NUMOF); /* save ISR context */ - isr_ctx[uart].rx_cb = rx_cb; - isr_ctx[uart].arg = arg; + isr_ctx[uart].rx_cb = rx_cb; + isr_ctx[uart].arg = arg; + isr_ctx[uart].data_mask = 0xFF; uart_init_pins(uart, rx_cb); @@ -154,6 +162,53 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) return UART_OK; } +#ifdef MODULE_PERIPH_UART_MODECFG +int uart_mode(uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity, + uart_stop_bits_t stop_bits) +{ + assert(uart < UART_NUMOF); + + isr_ctx[uart].data_mask = 0xFF; + + if (parity) { + switch (data_bits) { + case UART_DATA_BITS_6: + data_bits = UART_DATA_BITS_7; + isr_ctx[uart].data_mask = 0x3F; + break; + case UART_DATA_BITS_7: + data_bits = UART_DATA_BITS_8; + isr_ctx[uart].data_mask = 0x7F; + break; + case UART_DATA_BITS_8: + data_bits = USART_CR1_M; + break; + default: + return UART_NOMODE; + } + } + if ((data_bits & UART_INVALID_MODE) || (parity & UART_INVALID_MODE)) { + return UART_NOMODE; + } + +#ifdef USART_CR1_M1 + if (!(dev(uart)->ISR & USART_ISR_TC)) { + return UART_INTERR; + } + dev(uart)->CR1 &= ~(USART_CR1_UE | USART_CR1_TE); + dev(uart)->CR1 &= ~USART_CR1_M1; +#endif + + dev(uart)->CR2 &= ~USART_CR2_STOP; + dev(uart)->CR1 &= ~(USART_CR1_PS | USART_CR1_PCE | USART_CR1_M); + + dev(uart)->CR2 |= stop_bits; + dev(uart)->CR1 |= (USART_CR1_UE | USART_CR1_TE | data_bits | parity); + + return UART_OK; +} +#endif /* MODULE_PERIPH_UART_MODECFG */ + static inline void uart_init_usart(uart_t uart, uint32_t baudrate) { uint16_t mantissa; @@ -301,7 +356,8 @@ static inline void irq_handler(uart_t uart) uint32_t status = dev(uart)->ISR; if (status & USART_ISR_RXNE) { - isr_ctx[uart].rx_cb(isr_ctx[uart].arg, (uint8_t)dev(uart)->RDR); + isr_ctx[uart].rx_cb(isr_ctx[uart].arg, + (uint8_t)dev(uart)->RDR & isr_ctx[uart].data_mask); } if (status & USART_ISR_ORE) { dev(uart)->ICR |= USART_ICR_ORECF; /* simply clear flag on overrun */ @@ -312,7 +368,8 @@ static inline void irq_handler(uart_t uart) uint32_t status = dev(uart)->SR; if (status & USART_SR_RXNE) { - isr_ctx[uart].rx_cb(isr_ctx[uart].arg, (uint8_t)dev(uart)->DR); + isr_ctx[uart].rx_cb(isr_ctx[uart].arg, + (uint8_t)dev(uart)->DR & isr_ctx[uart].data_mask); } if (status & USART_SR_ORE) { /* ORE is cleared by reading SR and DR sequentially */ -- GitLab