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 */ diff --git a/drivers/include/periph/uart.h b/drivers/include/periph/uart.h index 9150aa2e1938afe341ca4aa3ce77abd4ddc091e5..983ccd201cd1f15af0be5b0974081f272c98bbe0 100644 --- a/drivers/include/periph/uart.h +++ b/drivers/include/periph/uart.h @@ -111,6 +111,41 @@ enum { UART_NOMODE = -4 /**< given mode is not applicable */ }; +/** + * @brief Definition of possible parity modes + */ +#ifndef HAVE_UART_PARITY_T +typedef enum { + UART_PARITY_NONE, /**< no parity */ + UART_PARITY_EVEN, /**< even parity */ + UART_PARITY_ODD, /**< odd parity */ + UART_PARITY_MARK, /**< mark parity */ + UART_PARITY_SPACE /**< space parity */ +} uart_parity_t; +#endif + +/** + * @brief Definition of possible data bits lengths in a UART frame + */ +#ifndef HAVE_UART_DATA_BITS_T +typedef enum { + UART_DATA_BITS_5, /**< 5 data bits */ + UART_DATA_BITS_6, /**< 6 data bits */ + UART_DATA_BITS_7, /**< 7 data bits */ + UART_DATA_BITS_8, /**< 8 data bits */ +} uart_data_bits_t; +#endif + +/** + * @brief Definition of possible stop bits lengths in a UART frame + */ +#ifndef HAVE_UART_STOP_BITS_T +typedef enum { + UART_STOP_BITS_1, /**< 1 stop bit */ + UART_STOP_BITS_2, /**< 2 stop bits */ +} uart_stop_bits_t; +#endif + /** * @brief Initialize a given UART device * @@ -137,6 +172,20 @@ enum { */ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg); +/** + * @brief Setup parity, data and stop bits for a given UART device + * + * @param[in] uart UART device to initialize + * @param[in] data_bits number of data bits in a UART frame + * @param[in] parity parity mode + * @param[in] stop_bits number of stop bits in a UART frame + * + * @return UART_OK on success + * @return UART_NOMODE on other errors + */ +int uart_mode(uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity, + uart_stop_bits_t stop_bits); + /** * @brief Write data from the given buffer to the specified UART device * diff --git a/tests/periph_uart/Makefile b/tests/periph_uart/Makefile index 73e6acdbafab499dfc67ae2de720e62d524a2190..5ee830f3b0a6dfb90a700abfe863d0a04ea2a242 100644 --- a/tests/periph_uart/Makefile +++ b/tests/periph_uart/Makefile @@ -2,8 +2,9 @@ include ../Makefile.tests_common BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-uno nucleo-f031k6 -FEATURES_REQUIRED = periph_uart -FEATURES_OPTIONAL = periph_lpuart # STM32 L0 and L4 provides lpuart support +FEATURES_REQUIRED += periph_uart +FEATURES_OPTIONAL += periph_lpuart # STM32 L0 and L4 provides lpuart support +FEATURES_OPTIONAL += periph_uart_modecfg USEMODULE += shell USEMODULE += xtimer diff --git a/tests/periph_uart/main.c b/tests/periph_uart/main.c index 850f6962d5e68331dfad4017f35085287d1eb3f0..0a2333365fe1fd424b6391d57d0aba242ceedbf1 100644 --- a/tests/periph_uart/main.c +++ b/tests/periph_uart/main.c @@ -53,6 +53,15 @@ static uart_ctx_t ctx[UART_NUMOF]; static kernel_pid_t printer_pid; static char printer_stack[THREAD_STACKSIZE_MAIN]; +#ifdef MODULE_PERIPH_UART_MODECFG +static uart_data_bits_t data_bits_lut[] = { UART_DATA_BITS_5, UART_DATA_BITS_6, + UART_DATA_BITS_7, UART_DATA_BITS_8 }; +static int data_bits_lut_len = sizeof(data_bits_lut)/sizeof(data_bits_lut[0]); + +static uart_stop_bits_t stop_bits_lut[] = { UART_STOP_BITS_1, UART_STOP_BITS_2 }; +static int stop_bits_lut_len = sizeof(stop_bits_lut)/sizeof(stop_bits_lut[0]); +#endif + static int parse_dev(char *arg) { unsigned dev = atoi(arg); @@ -142,7 +151,7 @@ static int cmd_init(int argc, char **argv) return 1; } else if (res != UART_OK) { - puts("Error: Unable to initialize UART device\n"); + puts("Error: Unable to initialize UART device"); return 1; } printf("Success: Initialized UART_DEV(%i) at BAUD %"PRIu32"\n", dev, baud); @@ -154,6 +163,74 @@ static int cmd_init(int argc, char **argv) return 0; } +#ifdef MODULE_PERIPH_UART_MODECFG +static int cmd_mode(int argc, char **argv) +{ + int dev, data_bits_arg, stop_bits_arg; + uart_data_bits_t data_bits; + uart_parity_t parity; + uart_stop_bits_t stop_bits; + + if (argc < 5) { + printf("usage: %s <dev> <data bits> <parity> <stop bits>\n", argv[0]); + return 1; + } + + dev = parse_dev(argv[1]); + if (dev < 0) { + return 1; + } + + data_bits_arg = atoi(argv[2]) - 5; + if (data_bits_arg >= 0 && data_bits_arg < data_bits_lut_len) { + data_bits = data_bits_lut[data_bits_arg]; + } + else { + printf("Error: Invalid number of data_bits (%i).\n", data_bits_arg + 5); + return 1; + } + + argv[3][0] &= ~0x20; + switch (argv[3][0]) { + case 'N': + parity = UART_PARITY_NONE; + break; + case 'E': + parity = UART_PARITY_EVEN; + break; + case 'O': + parity = UART_PARITY_ODD; + break; + case 'M': + parity = UART_PARITY_MARK; + break; + case 'S': + parity = UART_PARITY_SPACE; + break; + default: + printf("Error: Invalid parity (%c).\n", argv[3][0]); + return 1; + } + + stop_bits_arg = atoi(argv[4]) - 1; + if (stop_bits_arg >= 0 && stop_bits_arg < stop_bits_lut_len) { + stop_bits = stop_bits_lut[stop_bits_arg]; + } + else { + printf("Error: Invalid number of stop bits (%i).\n", stop_bits_arg + 1); + return 1; + } + + if (uart_mode(UART_DEV(dev), data_bits, parity, stop_bits) != UART_OK) { + puts("Error: Unable to apply UART settings"); + return 1; + } + printf("Success: Successfully applied UART_DEV(%i) settings\n", dev); + + return 0; +} +#endif /* MODULE_PERIPH_UART_MODECFG */ + static int cmd_send(int argc, char **argv) { int dev; @@ -177,6 +254,9 @@ static int cmd_send(int argc, char **argv) static const shell_command_t shell_commands[] = { { "init", "Initialize a UART device with a given baudrate", cmd_init }, +#ifdef MODULE_PERIPH_UART_MODECFG + { "mode", "Setup data bits, stop bits and parity for a given UART device", cmd_mode }, +#endif { "send", "Send a string through given UART device", cmd_send }, { NULL, NULL, NULL } }; diff --git a/tests/periph_uart/tests/periph_uart_if.py b/tests/periph_uart/tests/periph_uart_if.py index 213a3da7c183123024bb0841fafe96f612c93c51..6f1544b12c2637a2a9a339db06397bdcee28ab44 100644 --- a/tests/periph_uart/tests/periph_uart_if.py +++ b/tests/periph_uart/tests/periph_uart_if.py @@ -19,6 +19,11 @@ class PeriphUartIf(DutShell): """Initialize DUT's UART.""" return self.send_cmd("init {} {}".format(dev, baud)) + def uart_mode(self, dev, data_bits, parity, stop_bits): + """Setup databits, parity and stopbits.""" + return self.send_cmd( + "mode {} {} {} {}".format(dev, data_bits, parity, stop_bits)) + def uart_send_string(self, dev, test_string): """Send data via DUT's UART.""" return self.send_cmd("send {} {}".format(dev, test_string)) diff --git a/tests/periph_uart/tests/test.py b/tests/periph_uart/tests/test.py index dc6181a188e2c370c378013d15e5ac42f2643905..c5b7caca480209fa70faeecd9025e23fb44e380b 100644 --- a/tests/periph_uart/tests/test.py +++ b/tests/periph_uart/tests/test.py @@ -17,7 +17,7 @@ import random import string from periph_uart_if import PeriphUartIf -from if_lib import PhilipIf +from riot_pal import LLMemMapIf, PHILIP_MEM_MAP_PATH def kwexpect(val1, val2, level="WARN"): @@ -120,7 +120,7 @@ def setup_test(bpt, t): bpt.reset_mcu() t.manual_test("Reset BPT") - bpt.set_uart_baud(115200) + bpt.write_reg('uart.baud', 115200) bpt.execute_changes() t.manual_test("BPT: setup default baudrate") @@ -154,6 +154,119 @@ def echo_test(bpt, uart, dut_uart, support_reset=False): return t +def even_parity_8_bits_test(bpt, uart, dut_uart, support_reset=False): + cmd_log = list() + t = Test('even parity and 8 bits test', + 'Tests DUT receive/transmit functionality with enabled even parity and 8 bits', + cmd_log) + + setup_test(bpt, t) + + bpt.write_reg('uart.ctrl.parity', 1) + bpt.execute_changes() + + t.run_test(uart.uart_init(dut_uart, 115200), "Success") + t.run_test(uart.uart_mode(dut_uart, 8, "E", 1), "Success") + t.run_test(uart.uart_send_string(dut_uart, "t111"), "Success", ["t111"]) + + t.run_test(uart.uart_init(dut_uart, 115200), "Success") + t.run_test(uart.uart_mode(dut_uart, 8, "O", 1), "Success") + t.run_test(uart.uart_send_string(dut_uart, "t111"), "Timeout") + + return t + + +def odd_parity_8_bits_test(bpt, uart, dut_uart, support_reset=False): + cmd_log = list() + t = Test('odd parity and 8 bits test', + 'Tests DUT receive/transmit functionality with enabled odd parity and 8 bits', + cmd_log) + + setup_test(bpt, t) + + bpt.write_reg('uart.ctrl.parity', 2) + bpt.execute_changes() + + t.run_test(uart.uart_init(dut_uart, 115200), "Success") + t.run_test(uart.uart_mode(dut_uart, 8, "O", 1), "Success") + t.run_test(uart.uart_send_string(dut_uart, "t111"), "Success", ["t111"]) + + t.run_test(uart.uart_init(dut_uart, 115200), "Success") + t.run_test(uart.uart_mode(dut_uart, 8, "E", 1), "Success") + t.run_test(uart.uart_send_string(dut_uart, "t111"), "Timeout") + + return t + + +def even_parity_7_bits_test(bpt, uart, dut_uart, support_reset=False): + cmd_log = list() + t = Test('even parity and 7 bits test', + 'Tests DUT receive/transmit functionality with enabled even parity and 7 bits', + cmd_log) + + setup_test(bpt, t) + + bpt.write_reg('uart.ctrl.parity', 1) + bpt.write_reg('uart.ctrl.data_bits', 1) + bpt.execute_changes() + + t.run_test(uart.uart_init(dut_uart, 115200), "Success") + t.run_test(uart.uart_mode(dut_uart, 7, "E", 1), "Success") + t.run_test(uart.uart_send_string(dut_uart, "t111"), "Success", ["t111"]) + + t.run_test(uart.uart_init(dut_uart, 115200), "Success") + t.run_test(uart.uart_mode(dut_uart, 7, "O", 1), "Success") + t.run_test(uart.uart_send_string(dut_uart, "t111"), "Timeout") + + return t + + +def odd_parity_7_bits_test(bpt, uart, dut_uart, support_reset=False): + cmd_log = list() + t = Test('odd parity and 7 bits test', + 'Tests DUT receive/transmit functionality with enabled odd parity and 7 bits', + cmd_log) + + setup_test(bpt, t) + + bpt.write_reg('uart.ctrl.parity', 2) + bpt.write_reg('uart.ctrl.data_bits', 1) + bpt.execute_changes() + + t.run_test(uart.uart_init(dut_uart, 115200), "Success") + t.run_test(uart.uart_mode(dut_uart, 7, "O", 1), "Success") + t.run_test(uart.uart_send_string(dut_uart, "t111"), "Success", ["t111"]) + + t.run_test(uart.uart_init(dut_uart, 115200), "Success") + t.run_test(uart.uart_mode(dut_uart, 7, "E", 1), "Success") + t.run_test(uart.uart_send_string(dut_uart, "t111"), "Timeout") + + return t + + +def two_stop_bits_test(bpt, uart, dut_uart, support_reset=False): + cmd_log = list() + t = Test('two stop bits test', + 'Tests DUT receive/transmit functionality with two stop bits', + cmd_log) + + setup_test(bpt, t) + + bpt.write_reg('uart.ctrl.parity', 2) + bpt.write_reg('uart.ctrl.data_bits', 0) + bpt.execute_changes() + + t.run_test(uart.uart_init(dut_uart, 115200), "Success") + t.run_test(uart.uart_mode(dut_uart, 8, "N", 2), "Success") + t.run_test(uart.uart_send_string(dut_uart, "tttt"), "Success", ["tttt"]) + + t.run_test(uart.uart_init(dut_uart, 115200), "Success") + t.run_test(uart.uart_mode(dut_uart, 8, "N", 1), "Success") + t.run_test(uart.uart_send_string(dut_uart, "tttt"), "Timeout") + + return t + + def echo_ext_test(bpt, uart, dut_uart, support_reset=False): cmd_log = list() t = Test('echo ext test', @@ -162,7 +275,7 @@ def echo_ext_test(bpt, uart, dut_uart, support_reset=False): setup_test(bpt, t) - bpt.set_uart_mode(1) + bpt.write_reg('uart.mode', 1) bpt.execute_changes() t.manual_test("BPT: set echo ext mode") @@ -182,13 +295,15 @@ def register_read_test(bpt, uart, dut_uart, support_reset=False): setup_test(bpt, t) - bpt.set_uart_mode(2) + bpt.write_reg('uart.mode', 2) bpt.execute_changes() t.manual_test("BPT: set echo ext mode") t.run_test(uart.uart_init(dut_uart, 115200), "Success") t.run_test(uart.uart_send_string( - dut_uart, "\"rr 152 10\""), "Success", ["0,0x09080706050403020100"]) + dut_uart, "\"wr 1 10 1\""), "Success") + t.run_test(uart.uart_send_string( + dut_uart, "\"rr 1 1\""), "Success", ["0,0x0A"]) t.run_test(uart.uart_send_string( dut_uart, "\"rr -1 10\""), "Success", [errno.EINVAL]) @@ -250,13 +365,23 @@ def main(): raise ValueError('Invalid log level: %s' % loglevel) logging.basicConfig(level=loglevel) - bpt = PhilipIf(port=args.bpt_port) + bpt = LLMemMapIf(PHILIP_MEM_MAP_PATH, 'serial', args.bpt_port) uart = PeriphUartIf(port=args.dut_port) print('Starting Test periph_uart') test_list = list() test_list.append(echo_test(bpt, uart, args.dut_uart)) print_full_result(test_list[-1]) + test_list.append(even_parity_8_bits_test(bpt, uart, args.dut_uart)) + print_full_result(test_list[-1]) + test_list.append(odd_parity_8_bits_test(bpt, uart, args.dut_uart)) + print_full_result(test_list[-1]) + test_list.append(even_parity_7_bits_test(bpt, uart, args.dut_uart)) + print_full_result(test_list[-1]) + test_list.append(odd_parity_7_bits_test(bpt, uart, args.dut_uart)) + print_full_result(test_list[-1]) + test_list.append(two_stop_bits_test(bpt, uart, args.dut_uart)) + print_full_result(test_list[-1]) test_list.append(echo_ext_test(bpt, uart, args.dut_uart)) print_full_result(test_list[-1]) test_list.append(register_read_test(bpt, uart, args.dut_uart))