From abf95d14a69b9fb91fa167fa49bd990c43b29e37 Mon Sep 17 00:00:00 2001 From: chrysn Date: Thu, 17 Aug 2023 13:42:03 +0200 Subject: [PATCH 1/8] cpu/nrf5x: Tolerate NULL callback in timers timer_set has no documented restriction on this being not null, other implementations explicitly tolerate it (rpx0xx checks inside the ISR, but doing it at init time keeps the ISR slim). This is useful when using a timer just to read, without any action when it triggers (the action is taken depending on read values, eg. in a thread context). --- cpu/nrf5x_common/periph/timer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpu/nrf5x_common/periph/timer.c b/cpu/nrf5x_common/periph/timer.c index 556ead9b63be..ae944867d57f 100644 --- a/cpu/nrf5x_common/periph/timer.c +++ b/cpu/nrf5x_common/periph/timer.c @@ -111,7 +111,9 @@ int timer_init(tim_t tim, uint32_t freq, timer_cb_t cb, void *arg) } /* enable interrupts */ - NVIC_EnableIRQ(timer_config[tim].irqn); + if (cb != NULL) { + NVIC_EnableIRQ(timer_config[tim].irqn); + } /* start the timer */ dev(tim)->TASKS_START = 1; From d32c32ffae4193888bac606a00cac2fb0cec62b3 Mon Sep 17 00:00:00 2001 From: chrysn Date: Thu, 17 Aug 2023 13:51:14 +0200 Subject: [PATCH 2/8] boards/nrf52: Defined TIMER_x_MAX_VALUE The macro's presence is documented in `timer_init`, but was missing from this platform. --- boards/common/nrf52/include/cfg_timer_default.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/boards/common/nrf52/include/cfg_timer_default.h b/boards/common/nrf52/include/cfg_timer_default.h index e82b17fdb80e..2931a014c338 100644 --- a/boards/common/nrf52/include/cfg_timer_default.h +++ b/boards/common/nrf52/include/cfg_timer_default.h @@ -80,6 +80,21 @@ static const timer_conf_t timer_config[] = { #define TIMER_2_ISR isr_timer3 #define TIMER_3_ISR isr_timer4 +/** See @ref timer_init */ +#define TIMER_0_MAX_VALUE 0xffffffff +/** See @ref timer_init */ +#define TIMER_1_MAX_VALUE 0xffffffff +#ifdef NRF_TIMER3 +/** See @ref timer_init */ +#define TIMER_2_MAX_VALUE 0xffffffff +#endif +/* If there is no NRF_TIMER3 this should be TIMER_2 because the index shifts + * up, but there is only a TIMER4 if there is a TIMER3 too. */ +#ifdef NRF_TIMER4 +/** See @ref timer_init */ +#define TIMER_3_MAX_VALUE 0xffffffff +#endif + #define TIMER_NUMOF ARRAY_SIZE(timer_config) /** @} */ From 02285fd63a5163f42b597d6f21041745048aaa0c Mon Sep 17 00:00:00 2001 From: chrysn Date: Thu, 17 Aug 2023 14:45:42 +0200 Subject: [PATCH 3/8] drivers/periph: Add timer_poll feature and timer_poll_channel function --- cpu/nrf5x_common/Kconfig | 1 + cpu/nrf5x_common/Makefile.features | 1 + cpu/nrf5x_common/include/timer_arch.h | 42 +++++++++++++++++++++++++++ drivers/include/periph/timer.h | 34 ++++++++++++++++++++++ kconfigs/Kconfig.features | 6 ++++ 5 files changed, 84 insertions(+) create mode 100644 cpu/nrf5x_common/include/timer_arch.h diff --git a/cpu/nrf5x_common/Kconfig b/cpu/nrf5x_common/Kconfig index 9a9a8d7736a4..8e715bb39ef2 100644 --- a/cpu/nrf5x_common/Kconfig +++ b/cpu/nrf5x_common/Kconfig @@ -20,6 +20,7 @@ depends on !CPU_FAM_NRF53 select HAS_PERIPH_HWRNG select HAS_PERIPH_TEMPERATURE select HAS_PERIPH_TIMER_PERIODIC + select HAS_PERIPH_TIMER_POLL select HAS_PERIPH_TIMER_QUERY_FREQS select HAS_PERIPH_RTT_OVERFLOW select HAS_PERIPH_UART_MODECFG diff --git a/cpu/nrf5x_common/Makefile.features b/cpu/nrf5x_common/Makefile.features index 0bfe5661f356..7f6fdfdd07ab 100644 --- a/cpu/nrf5x_common/Makefile.features +++ b/cpu/nrf5x_common/Makefile.features @@ -5,6 +5,7 @@ FEATURES_PROVIDED += periph_flashpage_in_address_space FEATURES_PROVIDED += periph_flashpage_pagewise FEATURES_PROVIDED += periph_gpio periph_gpio_irq FEATURES_PROVIDED += periph_timer_periodic +FEATURES_PROVIDED += periph_timer_poll FEATURES_PROVIDED += periph_timer_query_freqs FEATURES_PROVIDED += periph_uart_modecfg FEATURES_PROVIDED += periph_wdt periph_wdt_cb diff --git a/cpu/nrf5x_common/include/timer_arch.h b/cpu/nrf5x_common/include/timer_arch.h new file mode 100644 index 000000000000..325145215888 --- /dev/null +++ b/cpu/nrf5x_common/include/timer_arch.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 Jan Wagner + * 2015-2016 Freie Universität Berlin + * 2019 Inria + * + * 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 cpu_nrf5x_common + * @ingroup drivers_periph_timer + * @{ + * + * @file + * @brief CPU specific part of the timer API + * + * @author Christian Amsüss + */ + +#ifndef TIMER_ARCH_H +#define TIMER_ARCH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DOXYGEN /* hide implementation specific details from Doxygen */ + +static inline bool timer_poll_channel(tim_t tim, int channel) +{ + return timer_config[tim].dev->EVENTS_COMPARE[channel]; +} + +#endif /* DOXYGEN */ +#ifdef __cplusplus +} +#endif + +#endif /* TIMER_ARCH_H */ +/** @} */ diff --git a/drivers/include/periph/timer.h b/drivers/include/periph/timer.h index c4c021aba1fa..e9321c196373 100644 --- a/drivers/include/periph/timer.h +++ b/drivers/include/periph/timer.h @@ -35,6 +35,7 @@ #include #include +#include #include "architecture.h" #include "periph_cpu.h" @@ -295,6 +296,39 @@ uword_t timer_query_channel_numof(tim_t dev); */ uint32_t timer_query_freqs(tim_t dev, uword_t index); +#if defined(DOXYGEN) +/** + * @brief Check whether a compare channel has matched + * + * @return true once after the channel has matched. + * + * It is currently not defined whether this keeps returning true after a + * channel has been polled until that channel is set, or whether later calls + * return false. + * + * This is typically used in spin loops that wait for a timer's completion: + * + * ~~~ + * while (!timer_poll_channel(tim, chan)) {}; + * ~~~ + * + * This function is only available on platforms that implement the + * `periph_timer_poll` peripheral in addition to `periph_timer`. + * + */ +/* As this function is polled, it needs to be inlined, so it is typically + * provided through timer_arch.h. If a platform ever does not need to go + * through static inline here, this declaration's condition can be extended to + * be `(defined(MODULE_PERIPH_TIMER_POLL) && + * !defined(PERIPH_TIMER_PROVIDES_INLINE_POLL_CHANNEL) || defined(DOXYGEN)` or + * similar. */ +bool timer_poll_channel(tim_t dev, int channel); +#endif + +#if defined(MODULE_PERIPH_TIMER_POLL) +#include "timer_arch.h" +#endif + #ifdef __cplusplus } #endif diff --git a/kconfigs/Kconfig.features b/kconfigs/Kconfig.features index b867d180a444..7614d72a8530 100644 --- a/kconfigs/Kconfig.features +++ b/kconfigs/Kconfig.features @@ -572,6 +572,12 @@ config HAS_PERIPH_TIMER_PERIODIC Indicates that the Timer peripheral provides the periodic timeout functionality. +config HAS_PERIPH_TIMER_POLL + bool + help + Indicates that the Timer peripheral supports the timer_poll_channel + function. + config HAS_PERIPH_TIMER_QUERY_FREQS bool help From d86405cef4e6437e5ba842b20a62f9c5d0de9bd3 Mon Sep 17 00:00:00 2001 From: chrysn Date: Tue, 28 Nov 2023 18:03:32 +0100 Subject: [PATCH 4/8] drivers/ws281x: Add timer and gpio_ll based driver --- drivers/ws281x/Kconfig | 11 +- drivers/ws281x/Makefile.dep | 11 +- drivers/ws281x/include/ws281x_backend.h | 9 ++ drivers/ws281x/include/ws281x_params.h | 41 ++++++ drivers/ws281x/timer_gpio_ll.c | 169 ++++++++++++++++++++++++ tests/drivers/ws281x/Makefile | 10 ++ 6 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 drivers/ws281x/timer_gpio_ll.c diff --git a/drivers/ws281x/Kconfig b/drivers/ws281x/Kconfig index ef44ba5f5e16..65b1241bb5f9 100644 --- a/drivers/ws281x/Kconfig +++ b/drivers/ws281x/Kconfig @@ -7,12 +7,13 @@ config MODULE_WS281X bool "WS2812/SK6812 RGB LED (NeoPixel)" - depends on HAS_CPU_CORE_ATMEGA || HAS_ARCH_ESP32 || HAS_ARCH_NATIVE + depends on HAS_CPU_CORE_ATMEGA || HAS_ARCH_ESP32 || HAS_ARCH_NATIVE || HAS_PERIPH_TIMER_POLL depends on TEST_KCONFIG select MODULE_XTIMER select MODULE_WS281X_ATMEGA if HAS_CPU_CORE_ATMEGA select MODULE_WS281X_VT100 if HAS_ARCH_NATIVE select MODULE_WS281X_ESP32 if HAS_ARCH_ESP32 + select MODULE_WS281X_TIMER_GPIO_LL if HAS_PERIPH_TIMER_POLL config MODULE_WS281X_ATMEGA bool @@ -45,6 +46,14 @@ config MODULE_WS281X_ESP32_SW Use the bit-banging software implementation to generate the RGB LED signal. +config MODULE_WS281X_TIMER_GPIO_LL + bool + depends on HAS_PERIPH_TIMER_POLL + depends on HAS_PERIPH_GPIO_LL + help + Use a platform independent bit-banging software implementation to + generate the RGB LED signal. + config HAVE_WS281X bool help diff --git a/drivers/ws281x/Makefile.dep b/drivers/ws281x/Makefile.dep index 76ef1f0b323f..f539c4de83d7 100644 --- a/drivers/ws281x/Makefile.dep +++ b/drivers/ws281x/Makefile.dep @@ -1,4 +1,5 @@ -FEATURES_REQUIRED_ANY += cpu_core_atmega|arch_esp32|arch_native +# Actually |(periph_timer_poll and periph_gpio_ll), but that's too complex for FEATURES_REQUIRED_ANY to express +FEATURES_REQUIRED_ANY += cpu_core_atmega|arch_esp32|arch_native|periph_timer_poll ifeq (,$(filter ws281x_%,$(USEMODULE))) ifneq (,$(filter cpu_core_atmega,$(FEATURES_USED))) @@ -10,6 +11,10 @@ ifeq (,$(filter ws281x_%,$(USEMODULE))) ifneq (,$(filter arch_esp32,$(FEATURES_USED))) USEMODULE += ws281x_esp32 endif + # Not only looking for the used feature but also for the absence of any more specific driver + ifeq (-periph_timer_poll,$(filter ws281x_%,$(USEMODULE))-$(filter periph_timer_poll,$(FEATURES_USED))) + USEMODULE += ws281x_timer_gpio_ll + endif endif ifneq (,$(filter ws281x_atmega,$(USEMODULE))) @@ -28,3 +33,7 @@ ifneq (,$(filter ws281x_esp32%,$(USEMODULE))) USEMODULE += ws281x_esp32_hw endif endif + +ifneq (,$(filter ws281x_timer_gpio_ll,$(USEMODULE))) + FEATURES_REQUIRED += periph_gpio_ll periph_timer periph_timer_poll +endif diff --git a/drivers/ws281x/include/ws281x_backend.h b/drivers/ws281x/include/ws281x_backend.h index 888c279a9045..01f74fb87789 100644 --- a/drivers/ws281x/include/ws281x_backend.h +++ b/drivers/ws281x/include/ws281x_backend.h @@ -51,6 +51,15 @@ extern "C" { #endif /** @} */ +/** + * @name Properties of the timer_gpio_ll backend. + * @{ + */ +#ifdef MODULE_WS281X_TIMER_GPIO_LL +#define WS281X_HAVE_INIT (1) +#endif +/** @} */ + #ifdef __cplusplus } #endif diff --git a/drivers/ws281x/include/ws281x_params.h b/drivers/ws281x/include/ws281x_params.h index 1950e3f4ebc1..e68b8f91839b 100644 --- a/drivers/ws281x/include/ws281x_params.h +++ b/drivers/ws281x/include/ws281x_params.h @@ -64,6 +64,47 @@ static const ws281x_params_t ws281x_params[] = WS281X_PARAMS }; +/** @brief Timer used for WS281x (by the timer_gpio_ll implementation) + * + * A single timer is configured for any number of WS281x strands, so this does + * not need to be part of params. + * + * It is required that the timer has at least 2 channels. (Future versions may + * require a 3rd channel). + * + * It is required that the timer's MAX_VALUE is 2^n-1, which is a trivial but + * not explicitly stated case. + * + * This timer is configured at WS281x initialization time, and kept stopped + * outside of transmissions. + * + * The default value of 2 is chosen because the only platform on which the + * module is usable is nRF5x, where TIMER_DEV(1) is in use by the radio module. + * It is strongly advised to explicitly set this timer to a known free timer, + * as the default may change without notice. + * */ +#if !defined(WS281X_TIMER_DEV) || defined(DOXYGEN) +#define WS281X_TIMER_DEV TIMER_DEV(2) +#endif + +/** @brief Maximum value of the timer used for WS281x (by the timer_gpio_ll implementation) + * + * This macro needs to be defined to the `TIMER_x_MAX_VALUE` corresponding to + * the `TIMER_DEV(x)` in @ref WS281X_TIMER_DEV. + * */ +#ifndef WS281X_TIMER_MAX_VALUE +#define WS281X_TIMER_MAX_VALUE TIMER_2_MAX_VALUE +#endif + +/** @brief Frequency for the timer used for WS281x (by the timer_gpio_ll implementation) + * + * This should be set to a frequency that is a close multiple of 3MHz, + * depending on the precise low and high times. A value of 16MHz works well. + * */ +#ifndef WS281X_TIMER_FREQ +#define WS281X_TIMER_FREQ 16000000 +#endif + #ifdef __cplusplus } #endif diff --git a/drivers/ws281x/timer_gpio_ll.c b/drivers/ws281x/timer_gpio_ll.c new file mode 100644 index 000000000000..652f5af0ae4f --- /dev/null +++ b/drivers/ws281x/timer_gpio_ll.c @@ -0,0 +1,169 @@ +/* + * Copyright 2023 Christian Amsüss + * + * 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 drivers_ws281x + * + * @{ + * + * @file + * @brief Implementation of the WS281x abstraction based on GPIO_LL and timers + * + * When this is used, the whole @ref ws281x_write_buffer operation is conducted + * in a single interrupts-disabled run. As it takes about 1us per bit (eg. + * 0.3ms on a 100-LED strip), the use of this module needs to be carefully + * evaluated against other real-time requirements. + * + * (Letting the trailing pause run with interrupts enabled, or even enabling + * interrupts between bits, is an option that is being considered for future + * expansion). + * + * @author Christian Amsüss + * + * @} + */ +#include +#include +#include +#include + +#include "irq.h" +#include "time_units.h" +#include "periph/timer.h" +#include "periph/gpio_ll.h" + +#include "ws281x.h" +#include "ws281x_params.h" +#include "ws281x_constants.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +/* (+ NS_PER_SEC - 1): Rounding up, as T1H is the time that needs to distinctly + * longer than T0H. + * + * Then adding +1 extra, because the spin loop adds another layer of jitter. A + * more correct version would be to add the spin loop time before rounding (and + * then rounding up), but as that time is not available, spending one more + * cycle is the next best thing to do. */ +const int ticks_one = ((uint64_t)WS281X_T_DATA_ONE_NS * WS281X_TIMER_FREQ + NS_PER_SEC - 1) + / NS_PER_SEC + 1; +/* Rounding down, zeros are better shorter */ +const int ticks_zero = (uint64_t)WS281X_T_DATA_ZERO_NS * (uint64_t)WS281X_TIMER_FREQ / NS_PER_SEC; +/* No particular known requirements, but we're taking longer than that anyway + * because we don't clock the times between bits. */ +const int ticks_data = (uint64_t)WS281X_T_DATA_NS * (uint64_t)WS281X_TIMER_FREQ / NS_PER_SEC; + +void ws281x_write_buffer(ws281x_t *dev, const void *buf, size_t size) +{ + assert(dev); + + const uint8_t *pos = buf; + const uint8_t *end = pos + size; + + /* The logical sequence for a cycle would be to set high, sleep, set low, + * sleep. But the critical time is the high time, so we do something + * different: We go low, set up the timer, and then go high and back low + * both as controlled by the timer. That means that our logical cycles are + * the previous bit's low time followed by the current bit's high time -- so we store the previous bit here. + * + * It is initialized to 1 because the 1 has the longer high time, so the + * lower low time -- we just spend 325 instead of 600ns before actually + * sending the message. (This wait-before-we-start time is also part of how + * this implementation can get away without compensation for the time it + * takes to set up timers: it sets them up and then waits for some time + * that is known to be long enough, because that time also works + * mid-transmission). */ + bool last_bit = 1; + + gpio_port_t port = gpio_get_port(dev->params.pin); + uword_t mask = 1 << gpio_get_pin_num(dev->params.pin); + + /* If a too-slow channel is used, then the critical section could be done + * away with: If an interrupt fires (or a higher priority thread runs; + * really: runs long enough to matter, but that'd need very fast switching + * and hundreds of MHz to be the case), the function will return with an + * error (or ignore the error), and the worst thing that'd happen is that + * if it hits the last bit of a LED, that might be flipped to a value 1. + * */ + unsigned int irq_state = irq_disable(); + + while (pos < end) { + uint8_t data = *pos; + for (uint8_t cnt = 8; cnt > 0; cnt--) { + bool bit = data >> 7; + data <<= 1; + int time_to_set = last_bit ? (ticks_data - ticks_one) : (ticks_data - ticks_zero); + int time_to_clear = time_to_set + (bit ? ticks_one : ticks_zero); + + /* I'd prefer to just zero the timer, but we don't have API for that */ + int now = timer_read(WS281X_TIMER_DEV); + time_to_set = (time_to_set + now) & WS281X_TIMER_MAX_VALUE; + time_to_clear = (time_to_clear + now) & WS281X_TIMER_MAX_VALUE; + + timer_set_absolute(WS281X_TIMER_DEV, 0, time_to_set); + timer_set_absolute(WS281X_TIMER_DEV, 1, time_to_clear); + timer_start(WS281X_TIMER_DEV); + /* A + * ~~~ + * while ((int)timer_read(WS281X_TIMER_DEV) < time_to_set) {}; + * ~~~ + * would be way too slow, plus it'd impose additional requirements + * on the timer to avoid wrapping */ + while (!timer_poll_channel(WS281X_TIMER_DEV, 0)) {} + gpio_ll_set(port, mask); + while (!timer_poll_channel(WS281X_TIMER_DEV, 1)) {} + gpio_ll_clear(port, mask); + timer_stop(WS281X_TIMER_DEV); + + last_bit = bit; + } + pos++; + } + + irq_restore(irq_state); +} + +int ws281x_init(ws281x_t *dev, const ws281x_params_t *params) +{ + int err; + + if (!dev || !params || !params->buf) { + return -EINVAL; + } + + memset(dev, 0, sizeof(ws281x_t)); + dev->params = *params; + + gpio_port_t port = gpio_get_port(dev->params.pin); + uint8_t pin = gpio_get_pin_num(dev->params.pin); + + err = gpio_ll_init(port, pin, &gpio_ll_out); + DEBUG("Initializing port %x pin %d (originally %x): %d\n", + port, pin, params->pin, err); + if (err != 0) { + return -EIO; + } + + err = timer_init(WS281X_TIMER_DEV, WS281X_TIMER_FREQ, NULL, NULL); + DEBUG("Initialized timer to %d Hz: %d\n", WS281X_TIMER_FREQ, err); + if (err != 0) { + return -EIO; + } + timer_stop(WS281X_TIMER_DEV); + + /* We're not trying to make an assessment on whether that means we can + * manage or not, for that also depends on the number of instructions in + * the loop, which adds to the granularity. (Really the relevant time is + * the duration converted back from ticks to time, rounded up or down + * randomly to the duration of the loop). */ + DEBUG("At this speed, ones are %d ticks long and zeros %d ticks of a %d " + "tick transmission.\n", ticks_one, ticks_zero, ticks_data); + + return 0; +} diff --git a/tests/drivers/ws281x/Makefile b/tests/drivers/ws281x/Makefile index 008d0033a18b..a8def7989520 100644 --- a/tests/drivers/ws281x/Makefile +++ b/tests/drivers/ws281x/Makefile @@ -4,6 +4,10 @@ include ../Makefile.drivers_common # Update this to your needs # PIN ?= GPIO_PIN(0, 0) # N ?= 8 +# When using the ws281x_timer_gpio_ll module, you'll also need to define those: +# (Example values are suitable for nRF52 devices) +# TIMER ?= 2 +# FREQ ?= 16000000 USEMODULE += ws281x USEMODULE += xtimer @@ -22,3 +26,9 @@ endif ifneq (, $(N)) CFLAGS += '-DWS281X_PARAM_NUMOF=$(N)' endif +ifneq (, $(TIMER)) + CFLAGS += '-DWS281X_TIMER_DEV=TIMER_DEV($(TIMER))' '-DWS281X_TIMER_MAX_VALUE=TIMER_$(TIMER)_MAX_VALUE' +endif +ifneq (, $(FREQ)) + CFLAGS += '-DWS281X_TIMER_FREQ=$(FREQ)' +endif From 7b80348f315d6fada88a382e6fd8fff05a8b9f24 Mon Sep 17 00:00:00 2001 From: MrKevinWeiss Date: Fri, 1 Dec 2023 14:15:05 +0100 Subject: [PATCH 5/8] drivers/ws281x: Fix Kconfig Patch from https://github.com/RIOT-OS/RIOT/pull/19891#pullrequestreview-1753651538 --- cpu/nrf53/Kconfig | 1 + cpu/nrf9160/Kconfig | 1 + drivers/periph_common/Kconfig | 8 ++++++++ drivers/periph_common/Kconfig.gpio_ll | 22 ++++++++++++++++++++++ drivers/ws281x/Kconfig | 2 ++ makefiles/features_modules.inc.mk | 1 + 6 files changed, 35 insertions(+) create mode 100644 drivers/periph_common/Kconfig.gpio_ll diff --git a/cpu/nrf53/Kconfig b/cpu/nrf53/Kconfig index e86107a0529b..9d62961a3dc4 100644 --- a/cpu/nrf53/Kconfig +++ b/cpu/nrf53/Kconfig @@ -16,6 +16,7 @@ config CPU_FAM_NRF53 select HAS_PERIPH_GPIO select HAS_PERIPH_GPIO_IRQ select HAS_PERIPH_TIMER_PERIODIC + select HAS_PERIPH_TIMER_POLL select HAS_PERIPH_TIMER_QUERY_FREQS select HAS_PERIPH_UART_MODECFG select HAS_PERIPH_WDT diff --git a/cpu/nrf9160/Kconfig b/cpu/nrf9160/Kconfig index e90ad52ea331..990e74dd210b 100644 --- a/cpu/nrf9160/Kconfig +++ b/cpu/nrf9160/Kconfig @@ -17,6 +17,7 @@ config CPU_FAM_NRF9160 select HAS_PERIPH_GPIO_LL_IRQ select HAS_PERIPH_GPIO_LL_IRQ_UNMASK select HAS_PERIPH_TIMER_PERIODIC + select HAS_PERIPH_TIMER_POLL select HAS_PERIPH_TIMER_QUERY_FREQS select HAS_PERIPH_UART_MODECFG select HAS_PERIPH_SPI_GPIO_MODE diff --git a/drivers/periph_common/Kconfig b/drivers/periph_common/Kconfig index 564a6283add5..c9eae6d7e9a3 100644 --- a/drivers/periph_common/Kconfig +++ b/drivers/periph_common/Kconfig @@ -78,6 +78,8 @@ rsource "Kconfig.flashpage" rsource "Kconfig.gpio" +rsource "Kconfig.gpio_ll" + config MODULE_PERIPH_HWRNG bool "HWRNG peripheral driver" depends on HAS_PERIPH_HWRNG @@ -157,6 +159,12 @@ config MODULE_PERIPH_INIT_RTT default y if MODULE_PERIPH_INIT depends on MODULE_PERIPH_RTT +config MODULE_PERIPH_TIMER_POLL + bool "Timer poll" + depends on HAS_PERIPH_TIMER_POLL + help + Enables the timer_poll_channel function. + rsource "Kconfig.sdmmc" rsource "Kconfig.spi" diff --git a/drivers/periph_common/Kconfig.gpio_ll b/drivers/periph_common/Kconfig.gpio_ll new file mode 100644 index 000000000000..55e0b2263147 --- /dev/null +++ b/drivers/periph_common/Kconfig.gpio_ll @@ -0,0 +1,22 @@ +# Copyright (c) 2023 HAW Hamburg +# +# 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. +# + +menuconfig MODULE_PERIPH_GPIO_LL + bool "Low-level GPIO peripheral driver" + depends on HAS_PERIPH_GPIO_LL + +if MODULE_PERIPH_GPIO_LL + +config MODULE_PERIPH_GPIO_LL_IRQ_UNMASK + bool "Unmask GPIO peripheral interrupts" + default y + depends on HAS_PERIPH_GPIO_LL_IRQ_UNMASK + help + Enables GPIO peripheral unmasking interrupts without + clearing pending IRQs that came in while masked. + +endif # MODULE_PERIPH_GPIO_LL diff --git a/drivers/ws281x/Kconfig b/drivers/ws281x/Kconfig index 65b1241bb5f9..d8bda4366a15 100644 --- a/drivers/ws281x/Kconfig +++ b/drivers/ws281x/Kconfig @@ -49,7 +49,9 @@ config MODULE_WS281X_ESP32_SW config MODULE_WS281X_TIMER_GPIO_LL bool depends on HAS_PERIPH_TIMER_POLL + select MODULE_PERIPH_TIMER_POLL depends on HAS_PERIPH_GPIO_LL + select MODULE_PERIPH_GPIO_LL help Use a platform independent bit-banging software implementation to generate the RGB LED signal. diff --git a/makefiles/features_modules.inc.mk b/makefiles/features_modules.inc.mk index 77e344a8087f..c9b958c3af18 100644 --- a/makefiles/features_modules.inc.mk +++ b/makefiles/features_modules.inc.mk @@ -47,6 +47,7 @@ PERIPH_IGNORE_MODULES := \ periph_rtt_hw_rtc \ periph_rtt_hw_sys \ periph_spi_on_qspi \ + periph_timer_poll \ periph_timer_query_freqs \ periph_uart_collision \ periph_uart_rxstart_irq \ From 0d31432cb34893bba2804f9ef84a021baef99fa6 Mon Sep 17 00:00:00 2001 From: MrKevinWeiss Date: Wed, 13 Dec 2023 13:45:43 +0100 Subject: [PATCH 6/8] boards/nrf51: Defined TIMER_x_MAX_VALUE --- boards/common/nrf51/include/cfg_timer_01.h | 5 +++++ boards/common/nrf51/include/cfg_timer_012.h | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/boards/common/nrf51/include/cfg_timer_01.h b/boards/common/nrf51/include/cfg_timer_01.h index eabd5af07d0e..5c04d9e4ac88 100644 --- a/boards/common/nrf51/include/cfg_timer_01.h +++ b/boards/common/nrf51/include/cfg_timer_01.h @@ -49,6 +49,11 @@ static const timer_conf_t timer_config[] = { #define TIMER_0_ISR isr_timer0 #define TIMER_1_ISR isr_timer1 +/** See @ref timer_init */ +#define TIMER_0_MAX_VALUE 0xffffffff +/** See @ref timer_init */ +#define TIMER_1_MAX_VALUE 0xffffffff + #define TIMER_NUMOF ARRAY_SIZE(timer_config) /** @} */ diff --git a/boards/common/nrf51/include/cfg_timer_012.h b/boards/common/nrf51/include/cfg_timer_012.h index 985e0a9e510d..bf1770c238d8 100644 --- a/boards/common/nrf51/include/cfg_timer_012.h +++ b/boards/common/nrf51/include/cfg_timer_012.h @@ -56,6 +56,13 @@ static const timer_conf_t timer_config[] = { #define TIMER_1_ISR isr_timer1 #define TIMER_2_ISR isr_timer2 +/** See @ref timer_init */ +#define TIMER_0_MAX_VALUE 0xffffffff +/** See @ref timer_init */ +#define TIMER_1_MAX_VALUE 0xffffffff +/** See @ref timer_init */ +#define TIMER_2_MAX_VALUE 0xffffffff + #define TIMER_NUMOF ARRAY_SIZE(timer_config) /** @} */ From fb5ebea1a54195cd748081a47ca80ea6ad90ebd8 Mon Sep 17 00:00:00 2001 From: MrKevinWeiss Date: Wed, 13 Dec 2023 17:05:24 +0100 Subject: [PATCH 7/8] boards/common/e104-bt50xxa-tb: Override WS281X_TIMER_* --- boards/common/e104-bt50xxa-tb/include/board.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/boards/common/e104-bt50xxa-tb/include/board.h b/boards/common/e104-bt50xxa-tb/include/board.h index 058aadd64399..97ab45a0af2d 100644 --- a/boards/common/e104-bt50xxa-tb/include/board.h +++ b/boards/common/e104-bt50xxa-tb/include/board.h @@ -56,6 +56,14 @@ extern "C" { #define BTN1_MODE GPIO_IN_PU /** @} */ +/** + * @name WS281x RGB LED configuration + * @{ + */ +#define WS281X_TIMER_DEV TIMER_DEV(1) /**< Timer device */ +#define WS281X_TIMER_MAX_VALUE TIMER_1_MAX_VALUE /**< Timer max value */ +/** @} */ + #ifdef __cplusplus } #endif From 6a0b1c59c52da93b027dd7f63dc6d04f0db3a52e Mon Sep 17 00:00:00 2001 From: MrKevinWeiss Date: Mon, 18 Dec 2023 10:19:25 +0100 Subject: [PATCH 8/8] boards/nrf9160dk: Override WS281X_TIMER_* --- boards/nrf9160dk/include/board.h | 8 ++++++++ boards/nrf9160dk/include/periph_conf.h | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/boards/nrf9160dk/include/board.h b/boards/nrf9160dk/include/board.h index 8010a4deeb45..b94fd399842a 100644 --- a/boards/nrf9160dk/include/board.h +++ b/boards/nrf9160dk/include/board.h @@ -87,6 +87,14 @@ extern "C" { #define BTN3_MODE GPIO_IN /**< BTN3 default mode */ /** @} */ +/** + * @name WS281x RGB LED configuration + * @{ + */ +#define WS281X_TIMER_DEV TIMER_DEV(1) /**< Timer device */ +#define WS281X_TIMER_MAX_VALUE TIMER_1_MAX_VALUE /**< Timer max value */ +/** @} */ + #ifdef __cplusplus } #endif diff --git a/boards/nrf9160dk/include/periph_conf.h b/boards/nrf9160dk/include/periph_conf.h index cdca44782f2c..b157bf368b90 100644 --- a/boards/nrf9160dk/include/periph_conf.h +++ b/boards/nrf9160dk/include/periph_conf.h @@ -83,6 +83,11 @@ static const timer_conf_t timer_config[] = { #define TIMER_0_ISR isr_timer0 /**< Timer0 IRQ*/ #define TIMER_1_ISR isr_timer1 /**< Timer1 IRQ */ +/** See @ref timer_init */ +#define TIMER_0_MAX_VALUE 0xffffffff +/** See @ref timer_init */ +#define TIMER_1_MAX_VALUE 0xffffffff + #define TIMER_NUMOF ARRAY_SIZE(timer_config) /**< Timer configuration NUMOF */ /** @} */