Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARM WS2812 SPI config (baudrate and circular buffer) #12216

Merged
merged 7 commits into from
Mar 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/ws2812_driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
44 changes: 40 additions & 4 deletions drivers/chibios/ws2812_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand All @@ -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
}