From 76921a63db98a927bdbfc3ef40cc32d2080b307c Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 26 Jan 2024 18:35:59 +0100 Subject: [PATCH 1/4] drivers/ws281x: Added STM32 support Currently only implemented for 84, 100 and 180 MHz MCUs of the STM32F4 family. Timings were verfied with a Saleae Logic Analyzer at 500 MS/s. --- drivers/ws281x/Makefile.dep | 5 +- drivers/ws281x/include/ws281x_backend.h | 11 + drivers/ws281x/stm32.c | 1021 +++++++++++++++++++++++ 3 files changed, 1036 insertions(+), 1 deletion(-) create mode 100644 drivers/ws281x/stm32.c diff --git a/drivers/ws281x/Makefile.dep b/drivers/ws281x/Makefile.dep index f539c4de83d7..e76827c45b35 100644 --- a/drivers/ws281x/Makefile.dep +++ b/drivers/ws281x/Makefile.dep @@ -1,5 +1,5 @@ # 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 +FEATURES_REQUIRED_ANY += cpu_core_atmega|arch_esp32|arch_native|periph_timer_poll|cpu_stm32f4 ifeq (,$(filter ws281x_%,$(USEMODULE))) ifneq (,$(filter cpu_core_atmega,$(FEATURES_USED))) @@ -11,6 +11,9 @@ ifeq (,$(filter ws281x_%,$(USEMODULE))) ifneq (,$(filter arch_esp32,$(FEATURES_USED))) USEMODULE += ws281x_esp32 endif + ifneq (,$(filter cpu_stm32f4,$(FEATURES_USED))) + USEMODULE += ws281x_stm32 + 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 diff --git a/drivers/ws281x/include/ws281x_backend.h b/drivers/ws281x/include/ws281x_backend.h index 01f74fb87789..981475bfbb54 100644 --- a/drivers/ws281x/include/ws281x_backend.h +++ b/drivers/ws281x/include/ws281x_backend.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2019 Marian Buschsieweke + * 2024 Lennart Lutz * * 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 @@ -14,6 +15,7 @@ * @brief Backend configuration for WS2812/SK6812 RGB LEDs * * @author Marian Buschsieweke + * @author Lennart Lutz */ #ifndef WS281X_BACKEND_H @@ -41,6 +43,15 @@ extern "C" { #endif /** @} */ +/** + * @name Properties of the STM32 backend. + * @{ + */ +#ifdef MODULE_WS281X_STM32 +#define WS281X_HAVE_INIT (1) +#endif +/** @} */ + /** * @name Properties of the VT100 terminal backend. * @{ diff --git a/drivers/ws281x/stm32.c b/drivers/ws281x/stm32.c new file mode 100644 index 000000000000..ad6153d6a44a --- /dev/null +++ b/drivers/ws281x/stm32.c @@ -0,0 +1,1021 @@ +/* + * Copyright 2024 Lennart Lutz + * + * 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 `ws281x_write_buffer()` for STM32 MCUs + * + * @note Currently only implemented for 84, 100 and 180 MHz MCUs of the + * STM32F4 family. + * + * @author Lennart Lutz + * + * @} + */ + +#include +#include +#include +#include + +#include "ws281x.h" +#include "ws281x_params.h" +#include "ws281x_constants.h" +#include "periph_cpu.h" +#include "periph/gpio.h" + +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; + + /** + * Get the address of the GPIO pin. + */ + uint32_t gpio_adr = (dev->params.pin & 0xFFFFFF00) + 0x14; + + /** + * The pin number is made up of the first eight LSB bits of the pin address. + */ + uint8_t pin_num = (uint8_t) dev->params.pin & 0xFF; + +#if defined(CPU_LINE_STM32F401xC) || defined(CPU_LINE_STM32F401xE) + + while (pos < end) + { + uint8_t cnt = 8; + uint8_t data = *pos; + while (cnt > 0) { + if (data & 0b10000000) + { + // One + __asm__ volatile ( + "LDR R1, [%[gpio_adr]] \n" // Load the current value + "EOR R1, R1, %[pin_mask] \n" // Set the specified pin + "STR R1, [%[gpio_adr]] \n" // Store the updated value + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "EOR R1, R1, %[pin_mask] \n" + "STR R1, [%[gpio_adr]] \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + : + : [gpio_adr] "r" (gpio_adr), [pin_mask] "r" (1U << pin_num) + : "r1" + ); + } + else + { + // Zero + __asm__ volatile ( + "LDR R1, [%[gpio_adr]] \n" // Load the current value + "EOR R1, R1, %[pin_mask] \n" // Set the specified pin + "STR R1, [%[gpio_adr]] \n" // Store the updated value + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "EOR R1, R1, %[pin_mask] \n" + "STR R1, [%[gpio_adr]] \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + : + : [gpio_adr] "r" (gpio_adr), [pin_mask] "r" (1U << pin_num) + : "r1" + ); + } + cnt--; + data = data << 1; + } + pos++; + } + +#elif defined(CPU_LINE_STM32F410Cx) || defined(CPU_LINE_STM32F410Rx) || \ + defined(CPU_LINE_STM32F410Tx) || defined(CPU_LINE_STM32F411xE) || \ + defined(CPU_LINE_STM32F412Cx) || defined(CPU_LINE_STM32F412Rx) || \ + defined(CPU_LINE_STM32F412Vx) || defined(CPU_LINE_STM32F412Zx) || \ + defined(CPU_LINE_STM32F413xx) || defined(CPU_LINE_STM32F423xx) + + while (pos < end) + { + uint8_t cnt = 8; + uint8_t data = *pos; + while (cnt > 0) { + if (data & 0b10000000) + { + // One + __asm__ volatile ( + "LDR R1, [%[gpio_adr]] \n" // Load the current value + "EOR R1, R1, %[pin_mask] \n" // Set the specified pin + "STR R1, [%[gpio_adr]] \n" // Store the updated value + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "EOR R1, R1, %[pin_mask] \n" + "STR R1, [%[gpio_adr]] \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + : + : [gpio_adr] "r" (gpio_adr), [pin_mask] "r" (1U << pin_num) + : "r1" + ); + } + else + { + // Zero + __asm__ volatile ( + "LDR R1, [%[gpio_adr]] \n" // Load the current value + "EOR R1, R1, %[pin_mask] \n" // Set the specified pin + "STR R1, [%[gpio_adr]] \n" // Store the updated value + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "EOR R1, R1, %[pin_mask] \n" + "STR R1, [%[gpio_adr]] \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + : + : [gpio_adr] "r" (gpio_adr), [pin_mask] "r" (1U << pin_num) + : "r1" + ); + } + cnt--; + data = data << 1; + } + pos++; + } + +#elif defined(CPU_LINE_STM32F427xx) || defined(CPU_LINE_STM32F437xx) || \ + defined(CPU_LINE_STM32F429xx) || defined(CPU_LINE_STM32F439xx) || \ + defined(CPU_LINE_STM32F446xx) || defined(CPU_LINE_STM32F469xx) || \ + defined(CPU_LINE_STM32F479xx) + + while (pos < end) + { + uint8_t cnt = 8; + uint8_t data = *pos; + while (cnt > 0) { + if (data & 0b10000000) + { + // One + __asm__ volatile ( + "LDR R1, [%[gpio_adr]] \n" // Load the current value + "EOR R1, R1, %[pin_mask] \n" // Set the specified pin + "STR R1, [%[gpio_adr]] \n" // Store the updated value + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "EOR R1, R1, %[pin_mask] \n" + "STR R1, [%[gpio_adr]] \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + : + : [gpio_adr] "r" (gpio_adr), [pin_mask] "r" (1U << pin_num) + : "r1" + ); + } + else + { + // Zero + __asm__ volatile ( + "LDR R1, [%[gpio_adr]] \n" // Load the current value + "EOR R1, R1, %[pin_mask] \n" // Set the specified pin + "STR R1, [%[gpio_adr]] \n" // Store the updated value + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "EOR R1, R1, %[pin_mask] \n" + "STR R1, [%[gpio_adr]] \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + "NOP \n" + : + : [gpio_adr] "r" (gpio_adr), [pin_mask] "r" (1U << pin_num) + : "r1" + ); + } + cnt--; + data = data << 1; + } + pos++; + } + +#else + + (void) pos; + (void) end; + (void) gpio_adr; + (void) pin_num; + +#error "ws281x: No implementation for current cpu model." + +#endif + +} + +int ws281x_init(ws281x_t *dev, const ws281x_params_t *params) +{ + if (!dev || !params || !params->buf) { + return -EINVAL; + } + + memset(dev, 0, sizeof(ws281x_t)); + dev->params = *params; + + if (gpio_init(dev->params.pin, GPIO_OUT)) { + return -EIO; + } + + return 0; +} From 23cc5311ac5f47003f76389124d3d734e0b3f6ba Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 26 Jan 2024 18:37:04 +0100 Subject: [PATCH 2/4] drivers/include: Added comments for the implemented STM32 support --- drivers/include/ws281x.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/include/ws281x.h b/drivers/include/ws281x.h index fe448ee19804..2809f8e8cb81 100644 --- a/drivers/include/ws281x.h +++ b/drivers/include/ws281x.h @@ -41,6 +41,15 @@ * * The ESP32 implementation is frequency independent, as frequencies above 80MHz * are high enough to support bit banging without assembly. + * + * ## STM32 + * + * The STM32 implementation is frequency dependent and currently only supported + * for 84, 100 and 180 MHz MCUs of the STM32f4 family. Support for the STM32f7 + * family is coming soon... The number of NOPs required was determined using a + * saleae logic analyzer. + * + * @warning Since this is bit banged an interrupt can destroy the timing. * * ## Native/VT100 * @@ -69,6 +78,11 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Makefile * USEMODULE += ws281x_esp32 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * * the STM32 backend: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Makefile + * USEMODULE += ws281x_stm32 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * * the native/VT100 backend: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Makefile From 31ee764e8e489078ea1a19092d0bb36874c58bf0 Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 26 Jan 2024 18:49:17 +0100 Subject: [PATCH 3/4] drivers/include: Removed trailing whitespaces --- drivers/include/ws281x.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/include/ws281x.h b/drivers/include/ws281x.h index 2809f8e8cb81..6e82c1e77357 100644 --- a/drivers/include/ws281x.h +++ b/drivers/include/ws281x.h @@ -41,14 +41,14 @@ * * The ESP32 implementation is frequency independent, as frequencies above 80MHz * are high enough to support bit banging without assembly. - * + * * ## STM32 - * + * * The STM32 implementation is frequency dependent and currently only supported * for 84, 100 and 180 MHz MCUs of the STM32f4 family. Support for the STM32f7 - * family is coming soon... The number of NOPs required was determined using a + * family is coming soon... The number of NOPs required was determined using a * saleae logic analyzer. - * + * * @warning Since this is bit banged an interrupt can destroy the timing. * * ## Native/VT100 @@ -78,7 +78,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Makefile * USEMODULE += ws281x_esp32 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * * the STM32 backend: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Makefile * USEMODULE += ws281x_stm32 From 92d40e3adc4403ed7ff9e8b8544a06a95a0cb2a1 Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 26 Jan 2024 18:49:30 +0100 Subject: [PATCH 4/4] drivers/ws281x: Removed trailing whitespaces --- drivers/ws281x/stm32.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/ws281x/stm32.c b/drivers/ws281x/stm32.c index ad6153d6a44a..d321e2bd8ca9 100644 --- a/drivers/ws281x/stm32.c +++ b/drivers/ws281x/stm32.c @@ -13,8 +13,8 @@ * * @file * @brief Implementation of `ws281x_write_buffer()` for STM32 MCUs - * - * @note Currently only implemented for 84, 100 and 180 MHz MCUs of the + * + * @note Currently only implemented for 84, 100 and 180 MHz MCUs of the * STM32F4 family. * * @author Lennart Lutz @@ -38,7 +38,7 @@ 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; - + /** * Get the address of the GPIO pin. */ @@ -51,7 +51,7 @@ void ws281x_write_buffer(ws281x_t *dev, const void *buf, size_t size) #if defined(CPU_LINE_STM32F401xC) || defined(CPU_LINE_STM32F401xE) - while (pos < end) + while (pos < end) { uint8_t cnt = 8; uint8_t data = *pos; @@ -276,7 +276,7 @@ void ws281x_write_buffer(ws281x_t *dev, const void *buf, size_t size) defined(CPU_LINE_STM32F412Vx) || defined(CPU_LINE_STM32F412Zx) || \ defined(CPU_LINE_STM32F413xx) || defined(CPU_LINE_STM32F423xx) - while (pos < end) + while (pos < end) { uint8_t cnt = 8; uint8_t data = *pos; @@ -539,7 +539,7 @@ void ws281x_write_buffer(ws281x_t *dev, const void *buf, size_t size) defined(CPU_LINE_STM32F446xx) || defined(CPU_LINE_STM32F469xx) || \ defined(CPU_LINE_STM32F479xx) - while (pos < end) + while (pos < end) { uint8_t cnt = 8; uint8_t data = *pos;