diff --git a/Makefile.dep b/Makefile.dep index 5e74feb159..fdcb2e3706 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -976,6 +976,11 @@ ifneq (,$(filter suit_%,$(USEMODULE))) USEMODULE += suit endif +# Enable periph_uart when periph_uart_nonblocking is enabled +ifneq (,$(filter periph_uart_nonblocking,$(USEMODULE))) + FEATURES_REQUIRED += periph_uart +endif + # Enable periph_gpio when periph_gpio_irq is enabled ifneq (,$(filter periph_gpio_irq,$(USEMODULE))) FEATURES_REQUIRED += periph_gpio diff --git a/boards/common/saml1x/include/periph_conf.h b/boards/common/saml1x/include/periph_conf.h index b2014a639a..3baf0e6bb9 100644 --- a/boards/common/saml1x/include/periph_conf.h +++ b/boards/common/saml1x/include/periph_conf.h @@ -73,6 +73,7 @@ static const uart_conf_t uart_config[] = { /* interrupt function name mapping */ #define UART_0_ISR isr_sercom2_2 +#define UART_0_ISR_TX isr_sercom2_0 #define UART_NUMOF ARRAY_SIZE(uart_config) /** @} */ diff --git a/boards/same54-xpro/include/periph_conf.h b/boards/same54-xpro/include/periph_conf.h index 80dffffbd2..b4125c6c93 100644 --- a/boards/same54-xpro/include/periph_conf.h +++ b/boards/same54-xpro/include/periph_conf.h @@ -88,6 +88,7 @@ static const uart_conf_t uart_config[] = { /* interrupt function name mapping */ #define UART_0_ISR isr_sercom2_2 +#define UART_0_ISR_TX isr_sercom2_0 #define UART_NUMOF ARRAY_SIZE(uart_config) /** @} */ diff --git a/cpu/sam0_common/Makefile.dep b/cpu/sam0_common/Makefile.dep new file mode 100644 index 0000000000..d14f2bdb7b --- /dev/null +++ b/cpu/sam0_common/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter periph_uart_nonblocking,$(USEMODULE))) + USEMODULE += tsrb +endif diff --git a/cpu/sam0_common/Makefile.features b/cpu/sam0_common/Makefile.features index 63eb3f31ae..38f83ce8b7 100644 --- a/cpu/sam0_common/Makefile.features +++ b/cpu/sam0_common/Makefile.features @@ -4,6 +4,7 @@ FEATURES_PROVIDED += periph_flashpage_raw FEATURES_PROVIDED += periph_flashpage_rwee FEATURES_PROVIDED += periph_gpio periph_gpio_irq FEATURES_PROVIDED += periph_uart_modecfg +FEATURES_PROVIDED += periph_uart_nonblocking FEATURES_PROVIDED += periph_wdt periph_wdt_cb -include $(RIOTCPU)/cortexm_common/Makefile.features diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index 65ed47eaff..e0180acd5f 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -196,6 +196,14 @@ typedef enum { /** @} */ #endif /* ndef DOXYGEN */ + +/** + * @brief Size of the UART TX buffer for non-blocking mode. + */ +#ifndef SAM0_UART_TXBUF_SIZE +#define SAM0_UART_TXBUF_SIZE (64) +#endif + /** * @brief UART device configuration */ diff --git a/cpu/sam0_common/periph/uart.c b/cpu/sam0_common/periph/uart.c index 29833fe694..1d3ed1bb38 100644 --- a/cpu/sam0_common/periph/uart.c +++ b/cpu/sam0_common/periph/uart.c @@ -31,9 +31,18 @@ #define ENABLE_DEBUG (0) #include "debug.h" +#if defined (CPU_SAML1X) || defined (CPU_SAMD5X) +#define UART_HAS_TX_ISR +#endif + /** - * @brief Allocate memory to store the callback functions + * @brief Allocate memory to store the callback functions & buffers */ +#ifdef MODULE_PERIPH_UART_NONBLOCKING +#include "tsrb.h" +static tsrb_t uart_tx_rb[UART_NUMOF]; +static uint8_t uart_tx_rb_buf[UART_NUMOF][SAM0_UART_TXBUF_SIZE]; +#endif static uart_isr_ctx_t uart_ctx[UART_NUMOF]; /** @@ -57,6 +66,11 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) /* must disable here first to ensure idempotency */ dev(uart)->CTRLA.reg &= ~(SERCOM_USART_CTRLA_ENABLE); +#ifdef MODULE_PERIPH_UART_NONBLOCKING + /* set up the TX buffer */ + tsrb_init(&uart_tx_rb[uart], uart_tx_rb_buf[uart], SAM0_UART_TXBUF_SIZE); +#endif + /* configure pins */ if (uart_config[uart].rx_pin != GPIO_UNDEF) { gpio_init(uart_config[uart].rx_pin, GPIO_IN); @@ -99,11 +113,13 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) if ((rx_cb) && (uart_config[uart].rx_pin != GPIO_UNDEF)) { uart_ctx[uart].rx_cb = rx_cb; uart_ctx[uart].arg = arg; -#if defined (CPU_SAML1X) || defined (CPU_SAMD5X) +#ifdef UART_HAS_TX_ISR + /* enable RXNE ISR */ NVIC_EnableIRQ(SERCOM0_2_IRQn + (sercom_id(dev(uart)) * 4)); #else + /* enable UART ISR */ NVIC_EnableIRQ(SERCOM0_IRQn + sercom_id(dev(uart))); -#endif +#endif /* UART_HAS_TX_ISR */ dev(uart)->CTRLB.reg |= SERCOM_USART_CTRLB_RXEN; dev(uart)->INTENSET.reg |= SERCOM_USART_INTENSET_RXC; /* set wakeup receive from sleep if enabled */ @@ -111,6 +127,18 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) dev(uart)->CTRLB.reg |= SERCOM_USART_CTRLB_SFDE; } } +#ifdef MODULE_PERIPH_UART_NONBLOCKING +#ifndef UART_HAS_TX_ISR + else { + /* enable UART ISR */ + NVIC_EnableIRQ(SERCOM0_IRQn + sercom_id(dev(uart))); + } +#else + /* enable TXE ISR */ + NVIC_EnableIRQ(SERCOM0_0_IRQn + (sercom_id(dev(uart)) * 4)); +#endif +#endif /* MODULE_PERIPH_UART_NONBLOCKING */ + while (dev(uart)->SYNCBUSY.bit.CTRLB) {} /* and finally enable the device */ @@ -121,11 +149,18 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) void uart_write(uart_t uart, const uint8_t *data, size_t len) { - for (size_t i = 0; i < len; i++) { +#ifdef MODULE_PERIPH_UART_NONBLOCKING + for (const void* end = data + len; data != end; ++data) { + while (tsrb_add_one(&uart_tx_rb[uart], *data) < 0) {} + dev(uart)->INTENSET.reg = SERCOM_USART_INTENSET_DRE; + } +#else + for (const void* end = data + len; data != end; ++data) { while (!dev(uart)->INTFLAG.bit.DRE) {} - dev(uart)->DATA.reg = data[i]; + dev(uart)->DATA.reg = *data; } while (!dev(uart)->INTFLAG.bit.TXC) {} +#endif } void uart_poweron(uart_t uart) @@ -181,14 +216,38 @@ int uart_mode(uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity, } #endif +#ifdef MODULE_PERIPH_UART_NONBLOCKING +static inline void irq_handler_tx(unsigned uartnum) +{ + /* workaround for saml1x */ + int c = tsrb_get_one(&uart_tx_rb[uartnum]); + if (c >= 0) { + dev(uartnum)->DATA.reg = c; + } + + /* disable the interrupt if there are no more bytes to send */ + if (tsrb_empty(&uart_tx_rb[uartnum])) { + dev(uartnum)->INTENCLR.reg = SERCOM_USART_INTENSET_DRE; + } +} +#endif + static inline void irq_handler(unsigned uartnum) { - if (dev(uartnum)->INTFLAG.bit.RXC) { + uint32_t status = dev(uartnum)->INTFLAG.reg; + +#if !defined(UART_HAS_TX_ISR) && defined(MODULE_PERIPH_UART_NONBLOCKING) + if ((status & SERCOM_USART_INTFLAG_DRE) && dev(uartnum)->INTENSET.bit.DRE) { + irq_handler_tx(uartnum); + } +#endif + + if (status & SERCOM_USART_INTFLAG_RXC) { /* interrupt flag is cleared by reading the data register */ uart_ctx[uartnum].rx_cb(uart_ctx[uartnum].arg, (uint8_t)(dev(uartnum)->DATA.reg)); } - else if (dev(uartnum)->INTFLAG.bit.ERROR) { + else if (status & SERCOM_USART_INTFLAG_ERROR) { /* clear error flag */ dev(uartnum)->INTFLAG.reg = SERCOM_USART_INTFLAG_ERROR; } @@ -237,3 +296,48 @@ void UART_5_ISR(void) irq_handler(5); } #endif + +#ifdef MODULE_PERIPH_UART_NONBLOCKING + +#ifdef UART_0_ISR_TX +void UART_0_ISR_TX(void) +{ + irq_handler_tx(0); +} +#endif + +#ifdef UART_1_ISR_TX +void UART_1_ISR_TX(void) +{ + irq_handler_tx(1); +} +#endif + +#ifdef UART_2_ISR_TX +void UART_2_ISR_TX(void) +{ + irq_handler_tx(2); +} +#endif + +#ifdef UART_3_ISR_TX +void UART_3_ISR_TX(void) +{ + irq_handler_tx(3); +} +#endif + +#ifdef UART_4_ISR_TX +void UART_4_ISR_TX(void) +{ + irq_handler_tx(4); +} +#endif + +#ifdef UART_5_ISR_TX +void UART_5_ISR_TX(void) +{ + irq_handler_tx(5); +} +#endif +#endif /* MODULE_PERIPH_UART_NONBLOCKING */ diff --git a/cpu/samd21/Makefile.dep b/cpu/samd21/Makefile.dep new file mode 100644 index 0000000000..eccd575bc9 --- /dev/null +++ b/cpu/samd21/Makefile.dep @@ -0,0 +1 @@ +include $(RIOTCPU)/sam0_common/Makefile.dep diff --git a/cpu/samd5x/Makefile.dep b/cpu/samd5x/Makefile.dep new file mode 100644 index 0000000000..eccd575bc9 --- /dev/null +++ b/cpu/samd5x/Makefile.dep @@ -0,0 +1 @@ +include $(RIOTCPU)/sam0_common/Makefile.dep diff --git a/cpu/saml1x/Makefile.dep b/cpu/saml1x/Makefile.dep new file mode 100644 index 0000000000..eccd575bc9 --- /dev/null +++ b/cpu/saml1x/Makefile.dep @@ -0,0 +1 @@ +include $(RIOTCPU)/sam0_common/Makefile.dep diff --git a/cpu/saml21/Makefile.dep b/cpu/saml21/Makefile.dep new file mode 100644 index 0000000000..eccd575bc9 --- /dev/null +++ b/cpu/saml21/Makefile.dep @@ -0,0 +1 @@ +include $(RIOTCPU)/sam0_common/Makefile.dep