diff --git a/cpu/stm32_common/Makefile.features b/cpu/stm32_common/Makefile.features index 13c07298d718c6cd4de6ec6b068ea1397c437db7..f54d184a87ca0feda3d027924b8116dc89422e3c 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 b1f83a032f1e7fc026b7f2d659f06f234812e405..fd97fb25a2735b977c13baa1391235305d41aa37 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 135830051791c90c541487b701c6927fae039094..7a932b088cc413ca97bd64d1ad5c3a9e590987b8 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 */