diff options
Diffstat (limited to 'platforms/chibios/drivers')
19 files changed, 383 insertions, 28 deletions
diff --git a/platforms/chibios/drivers/audio_dac_additive.c b/platforms/chibios/drivers/audio_dac_additive.c index d29147ca3b..9a5fe4c336 100644 --- a/platforms/chibios/drivers/audio_dac_additive.c +++ b/platforms/chibios/drivers/audio_dac_additive.c @@ -16,8 +16,9 @@ */ #include "audio.h" -#include <ch.h> -#include <hal.h> +#include "gpio.h" +#include <math.h> +#include "util.h" // Need to disable GCC's "tautological-compare" warning for this file, as it causes issues when running `KEEP_INTERMEDIATES=yes`. Corresponding pop at the end of the file. #pragma GCC diagnostic push diff --git a/platforms/chibios/drivers/audio_dac_basic.c b/platforms/chibios/drivers/audio_dac_basic.c index 4ea23a2158..9a3f3fea1f 100644 --- a/platforms/chibios/drivers/audio_dac_basic.c +++ b/platforms/chibios/drivers/audio_dac_basic.c @@ -16,8 +16,7 @@ */ #include "audio.h" -#include "ch.h" -#include "hal.h" +#include "gpio.h" // Need to disable GCC's "tautological-compare" warning for this file, as it causes issues when running `KEEP_INTERMEDIATES=yes`. Corresponding pop at the end of the file. #pragma GCC diagnostic push diff --git a/platforms/chibios/drivers/audio_pwm_hardware.c b/platforms/chibios/drivers/audio_pwm_hardware.c index 54dac46605..40d891326f 100644 --- a/platforms/chibios/drivers/audio_pwm_hardware.c +++ b/platforms/chibios/drivers/audio_pwm_hardware.c @@ -12,8 +12,7 @@ // function. #include "audio.h" -#include "ch.h" -#include "hal.h" +#include "gpio.h" #if !defined(AUDIO_PIN) # error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings" diff --git a/platforms/chibios/drivers/audio_pwm_software.c b/platforms/chibios/drivers/audio_pwm_software.c index e01f86ea52..663a9eca16 100644 --- a/platforms/chibios/drivers/audio_pwm_software.c +++ b/platforms/chibios/drivers/audio_pwm_software.c @@ -25,8 +25,7 @@ this driver uses the chibios-PWM system to produce a square-wave on any given ou */ #include "audio.h" -#include "ch.h" -#include "hal.h" +#include "gpio.h" #if !defined(AUDIO_PIN) # error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings" diff --git a/platforms/chibios/drivers/backlight_pwm.c b/platforms/chibios/drivers/backlight_pwm.c new file mode 100644 index 0000000000..01e6f71307 --- /dev/null +++ b/platforms/chibios/drivers/backlight_pwm.c @@ -0,0 +1,173 @@ +#include "backlight.h" +#include "gpio.h" +#include "wait.h" +#include <hal.h> + +// Maximum duty cycle limit +#ifndef BACKLIGHT_LIMIT_VAL +# define BACKLIGHT_LIMIT_VAL 255 +#endif + +#ifndef BACKLIGHT_PAL_MODE +# if defined(USE_GPIOV1) +# define BACKLIGHT_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL +# else +// GPIOV2 && GPIOV3 +# define BACKLIGHT_PAL_MODE 2 +# endif +#endif + +// GENERIC +#ifndef BACKLIGHT_PWM_DRIVER +# define BACKLIGHT_PWM_DRIVER PWMD4 +#endif +#ifndef BACKLIGHT_PWM_CHANNEL +# define BACKLIGHT_PWM_CHANNEL 3 +#endif + +// Support for pins which are on TIM1_CH1N - requires STM32_PWM_USE_ADVANCED +#ifdef BACKLIGHT_PWM_COMPLEMENTARY_OUTPUT +# if BACKLIGHT_ON_STATE == 1 +# define PWM_OUTPUT_MODE PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW; +# else +# define PWM_OUTPUT_MODE PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH; +# endif +#else +# if BACKLIGHT_ON_STATE == 1 +# define PWM_OUTPUT_MODE PWM_OUTPUT_ACTIVE_HIGH; +# else +# define PWM_OUTPUT_MODE PWM_OUTPUT_ACTIVE_LOW; +# endif +#endif + +#ifndef BACKLIGHT_PWM_COUNTER_FREQUENCY +# define BACKLIGHT_PWM_COUNTER_FREQUENCY 0xFFFF +#endif + +#ifndef BACKLIGHT_PWM_PERIOD +# define BACKLIGHT_PWM_PERIOD 256 +#endif + +static PWMConfig pwmCFG = { + .frequency = BACKLIGHT_PWM_COUNTER_FREQUENCY, /* PWM clock frequency */ + .period = BACKLIGHT_PWM_PERIOD, /* PWM period in counter ticks. e.g. clock frequency is 10KHz, period is 256 ticks then t_period is 25.6ms */ +}; + +#ifdef BACKLIGHT_BREATHING +static virtual_timer_t breathing_vt; +#endif + +// See http://jared.geek.nz/2013/feb/linear-led-pwm +static uint16_t cie_lightness(uint16_t v) { + if (v <= 5243) // if below 8% of max + return v / 9; // same as dividing by 900% + else { + uint32_t y = (((uint32_t)v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare + // to get a useful result with integer division, we shift left in the expression above + // and revert what we've done again after squaring. + y = y * y * y >> 8; + if (y > 0xFFFFUL) { // prevent overflow + return 0xFFFFU; + } else { + return (uint16_t)y; + } + } +} + +static uint32_t rescale_limit_val(uint32_t val) { + // rescale the supplied backlight value to be in terms of the value limit + return (val * (BACKLIGHT_LIMIT_VAL + 1)) / 256; +} + +void backlight_init_ports(void) { +#ifdef USE_GPIOV1 + palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), BACKLIGHT_PAL_MODE); +#else + palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), PAL_MODE_ALTERNATE(BACKLIGHT_PAL_MODE)); +#endif + + pwmCFG.channels[BACKLIGHT_PWM_CHANNEL - 1].mode = PWM_OUTPUT_MODE; + pwmStart(&BACKLIGHT_PWM_DRIVER, &pwmCFG); + + backlight_set(get_backlight_level()); + +#ifdef BACKLIGHT_BREATHING + chVTObjectInit(&breathing_vt); + if (is_backlight_breathing()) { + breathing_enable(); + } +#endif +} + +void backlight_set(uint8_t level) { + if (level > BACKLIGHT_LEVELS) { + level = BACKLIGHT_LEVELS; + } + + if (level == 0) { + // Turn backlight off + pwmDisableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1); + } else { + // Turn backlight on + uint32_t duty = (uint32_t)(cie_lightness(rescale_limit_val(0xFFFF * (uint32_t)level / BACKLIGHT_LEVELS))); + pwmEnableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty)); + } +} + +void backlight_task(void) {} + +#ifdef BACKLIGHT_BREATHING + +# define BREATHING_STEPS 128 + +/* To generate breathing curve in python: + * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)] + */ +static const uint8_t breathing_table[BREATHING_STEPS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static void breathing_callback(virtual_timer_t *vtp, void *p); + +bool is_breathing(void) { + return chVTIsArmed(&breathing_vt); +} + +void breathing_enable(void) { + /* Update frequency is 256Hz -> 3906us intervals */ + chVTSetContinuous(&breathing_vt, TIME_US2I(3906), breathing_callback, NULL); +} + +void breathing_disable(void) { + chVTReset(&breathing_vt); + + // Restore backlight level + backlight_set(get_backlight_level()); +} + +// Use this before the cie_lightness function. +static inline uint16_t scale_backlight(uint16_t v) { + return v / BACKLIGHT_LEVELS * get_backlight_level(); +} + +static void breathing_callback(virtual_timer_t *vtp, void *p) { + uint8_t breathing_period = get_breathing_period(); + uint16_t interval = (uint16_t)breathing_period * 256 / BREATHING_STEPS; + + // resetting after one period to prevent ugly reset at overflow. + static uint16_t breathing_counter = 0; + breathing_counter = (breathing_counter + 1) % (breathing_period * 256); + uint8_t index = breathing_counter / interval % BREATHING_STEPS; + uint32_t duty = cie_lightness(rescale_limit_val(scale_backlight(breathing_table[index] * 256))); + + chSysLockFromISR(); + pwmEnableChannelI(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty)); + chSysUnlockFromISR(); +} + +// TODO: integrate generic pulse solution +void breathing_pulse(void) { + backlight_set(is_backlight_enabled() ? 0 : BACKLIGHT_LEVELS); + wait_ms(10); + backlight_set(is_backlight_enabled() ? get_backlight_level() : 0); +} + +#endif diff --git a/platforms/chibios/drivers/backlight_timer.c b/platforms/chibios/drivers/backlight_timer.c new file mode 100644 index 0000000000..0fabee93b1 --- /dev/null +++ b/platforms/chibios/drivers/backlight_timer.c @@ -0,0 +1,178 @@ +#include "backlight.h" +#include "backlight_driver_common.h" +#include "wait.h" + +#ifndef BACKLIGHT_GPT_DRIVER +# define BACKLIGHT_GPT_DRIVER GPTD15 +#endif + +// Platform specific implementations +static void backlight_timer_configure(bool enable); +static void backlight_timer_set_duty(uint16_t duty); +static uint16_t backlight_timer_get_duty(void); + +// See http://jared.geek.nz/2013/feb/linear-led-pwm +static uint16_t cie_lightness(uint16_t v) { + if (v <= 5243) // if below 8% of max + return v / 9; // same as dividing by 900% + else { + uint32_t y = (((uint32_t)v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare + // to get a useful result with integer division, we shift left in the expression above + // and revert what we've done again after squaring. + y = y * y * y >> 8; + if (y > 0xFFFFUL) // prevent overflow + return 0xFFFFU; + else + return (uint16_t)y; + } +} + +void backlight_init_ports(void) { + backlight_pins_init(); + + backlight_set(get_backlight_level()); + +#ifdef BACKLIGHT_BREATHING + if (is_backlight_breathing()) { + breathing_enable(); + } +#endif +} + +void backlight_set(uint8_t level) { + if (level > BACKLIGHT_LEVELS) level = BACKLIGHT_LEVELS; + + backlight_pins_off(); + + backlight_timer_set_duty(cie_lightness(0xFFFFU / BACKLIGHT_LEVELS * level)); + backlight_timer_configure(level != 0); +} + +static void backlight_timer_top(void) { +#ifdef BACKLIGHT_BREATHING + if (is_breathing()) { + breathing_task(); + } +#endif + + if (backlight_timer_get_duty() > 256) { + backlight_pins_on(); + } +} + +static void backlight_timer_cmp(void) { + backlight_pins_off(); +} + +void backlight_task(void) {} + +#ifdef BACKLIGHT_BREATHING +# define BREATHING_STEPS 128 + +static bool breathing = false; +static uint16_t breathing_counter = 0; + +/* To generate breathing curve in python: + * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)] + */ +static const uint8_t breathing_table[BREATHING_STEPS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +// Use this before the cie_lightness function. +static inline uint16_t scale_backlight(uint16_t v) { + return v / BACKLIGHT_LEVELS * get_backlight_level(); +} + +void breathing_task(void) { + uint8_t breathing_period = get_breathing_period(); + uint16_t interval = (uint16_t)breathing_period * 256 / BREATHING_STEPS; + // resetting after one period to prevent ugly reset at overflow. + breathing_counter = (breathing_counter + 1) % (breathing_period * 256); + uint8_t index = breathing_counter / interval % BREATHING_STEPS; + + // printf("index:%u\n", index); + + backlight_timer_set_duty(cie_lightness(scale_backlight((uint16_t)breathing_table[index] * 256))); +} + +bool is_breathing(void) { + return breathing; +} + +void breathing_enable(void) { + breathing_counter = 0; + breathing = true; +} +void breathing_disable(void) { + breathing = false; +} + +void breathing_pulse(void) { + backlight_set(is_backlight_enabled() ? 0 : BACKLIGHT_LEVELS); + wait_ms(10); + backlight_set(is_backlight_enabled() ? get_backlight_level() : 0); +} +#endif + +#ifdef PROTOCOL_CHIBIOS +// On Platforms where timers fire every tick and have no capture/top events +// - fake event in the normal timer callback +uint16_t s_duty = 0; + +static void timerCallback(void) { + /* Software PWM + * timer:1111 1111 1111 1111 + * \______/| \_______/____ count(0-255) + * \ \______________ unused(1) + * \__________________ index of step table(0-127) + */ + + // this works for cca 65536 irqs/sec + static union { + uint16_t raw; + struct { + uint16_t count : 8; + uint8_t dummy : 1; + uint8_t index : 7; + } pwm; + } timer = {.raw = 0}; + + timer.raw++; + + if (timer.pwm.count == 0) { + // LED on + backlight_timer_top(); + } else if (timer.pwm.count == (s_duty / 256)) { + // LED off + backlight_timer_cmp(); + } +} + +static void backlight_timer_set_duty(uint16_t duty) { + s_duty = duty; +} +static uint16_t backlight_timer_get_duty(void) { + return s_duty; +} + +// ChibiOS - Map GPT timer onto Software PWM +static void gptTimerCallback(GPTDriver *gptp) { + (void)gptp; + timerCallback(); +} + +static void backlight_timer_configure(bool enable) { + static const GPTConfig gptcfg = {1000000, gptTimerCallback, 0, 0}; + + static bool s_init = false; + if (!s_init) { + gptStart(&BACKLIGHT_GPT_DRIVER, &gptcfg); + s_init = true; + } + + if (enable) { + gptStartContinuous(&BACKLIGHT_GPT_DRIVER, gptcfg.frequency / 0xFFFF); + } else { + gptStopTimer(&BACKLIGHT_GPT_DRIVER); + } +} +#endif diff --git a/platforms/chibios/drivers/i2c_master.c b/platforms/chibios/drivers/i2c_master.c index 4c7a5daa17..7c49f9d005 100644 --- a/platforms/chibios/drivers/i2c_master.c +++ b/platforms/chibios/drivers/i2c_master.c @@ -24,8 +24,10 @@ * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. Pins B6 and B7 are used * but using any other I2C pins should be trivial. */ -#include "quantum.h" + #include "i2c_master.h" +#include "gpio.h" +#include "chibios_config.h" #include <string.h> #include <ch.h> #include <hal.h> diff --git a/platforms/chibios/drivers/serial.c b/platforms/chibios/drivers/serial.c index 0dd8e71ae8..f087d0c2ed 100644 --- a/platforms/chibios/drivers/serial.c +++ b/platforms/chibios/drivers/serial.c @@ -2,8 +2,8 @@ * WARNING: be careful changing this code, it is very timing dependent */ -#include "quantum.h" #include "serial.h" +#include "gpio.h" #include "wait.h" #include "synchronization_util.h" diff --git a/platforms/chibios/drivers/serial_protocol.c b/platforms/chibios/drivers/serial_protocol.c index ccaf73282d..e0f583ccde 100644 --- a/platforms/chibios/drivers/serial_protocol.c +++ b/platforms/chibios/drivers/serial_protocol.c @@ -3,10 +3,8 @@ #include <ch.h> -#include "quantum.h" #include "serial.h" #include "serial_protocol.h" -#include "printf.h" #include "synchronization_util.h" static inline bool initiate_transaction(uint8_t transaction_id); diff --git a/platforms/chibios/drivers/serial_usart.c b/platforms/chibios/drivers/serial_usart.c index 6ebbf7c8ca..767ef8726f 100644 --- a/platforms/chibios/drivers/serial_usart.c +++ b/platforms/chibios/drivers/serial_usart.c @@ -5,6 +5,7 @@ #include "serial_usart.h" #include "serial_protocol.h" #include "synchronization_util.h" +#include "chibios_config.h" #if defined(SERIAL_USART_CONFIG) static QMKSerialConfig serial_config = SERIAL_USART_CONFIG; diff --git a/platforms/chibios/drivers/serial_usart.h b/platforms/chibios/drivers/serial_usart.h index fa062cd736..dec8a292e9 100644 --- a/platforms/chibios/drivers/serial_usart.h +++ b/platforms/chibios/drivers/serial_usart.h @@ -3,7 +3,6 @@ #pragma once -#include "quantum.h" #include "serial.h" #include <hal.h> diff --git a/platforms/chibios/drivers/uart.c b/platforms/chibios/drivers/uart.c index 34f77232b6..39a59dd445 100644 --- a/platforms/chibios/drivers/uart.c +++ b/platforms/chibios/drivers/uart.c @@ -16,8 +16,6 @@ #include "uart.h" -#include "quantum.h" - #if defined(MCU_KINETIS) static SerialConfig serialConfig = {SERIAL_DEFAULT_BITRATE}; #elif defined(WB32F3G71xx) || defined(WB32FQ95xx) diff --git a/platforms/chibios/drivers/uart.h b/platforms/chibios/drivers/uart.h index e9e3b0855b..d1917bfc80 100644 --- a/platforms/chibios/drivers/uart.h +++ b/platforms/chibios/drivers/uart.h @@ -17,6 +17,7 @@ #pragma once #include <stdint.h> +#include <stdbool.h> #include <hal.h> diff --git a/platforms/chibios/drivers/vendor/RP/RP2040/ps2_vendor.c b/platforms/chibios/drivers/vendor/RP/RP2040/ps2_vendor.c index 937fa5de6f..d775ec29d5 100644 --- a/platforms/chibios/drivers/vendor/RP/RP2040/ps2_vendor.c +++ b/platforms/chibios/drivers/vendor/RP/RP2040/ps2_vendor.c @@ -1,12 +1,10 @@ // Copyright 2022 Marek Kraus (@gamelaster) // SPDX-License-Identifier: GPL-2.0-or-later -#include "quantum.h" - #include "hardware/pio.h" #include "hardware/clocks.h" #include "ps2.h" -#include "print.h" +#include "debug.h" #if !defined(MCU_RP) # error PIO Driver is only available for Raspberry Pi 2040 MCUs! diff --git a/platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c b/platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c index dd4723a086..3aa8e1165f 100644 --- a/platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c +++ b/platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c @@ -1,11 +1,12 @@ // Copyright 2022 Stefan Kerkmann // SPDX-License-Identifier: GPL-2.0-or-later -#include "quantum.h" #include "serial_usart.h" #include "serial_protocol.h" #include "hardware/pio.h" #include "hardware/clocks.h" +#include "wait.h" +#include "debug.h" #if !defined(MCU_RP) # error PIO Driver is only available for Raspberry Pi 2040 MCUs! diff --git a/platforms/chibios/drivers/vendor/RP/RP2040/ws2812_vendor.c b/platforms/chibios/drivers/vendor/RP/RP2040/ws2812_vendor.c index 99a6cfaba9..8d59e13bb2 100644 --- a/platforms/chibios/drivers/vendor/RP/RP2040/ws2812_vendor.c +++ b/platforms/chibios/drivers/vendor/RP/RP2040/ws2812_vendor.c @@ -2,13 +2,19 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "ws2812.h" -#include "hardware/timer.h" -#include "hardware/clocks.h" + // Keep this exact include order otherwise we run into naming conflicts between // pico-sdk and rp2040.h which we don't control. -#include "quantum.h" +#include "hardware/timer.h" +#include "hardware/clocks.h" +#include <hal.h> #include "hardware/pio.h" +#include "gpio.h" +#include "debug.h" +#include "wait.h" +#include "util.h" + #if !defined(MCU_RP) # error PIO Driver is only available for Raspberry Pi 2040 MCUs! #endif diff --git a/platforms/chibios/drivers/ws2812_bitbang.c b/platforms/chibios/drivers/ws2812_bitbang.c index d05deb1a50..c55e0f654c 100644 --- a/platforms/chibios/drivers/ws2812_bitbang.c +++ b/platforms/chibios/drivers/ws2812_bitbang.c @@ -1,7 +1,7 @@ -#include "quantum.h" #include "ws2812.h" -#include <ch.h> -#include <hal.h> + +#include "gpio.h" +#include "chibios_config.h" /* Adapted from https://github.com/bigjosh/SimpleNeoPixelDemo/ */ diff --git a/platforms/chibios/drivers/ws2812_pwm.c b/platforms/chibios/drivers/ws2812_pwm.c index 04c8279a97..cfee547a82 100644 --- a/platforms/chibios/drivers/ws2812_pwm.c +++ b/platforms/chibios/drivers/ws2812_pwm.c @@ -1,6 +1,6 @@ #include "ws2812.h" -#include "quantum.h" -#include <hal.h> +#include "gpio.h" +#include "chibios_config.h" /* Adapted from https://github.com/joewa/WS2812-LED-Driver_ChibiOS/ */ diff --git a/platforms/chibios/drivers/ws2812_spi.c b/platforms/chibios/drivers/ws2812_spi.c index c28f5007f1..f188576e04 100644 --- a/platforms/chibios/drivers/ws2812_spi.c +++ b/platforms/chibios/drivers/ws2812_spi.c @@ -1,5 +1,7 @@ -#include "quantum.h" #include "ws2812.h" +#include "gpio.h" +#include "util.h" +#include "chibios_config.h" /* Adapted from https://github.com/gamazeps/ws2812b-chibios-SPIDMA/ */ |