diff --git a/cpu/cc26x0/Makefile.features b/cpu/cc26x0/Makefile.features index c7af1adde5..a797d3ba77 100644 --- a/cpu/cc26x0/Makefile.features +++ b/cpu/cc26x0/Makefile.features @@ -1,3 +1,5 @@ FEATURES_PROVIDED += periph_cpuid +FEATURES_PROVIDED += periph_uart +FEATURES_PROVIDED += periph_uart_modecfg -include $(RIOTCPU)/cortexm_common/Makefile.features diff --git a/cpu/cc26x0/include/cc26x0_uart.h b/cpu/cc26x0/include/cc26x0_uart.h index 12cc17df6a..9627c36091 100644 --- a/cpu/cc26x0/include/cc26x0_uart.h +++ b/cpu/cc26x0/include/cc26x0_uart.h @@ -72,9 +72,8 @@ typedef struct { #define UART_FR_RXFF 0x40 #define UART_FR_TXFE 0x80 -#define UART_LCRH_PEN 0x1 -#define UART_LCRH_EPS 0x2 -#define UART_LCRH_RXFE 0x4 +#define UART_LCRH_PEN 0x2 +#define UART_LCRH_EPS 0x4 #define UART_LCRH_STP2 0x8 #define UART_LCRH_FEN 0x10 #define UART_LCRH_WLEN_mask 0x60 diff --git a/cpu/cc26x0/include/periph_cpu.h b/cpu/cc26x0/include/periph_cpu.h index aba8367649..aca737c516 100644 --- a/cpu/cc26x0/include/periph_cpu.h +++ b/cpu/cc26x0/include/periph_cpu.h @@ -65,6 +65,53 @@ typedef enum { GPIO_BOTH = IOCFG_EDGEDET_BOTH } gpio_flank_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, + UART_PARITY_EVEN = (UART_LCRH_PEN | UART_LCRH_EPS), + UART_PARITY_ODD = UART_LCRH_PEN, + UART_PARITY_MARK = UART_INVALID_MODE | 4, + UART_PARITY_SPACE = UART_INVALID_MODE | 5 +} uart_parity_t; +/** @} */ + + /** + * @brief Override data bits length values + * @{ + */ +#define HAVE_UART_DATA_BITS_T +typedef enum { + UART_DATA_BITS_5 = UART_LCRH_WLEN_5, + UART_DATA_BITS_6 = UART_LCRH_WLEN_6, + UART_DATA_BITS_7 = UART_LCRH_WLEN_7, + UART_DATA_BITS_8 = UART_LCRH_WLEN_8 +} uart_data_bits_t; +/** @} */ + +/** + * @brief Override stop bits length values + * @{ + */ +#define HAVE_UART_STOP_BITS_T +typedef enum { + UART_STOP_BITS_1 = 0, + UART_STOP_BITS_2 = UART_LCRH_STP2, +} uart_stop_bits_t; +/** @} */ + + /** * @brief Configuration of low-level general purpose timers * diff --git a/cpu/cc26x0/periph/uart.c b/cpu/cc26x0/periph/uart.c index ce35df71f3..b30a41dfc9 100644 --- a/cpu/cc26x0/periph/uart.c +++ b/cpu/cc26x0/periph/uart.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "periph/uart.h" +#include "periph_conf.h" /** * @brief Bit mask for the fractional part of the baudrate @@ -91,6 +92,47 @@ 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(data_bits == UART_DATA_BITS_5 || + data_bits == UART_DATA_BITS_6 || + data_bits == UART_DATA_BITS_7 || + data_bits == UART_DATA_BITS_8); + + assert(parity == UART_PARITY_NONE || + parity == UART_PARITY_EVEN || + parity == UART_PARITY_ODD || + parity == UART_PARITY_MARK || + parity == UART_PARITY_SPACE); + + assert(stop_bits == UART_STOP_BITS_1 || + stop_bits == UART_STOP_BITS_2); + + /* make sure the uart device is valid */ + if (uart != 0) { + return UART_NODEV; + } + + /* cc26x0 does not support mark or space parity */ + if (parity == UART_PARITY_MARK || parity == UART_PARITY_SPACE) { + return UART_NOMODE; + } + + /* Disable UART and clear old settings */ + UART->CTL = 0; + UART->LCRH = 0; + + /* Apply setting and enable UART */ + UART->LCRH = data_bits | parity | stop_bits; + UART->CTL = ENABLE_MASK; + + return UART_OK; +} +#endif + void uart_write(uart_t uart, const uint8_t *data, size_t len) { (void) uart; diff --git a/tests/periph_uart_mode/Makefile b/tests/periph_uart_mode/Makefile new file mode 100644 index 0000000000..2bd7b64346 --- /dev/null +++ b/tests/periph_uart_mode/Makefile @@ -0,0 +1,14 @@ +include ../Makefile.tests_common + +BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-leonardo arduino-nano\ + arduino-uno nucleo-f031k6 + +FEATURES_REQUIRED += periph_uart +FEATURES_REQUIRED += periph_uart_modecfg + +USEMODULE += xtimer + +# Set this to prevent welcome message from printing and confusing output +CFLAGS+=-DLOG_LEVEL=LOG_NONE + +include $(RIOTBASE)/Makefile.include diff --git a/tests/periph_uart_mode/README.md b/tests/periph_uart_mode/README.md new file mode 100644 index 0000000000..f5f387184a --- /dev/null +++ b/tests/periph_uart_mode/README.md @@ -0,0 +1,22 @@ +Expected result +=============== + +Use a probe to examine the output from the UART. It tests all +permutations of data-bits, parity, and stop-bits. For each mode, the mode +configuration will be printed to STDOUT as: + +For example: 7 data-bits, even parity, and 2 stop-bits would be: 7E2 +Only supported mode strings will be printed out. At the end of the +test, the default mode will be restored, and a list indicating which +modes were supported and unsupported by the device. The scope still +needs to be used to validate that the settings were applied properly. +If a different BAUD rate than 115200 is desired, compile with: +`CFLAGS+=-DSTDIO_UART_BAUDRATE=` + +Background +========== + +This test was created because the existing periph_uart test relies on the +presence of multiple UARTs, so that one can be used for the shell, and another +for testing. This test requires no shell hookup, and automatically runs through +the test procedure, relying on a probe to examine the results. \ No newline at end of file diff --git a/tests/periph_uart_mode/main.c b/tests/periph_uart_mode/main.c new file mode 100644 index 0000000000..d15df67ba0 --- /dev/null +++ b/tests/periph_uart_mode/main.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2018 Sparkmeter + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Manual test application for UART mode on CPUs with only one UART + * + * @author Ben Postman + * + * @} + */ + +#include +#include +#include + +#include "periph/uart.h" +#include "stdio_uart.h" +#include "xtimer.h" + +/* Number of different options for each mode parameter */ +#define DATA_BIT_OPTIONS (4) +#define PARITY_OPTIONS (5) +#define STOP_BIT_OPTIONS (2) +#define TOTAL_OPTIONS (DATA_BIT_OPTIONS * PARITY_OPTIONS * STOP_BIT_OPTIONS) + +/* Character positions in the mode string */ +#define DATA_BIT_POS (0) +#define PARITY_POS (1) +#define STOP_BIT_POS (2) + +/* Length of the printable mode string, includes null terminator */ +#define MODE_STR_LEN (4) + +/* UART_DEV is always 0 since this test is for device with 1 UART */ +#define _UART_DEV (UART_DEV(0)) + +/* Delay between each mode, makes parsing results easier */ +#ifndef DELAY_US +#define DELAY_US 50000 +#endif + + +/* Stores each mode string for printing at the end of the test */ +static char mode_strings[TOTAL_OPTIONS][MODE_STR_LEN]; + + +static void _get_mode(const uart_data_bits_t data_bits, + const uart_parity_t parity, const uart_stop_bits_t stop_bits, char* mode_str) { + switch (data_bits) { + case UART_DATA_BITS_5: + mode_str[DATA_BIT_POS] = '5'; + break; + case UART_DATA_BITS_6: + mode_str[DATA_BIT_POS] = '6'; + break; + case UART_DATA_BITS_7: + mode_str[DATA_BIT_POS] = '7'; + break; + case UART_DATA_BITS_8: + mode_str[DATA_BIT_POS] = '8'; + break; + default: + break; + } + + switch (parity) { + case UART_PARITY_NONE: + mode_str[PARITY_POS] = 'N'; + break; + case UART_PARITY_EVEN: + mode_str[PARITY_POS] = 'E'; + break; + case UART_PARITY_ODD: + mode_str[PARITY_POS] = 'O'; + break; + case UART_PARITY_MARK: + mode_str[PARITY_POS] = 'M'; + break; + case UART_PARITY_SPACE: + mode_str[PARITY_POS] = 'S'; + break; + default: + break; + } + + switch (stop_bits) { + case UART_STOP_BITS_1: + mode_str[STOP_BIT_POS] = '1'; + break; + case UART_STOP_BITS_2: + mode_str[STOP_BIT_POS] = '2'; + break; + default: + break; + } + + mode_str[MODE_STR_LEN - 1] = '\0'; +} + + +int main(void) +{ + int8_t status; + + /* Stores the result for each mode */ + bool results[TOTAL_OPTIONS]; + + /* Results index that is used to associate mode_strings and results */ + uint8_t ridx = 0; + + /* Use arrays so that each option can be easily iterated over */ + const uart_data_bits_t data_bits[DATA_BIT_OPTIONS] = { + UART_DATA_BITS_5, + UART_DATA_BITS_6, + UART_DATA_BITS_7, + UART_DATA_BITS_8 + }; + + const uart_parity_t parity[PARITY_OPTIONS] = { + UART_PARITY_NONE, + UART_PARITY_EVEN, + UART_PARITY_ODD, + UART_PARITY_MARK, + UART_PARITY_SPACE + }; + + const uart_stop_bits_t stop_bits[STOP_BIT_OPTIONS] = { + UART_STOP_BITS_1, + UART_STOP_BITS_2 + }; + + /* Test each permutation */ + for (int didx = 0; didx < 4; ++didx) { + for (int pidx = 0; pidx < 5; ++pidx) { + for (int sidx = 0; sidx < 2; ++sidx) { + /* Initialize stdio to get printf */ + stdio_init(); + + status = uart_mode(_UART_DEV, data_bits[didx], + parity[pidx], stop_bits[sidx]); + _get_mode(data_bits[didx], parity[pidx], + stop_bits[sidx], mode_strings[ridx]); + + if (status == UART_OK) { + results[ridx] = true; + printf("%s\n", mode_strings[ridx]); + xtimer_usleep(DELAY_US); + } + else { + results[ridx] = false; + } + + ridx++; + } + } + } + + /* Reset mode and print results */ + stdio_init(); + uart_mode(_UART_DEV, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1); + for (int i = 0; i < TOTAL_OPTIONS; ++i) { + if (results[i] == true) { + printf("%s: Supported\n", mode_strings[i]); + } + else { + printf("%s: Unsupported\n", mode_strings[i]); + } + } + + return 0; +}