From 47892d4420bae07ad780df1ba8d700d03a792ee3 Mon Sep 17 00:00:00 2001 From: Xelus22 <17491233+Xelus22@users.noreply.github.com> Date: Fri, 19 Mar 2021 08:29:18 +0000 Subject: [PATCH] ARM WS2812 SPI config (baudrate and circular buffer) (#12216) * initial commit * include circular buffer command * add endif * circular buffer mode * remove untrue comment * revamp and add documentation * do not allow WS2812_SPI_SYNC & CIRCULAR_BUFFER --- docs/ws2812_driver.md | 19 ++++++++++++++++ drivers/chibios/ws2812_spi.c | 44 ++++++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/docs/ws2812_driver.md b/docs/ws2812_driver.md index fa14f02fdbc0..e69400364cb9 100644 --- a/docs/ws2812_driver.md +++ b/docs/ws2812_driver.md @@ -77,6 +77,25 @@ Configure the hardware via your config.h: You must also turn on the SPI feature in your halconf.h and mcuconf.h +#### Circular Buffer Mode +Some boards may flicker while in the normal buffer mode. To fix this issue, circular buffer mode may be used to rectify the issue. + +By default, the circular buffer mode is disabled. + +To enable this alternative buffer mode, place this into your `config.h` file: +```c +#define WS2812_SPI_USE_CIRCULAR_BUFFER +``` + +#### Setting baudrate with divisor +To adjust the baudrate at which the SPI peripheral is configured, users will need to derive the target baudrate from the clock tree provided by STM32CubeMX. + +Only divisors of 2, 4, 8, 16, 32, 64, 128 and 256 are supported by hardware. + +|Define |Default|Description | +|--------------------|-------|-------------------------------------| +|`WS2812_SPI_DIVISOR`|`16` |SPI source clock peripheral divisor | + #### Testing Notes While not an exhaustive list, the following table provides the scenarios that have been partially validated: diff --git a/drivers/chibios/ws2812_spi.c b/drivers/chibios/ws2812_spi.c index 89df2987b548..bc0a54accedc 100644 --- a/drivers/chibios/ws2812_spi.c +++ b/drivers/chibios/ws2812_spi.c @@ -32,6 +32,37 @@ # endif #endif +// Define SPI config speed +// baudrate should target 3.2MHz +// F072 fpclk = 48MHz +// 48/16 = 3Mhz +#if WS2812_SPI_DIVISOR == 2 +# define WS2812_SPI_DIVISOR (0) +#elif WS2812_SPI_DIVISOR == 4 +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_0) +#elif WS2812_SPI_DIVISOR == 8 +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1) +#elif WS2812_SPI_DIVISOR == 16 //same as default +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1 | SPI_CR1_BR_0) +#elif WS2812_SPI_DIVISOR == 32 +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2) +#elif WS2812_SPI_DIVISOR == 64 +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_0) +#elif WS2812_SPI_DIVISOR == 128 +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_1) +#elif WS2812_SPI_DIVISOR == 256 +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0) +#else +# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1 | SPI_CR1_BR_0) //default +#endif + +// Use SPI circular buffer +#ifdef WS2812_SPI_USE_CIRCULAR_BUFFER +# define WS2812_SPI_BUFFER_MODE 1 //circular buffer +#else +# define WS2812_SPI_BUFFER_MODE 0 //normal buffer +#endif + #define BYTES_FOR_LED_BYTE 4 #define NB_COLORS 3 #define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * NB_COLORS) @@ -82,13 +113,16 @@ void ws2812_init(void) { // TODO: more dynamic baudrate static const SPIConfig spicfg = { - 0, NULL, PAL_PORT(RGB_DI_PIN), PAL_PAD(RGB_DI_PIN), - SPI_CR1_BR_1 | SPI_CR1_BR_0 // baudrate : fpclk / 8 => 1tick is 0.32us (2.25 MHz) + WS2812_SPI_BUFFER_MODE, NULL, PAL_PORT(RGB_DI_PIN), PAL_PAD(RGB_DI_PIN), + WS2812_SPI_DIVISOR }; spiAcquireBus(&WS2812_SPI); /* Acquire ownership of the bus. */ spiStart(&WS2812_SPI, &spicfg); /* Setup transfer parameters. */ spiSelect(&WS2812_SPI); /* Slave Select assertion. */ +#ifdef WS2812_SPI_USE_CIRCULAR_BUFFER + spiStartSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf); +#endif } void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) { @@ -104,9 +138,11 @@ void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) { // Send async - each led takes ~0.03ms, 50 leds ~1.5ms, animations flushing faster than send will cause issues. // Instead spiSend can be used to send synchronously (or the thread logic can be added back). -#ifdef WS2812_SPI_SYNC +#ifndef WS2812_SPI_USE_CIRCULAR_BUFFER +# ifdef WS2812_SPI_SYNC spiSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf); -#else +# else spiStartSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf); +# endif #endif }