From f89d0c7c255ccb4be1eceb0572b6a6fe27ed2ab2 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Mon, 26 Jul 2021 14:42:03 +0200 Subject: [PATCH 1/5] drivers/periph/uart: add periph_uart_collision feature --- drivers/include/periph/uart.h | 42 ++++++++++++++++++++++++++++++ drivers/periph_common/Kconfig.uart | 4 +++ kconfigs/Kconfig.features | 5 ++++ 3 files changed, 51 insertions(+) diff --git a/drivers/include/periph/uart.h b/drivers/include/periph/uart.h index 26b4142132..92448a3ebe 100644 --- a/drivers/include/periph/uart.h +++ b/drivers/include/periph/uart.h @@ -296,6 +296,48 @@ void uart_rxstart_irq_enable(uart_t uart); void uart_rxstart_irq_disable(uart_t uart); #endif /* MODULE_PERIPH_UART_RXSTART_IRQ */ +#if defined(MODULE_PERIPH_UART_COLLISION) || DOXYGEN +/** + * @brief Enables collision detection check of the UART. + * This assumes the UART is connected to a bus where RX and TX are + * connected. After each sent byte it is checked whether the same + * byte could be received. + * + * This disables the RX interrupt. + * + * @note You have to add the module `periph_uart_rxstart_irq` to your project + * to enable this function + * + * @param[in] uart The device to configure + */ +void uart_collision_detect_enable(uart_t uart); +/** + * @brief Disables collision detection check of the UART. + * + * If an RX interrupt was configured before, it is enabled again. + * + * @note You have to add the module `periph_uart_rxstart_irq` to your project + * to enable this function + * + * @param[in] uart The device to configure + */ +void uart_collision_detect_disable(uart_t uart); + +/** + * @brief Disables collision detection check of the UART. + * + * If an RX interrupt was configured before, it is enabled again. + * + * @note You have to add the module `periph_uart_rxstart_irq` to your project + * to enable this function + * + * @param[in] uart The device to probe + * + * @return true if a collision occurred during the last transder + */ +bool uart_collision_detected(uart_t uart); +#endif /* MODULE_PERIPH_UART_COLLISION */ + /** * @brief Setup parity, data and stop bits for a given UART device * diff --git a/drivers/periph_common/Kconfig.uart b/drivers/periph_common/Kconfig.uart index 8e5402f3fc..f43b873d25 100644 --- a/drivers/periph_common/Kconfig.uart +++ b/drivers/periph_common/Kconfig.uart @@ -28,6 +28,10 @@ config MODULE_PERIPH_LPUART bool "Enable Low Power UART (LPUART)" depends on HAS_PERIPH_LPUART +config MODULE_PERIPH_UART_COLLISION + bool "Enable UART collision detection" + depends on HAS_PERIPH_UART_COLLISION + config MODULE_PERIPH_UART_NONBLOCKING bool "Non-blocking support" depends on HAS_PERIPH_UART_NONBLOCKING diff --git a/kconfigs/Kconfig.features b/kconfigs/Kconfig.features index 537b63b2fc..ed995187e5 100644 --- a/kconfigs/Kconfig.features +++ b/kconfigs/Kconfig.features @@ -323,6 +323,11 @@ config HAS_PERIPH_UART help Indicates that an UART peripheral is present. +config HAS_PERIPH_UART_COLLISION + bool + help + Indicates that the UART peripheral supports hardware collision detection. + config HAS_PERIPH_UART_HW_FC bool help From a51fb298dc2cc7e909e47623f48bc8293364d140 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Mon, 26 Jul 2021 14:42:36 +0200 Subject: [PATCH 2/5] cpu/sam0_common: implement periph_uart_collision feature --- cpu/sam0_common/periph/uart.c | 76 +++++++++++++++++++++++++++++++++++ cpu/samd21/Kconfig | 3 ++ cpu/samd21/Makefile.features | 4 ++ cpu/samd5x/Kconfig | 1 + cpu/samd5x/Makefile.features | 1 + cpu/saml1x/Kconfig | 1 + cpu/saml1x/Makefile.features | 1 + cpu/saml21/Kconfig | 1 + cpu/saml21/Makefile.features | 1 + 9 files changed, 89 insertions(+) diff --git a/cpu/sam0_common/periph/uart.c b/cpu/sam0_common/periph/uart.c index dc8fb6a224..32b804998e 100644 --- a/cpu/sam0_common/periph/uart.c +++ b/cpu/sam0_common/periph/uart.c @@ -322,6 +322,82 @@ void uart_poweroff(uart_t uart) sercom_clk_dis(dev(uart)); } +#ifdef MODULE_PERIPH_UART_COLLISION +bool uart_collision_detected(uart_t uart) +{ + /* In case of collision, the CTRLB register + * will be in sync during disabling of TX, + * then the flag will be set. + */ + _syncbusy(dev(uart)); + + bool collision = dev(uart)->STATUS.bit.COLL; + dev(uart)->STATUS.reg = SERCOM_USART_STATUS_COLL; + return collision; +} + +void uart_collision_detect_enable(uart_t uart) +{ + /* CTRLB is enable protected */ + dev(uart)->CTRLA.bit.ENABLE = 0; + _syncbusy(dev(uart)); + + /* clear stale collision flag */ + dev(uart)->STATUS.reg = SERCOM_USART_STATUS_COLL; + + /* enable collision detection */ + dev(uart)->CTRLB.bit.COLDEN = 1; + + /* disable RX interrupt */ + dev(uart)->INTENCLR.bit.RXC = 1; + + /* re-enable UART */ + dev(uart)->CTRLA.bit.ENABLE = 1; + + /* wait for config to be applied */ + _syncbusy(dev(uart)); +} + +static void _drain_rxbuf(SercomUsart *dev) +{ + /* clear readback bytes from receive buffer */ + while (dev->INTFLAG.bit.RXC) { + dev->DATA.reg; + } +} + +void uart_collision_detect_disable(uart_t uart) +{ + uint32_t ctrlb = dev(uart)->CTRLB.reg; + + /* re-enable TX after collision */ + ctrlb |= SERCOM_USART_CTRLB_TXEN; + + /* disable collision detection */ + ctrlb &= ~SERCOM_USART_CTRLB_COLDEN; + + /* CTRLB is enable protected */ + dev(uart)->CTRLA.bit.ENABLE = 0; + _syncbusy(dev(uart)); + + dev(uart)->CTRLB.reg = ctrlb; + + /* re-enable UART */ + dev(uart)->CTRLA.bit.ENABLE = 1; + + /* wait for config to be applied */ + _syncbusy(dev(uart)); + + /* clear bytes from RX buffer */ + _drain_rxbuf(dev(uart)); + + /* re-enable RX complete IRQ */ + if (uart_ctx[uart].rx_cb) { + dev(uart)->INTENSET.bit.RXC = 1; + } +} +#endif + #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) diff --git a/cpu/samd21/Kconfig b/cpu/samd21/Kconfig index f90b970f71..718bb8f9a4 100644 --- a/cpu/samd21/Kconfig +++ b/cpu/samd21/Kconfig @@ -16,6 +16,7 @@ config CPU_FAM_SAMD10 bool select CPU_COMMON_SAMD21 select HAS_PERIPH_DMA + select HAS_PERIPH_UART_COLLISION config CPU_FAM_SAMD20 bool @@ -25,11 +26,13 @@ config CPU_FAM_SAMD21 bool select CPU_COMMON_SAMD21 select HAS_PERIPH_DMA + select HAS_PERIPH_UART_COLLISION config CPU_FAM_SAMR21 bool select CPU_COMMON_SAMD21 select HAS_PERIPH_DMA + select HAS_PERIPH_UART_COLLISION ## Definition of specific features config HAS_CPU_SAMD21 diff --git a/cpu/samd21/Makefile.features b/cpu/samd21/Makefile.features index 043d08dea3..f7e9895431 100644 --- a/cpu/samd21/Makefile.features +++ b/cpu/samd21/Makefile.features @@ -2,4 +2,8 @@ CPU_CORE = cortex-m0plus FEATURES_PROVIDED += periph_gpio_fast_read +ifeq (,$(filter samd20%,$(CPU_MODEL))) + FEATURES_PROVIDED += periph_uart_collision +endif + -include $(RIOTCPU)/sam0_common/Makefile.features diff --git a/cpu/samd5x/Kconfig b/cpu/samd5x/Kconfig index 0531548781..54339e8ca7 100644 --- a/cpu/samd5x/Kconfig +++ b/cpu/samd5x/Kconfig @@ -16,6 +16,7 @@ config CPU_COMMON_SAMD5X select HAS_PERIPH_GPIO_TAMPER_WAKE select HAS_PERIPH_HWRNG select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_UART_COLLISION select HAS_PERIPH_SPI_ON_QSPI config CPU_FAM_SAMD51 diff --git a/cpu/samd5x/Makefile.features b/cpu/samd5x/Makefile.features index e024c5e1bc..7cba14d218 100644 --- a/cpu/samd5x/Makefile.features +++ b/cpu/samd5x/Makefile.features @@ -6,5 +6,6 @@ FEATURES_PROVIDED += cortexm_mpu FEATURES_PROVIDED += periph_gpio_tamper_wake FEATURES_PROVIDED += periph_rtc_mem FEATURES_PROVIDED += periph_spi_on_qspi +FEATURES_PROVIDED += periph_uart_collision include $(RIOTCPU)/sam0_common/Makefile.features diff --git a/cpu/saml1x/Kconfig b/cpu/saml1x/Kconfig index ca7fa9ad04..1fa2335c38 100644 --- a/cpu/saml1x/Kconfig +++ b/cpu/saml1x/Kconfig @@ -13,6 +13,7 @@ config CPU_COMMON_SAML1X select HAS_PERIPH_DMA select HAS_PERIPH_GPIO_FAST_READ select HAS_PERIPH_HWRNG + select HAS_PERIPH_UART_COLLISION config CPU_FAM_SAML10 bool diff --git a/cpu/saml1x/Makefile.features b/cpu/saml1x/Makefile.features index 267b8b7fee..b46af8aaba 100644 --- a/cpu/saml1x/Makefile.features +++ b/cpu/saml1x/Makefile.features @@ -5,5 +5,6 @@ CPU_CORE = cortex-m23 FEATURES_PROVIDED += periph_hwrng FEATURES_PROVIDED += periph_gpio_fast_read +FEATURES_PROVIDED += periph_uart_collision include $(RIOTCPU)/sam0_common/Makefile.features diff --git a/cpu/saml21/Kconfig b/cpu/saml21/Kconfig index 97c99c2540..dc8ca5f358 100644 --- a/cpu/saml21/Kconfig +++ b/cpu/saml21/Kconfig @@ -14,6 +14,7 @@ config CPU_COMMON_SAML21 select HAS_PERIPH_DMA select HAS_PERIPH_GPIO_FAST_READ select HAS_PERIPH_RTC_MEM + select HAS_PERIPH_UART_COLLISION config CPU_FAM_SAML21 bool diff --git a/cpu/saml21/Makefile.features b/cpu/saml21/Makefile.features index 85685fb27f..fe77d1c1df 100644 --- a/cpu/saml21/Makefile.features +++ b/cpu/saml21/Makefile.features @@ -4,6 +4,7 @@ CPU_CORE = cortex-m0plus CPU_MODELS_WITHOUT_HWRNG += samr30% FEATURES_PROVIDED += periph_gpio_fast_read +FEATURES_PROVIDED += periph_uart_collision # Low Power SRAM is *not* retained during Backup Sleep. # It therefore does not fulfill the requirements of the 'backup_ram' interface. From 0c5631bd52e3b2cafcb92d68e1278cf66665a679 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Mon, 26 Jul 2021 14:42:53 +0200 Subject: [PATCH 3/5] drivers/dose: make use of periph_uart_collision feature --- drivers/dose/Makefile.dep | 1 + drivers/dose/dose.c | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/drivers/dose/Makefile.dep b/drivers/dose/Makefile.dep index a2b81d4233..c483c0e946 100644 --- a/drivers/dose/Makefile.dep +++ b/drivers/dose/Makefile.dep @@ -1,5 +1,6 @@ FEATURES_REQUIRED += periph_gpio_irq FEATURES_REQUIRED += periph_uart +FEATURES_OPTIONAL += periph_uart_collision FEATURES_OPTIONAL += periph_uart_rxstart_irq USEMODULE += eui_provider diff --git a/drivers/dose/dose.c b/drivers/dose/dose.c index 470db77406..65d192ae0b 100644 --- a/drivers/dose/dose.c +++ b/drivers/dose/dose.c @@ -201,9 +201,9 @@ static dose_signal_t state_transit_send(dose_t *ctx, dose_signal_t signal) /* Don't trace any END octets ... the timeout or the END signal * will bring us back to the BLOCKED state after _send has emitted * its last octet. */ - +#ifndef MODULE_PERIPH_UART_COLLISION xtimer_set(&ctx->timeout, ctx->timeout_base); - +#endif return DOSE_SIGNAL_NONE; } @@ -398,6 +398,10 @@ static int send_octet(dose_t *ctx, uint8_t c) { uart_write(ctx->uart, (uint8_t *) &c, sizeof(c)); +#ifdef MODULE_PERIPH_UART_COLLISION + return uart_collision_detected(ctx->uart); +#endif + /* Wait for a state transition */ uint8_t new_state = wait_for_state(ctx, DOSE_STATE_ANY); if (new_state != DOSE_STATE_SEND) { @@ -432,6 +436,28 @@ static int send_data_octet(dose_t *ctx, uint8_t c) return rc; } +static inline void _send_start(dose_t *ctx) +{ +#ifdef MODULE_PERIPH_UART_COLLISION + uart_collision_detect_enable(ctx->uart); +#else + (void)ctx; +#endif +} + +static inline void _send_done(dose_t *ctx, bool collision) +{ +#ifdef MODULE_PERIPH_UART_COLLISION + uart_collision_detect_disable(ctx->uart); + if (collision) { + state(ctx, DOSE_SIGNAL_XTIMER); + } +#else + (void)ctx; + (void)collision; +#endif +} + static int _send(netdev_t *dev, const iolist_t *iolist) { dose_t *ctx = container_of(dev, dose_t, netdev); @@ -459,6 +485,8 @@ send: state(ctx, DOSE_SIGNAL_SEND); } while (wait_for_state(ctx, DOSE_STATE_ANY) != DOSE_STATE_SEND); + _send_start(ctx); + /* Send packet buffer */ for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) { size_t n = iol->iol_len; @@ -491,6 +519,8 @@ send: goto collision; } + _send_done(ctx, false); + /* We probably sent the whole packet?! */ dev->event_callback(dev, NETDEV_EVENT_TX_COMPLETE); @@ -500,11 +530,13 @@ send: return pktlen; collision: + _send_done(ctx, true); DEBUG("dose _send(): collision!\n"); if (--retries < 0) { dev->event_callback(dev, NETDEV_EVENT_TX_MEDIUM_BUSY); return -EBUSY; } + goto send; } From 9f63f807476954d240f9a960b0f640a601b1e968 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Mon, 26 Jul 2021 14:43:48 +0200 Subject: [PATCH 4/5] tests/periph_uart: add test for periph_uart_collision feature --- tests/periph_uart/Kconfig | 1 + tests/periph_uart/Makefile | 1 + tests/periph_uart/main.c | 8 ++++++++ 3 files changed, 10 insertions(+) diff --git a/tests/periph_uart/Kconfig b/tests/periph_uart/Kconfig index 394cc6b398..48eb39d095 100644 --- a/tests/periph_uart/Kconfig +++ b/tests/periph_uart/Kconfig @@ -11,4 +11,5 @@ config APPLICATION imply MODULE_PERIPH_UART_MODECFG imply MODULE_PERIPH_LPUART imply MODULE_PERIPH_UART_RXSTART_IRQ + imply MODULE_PERIPH_UART_COLLISION depends on TEST_KCONFIG diff --git a/tests/periph_uart/Makefile b/tests/periph_uart/Makefile index 20dd17f59a..37ef5e1065 100644 --- a/tests/periph_uart/Makefile +++ b/tests/periph_uart/Makefile @@ -2,6 +2,7 @@ include ../Makefile.tests_common FEATURES_REQUIRED += periph_uart FEATURES_OPTIONAL += periph_lpuart # STM32 L0 and L4 provides lpuart support +FEATURES_OPTIONAL += periph_uart_collision FEATURES_OPTIONAL += periph_uart_modecfg FEATURES_OPTIONAL += periph_uart_rxstart_irq diff --git a/tests/periph_uart/main.c b/tests/periph_uart/main.c index 0a58ae7d09..1d7a73f74c 100644 --- a/tests/periph_uart/main.c +++ b/tests/periph_uart/main.c @@ -151,6 +151,14 @@ static int _self_test(uart_t dev, unsigned baud) } uart_rxstart_irq_disable(dev); #endif +#ifdef MODULE_PERIPH_UART_COLLISION + uart_collision_detect_enable(dev); + uart_write(dev, (uint8_t*)test_string, sizeof(test_string)); + if (uart_collision_detected(dev)) { + printf("collision detected\n"); + } + uart_collision_detect_disable(dev); +#endif test_mode = false; return 0; From 780e87325bf5dca28b278150ed725fd27a1c312c Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 7 Dec 2021 19:35:53 +0100 Subject: [PATCH 5/5] tests/driver_dose: use USEMODULE += --- tests/driver_dose/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/driver_dose/Makefile b/tests/driver_dose/Makefile index 53a3382ca4..456947dcf4 100644 --- a/tests/driver_dose/Makefile +++ b/tests/driver_dose/Makefile @@ -1,4 +1,4 @@ # the driver to test -USEMODULE = dose +USEMODULE += dose include ../driver_netdev_common/Makefile.netdev.mk