summaryrefslogtreecommitdiff
path: root/drivers/arm/ws2812_spi.c
diff options
context:
space:
mode:
authorDrashna Jaelre <drashna@live.com>2020-01-05 19:04:55 -0800
committerFlorian Didron <fdidron@users.noreply.github.com>2020-01-09 08:57:11 +0900
commitdf91396be99ea4bd916c8671d233d75d3d67aa18 (patch)
tree33325a06ad2caf8c5b0e9206733719d9c4b938d8 /drivers/arm/ws2812_spi.c
parent81126b6673313dc5e40ed92cc67f85b430003894 (diff)
WS2812 Overhaul (#210)
* ARM - ws2812 bitbang (#7173) * Initial ARM bitbang ws2812 driver * Unify chibios platform to run rgblight_task * Remove 'avr only' comments from ws2812 docs * Remove 'avr only' comments from ws2812 docs * Unify chibios platform to run rgblight_task - review comments * Remove debug flags from keymap * Add comments from review * Add defines for STM32L0XX * Attempt to get arm ws2812 working on multiple gcc versions * Support RGBLIGHT_SLEEP when ChibiOS boards suspend (#7280) Copypasta from the AVR suspend implementation with a Teensy-specific hack removed * Unify RGB and RGBW commands (#7297) * Fix unicode in comments Co-Authored-By: fauxpark <fauxpark@gmail.com> * Remove separate RGBW implementation for a unified function * Set White to 0 in RGBW LEDs This is just to get this working, later, proper brightness can be handled elsewhere. * Use us instead of nanoseconds(?) since it renders correctly on web * Remove RGBW function from arm/ws2812.h * Remove RGBW function from arm/ws2812.c * Formatting changes * Add doc info * Remove force of debug on within rgblight - causes lockups waiting for hid_listen (#7330) * Move Ergodox EZ RGB Light code to custom driver (#7309) * Move Ergodox EZ RGB code to custom driver Also implements full addressing of Ergodox EZ's LED Strip, as written by seebs Co-authored-by: Seebs <seebs@seebs.net> * Make Clipping range accessible for custom drivers * Remove RGBW_BB_TWI from driver and docs * Revert changes to clipping range support * Use just rgblight_set instead of full custom driver * Convert to i2c_master commands * Rename rgblight driver and clean up includes * Use White channel on RGBW LEDs * SPI DMA based RGB Underglow for STM32 (#7674) * Initial stash of ws2812 spi driver * Update comment, add sync backup plan * Add testing notes to spi ws2812 driver * Align RGBW error messages Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Jonathan Rascher <jon@bcat.name> Co-authored-by: Florian Didron <fdidron@users.noreply.github.com>
Diffstat (limited to 'drivers/arm/ws2812_spi.c')
-rw-r--r--drivers/arm/ws2812_spi.c91
1 files changed, 90 insertions, 1 deletions
diff --git a/drivers/arm/ws2812_spi.c b/drivers/arm/ws2812_spi.c
index 2094e50098..0e954ec506 100644
--- a/drivers/arm/ws2812_spi.c
+++ b/drivers/arm/ws2812_spi.c
@@ -1 +1,90 @@
-#error("NOT SUPPORTED") \ No newline at end of file
+#include "quantum.h"
+#include "ws2812.h"
+
+/* Adapted from https://github.com/gamazeps/ws2812b-chibios-SPIDMA/ */
+
+#ifdef RGBW
+# error "RGBW not supported"
+#endif
+
+// Define the spi your LEDs are plugged to here
+#ifndef WS2812_SPI
+# define WS2812_SPI SPID1
+#endif
+
+#ifndef WS2812_SPI_MOSI_PAL_MODE
+# define WS2812_SPI_MOSI_PAL_MODE 5
+#endif
+
+#define BYTES_FOR_LED_BYTE 4
+#define NB_COLORS 3
+#define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * NB_COLORS)
+#define DATA_SIZE (BYTES_FOR_LED * RGBLED_NUM)
+#define RESET_SIZE 200
+#define PREAMBLE_SIZE 4
+
+static uint8_t txbuf[PREAMBLE_SIZE + DATA_SIZE + RESET_SIZE] = {0};
+
+/*
+ * As the trick here is to use the SPI to send a huge pattern of 0 and 1 to
+ * the ws2812b protocol, we use this helper function to translate bytes into
+ * 0s and 1s for the LED (with the appropriate timing).
+ */
+static uint8_t get_protocol_eq(uint8_t data, int pos) {
+ uint8_t eq = 0;
+ if (data & (1 << (2 * (3 - pos))))
+ eq = 0b1110;
+ else
+ eq = 0b1000;
+ if (data & (2 << (2 * (3 - pos))))
+ eq += 0b11100000;
+ else
+ eq += 0b10000000;
+ return eq;
+}
+
+static void set_led_color_rgb(LED_TYPE color, int pos) {
+ uint8_t* tx_start = &txbuf[PREAMBLE_SIZE];
+
+ for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.g, j);
+ for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.r, j);
+ for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.b, j);
+}
+
+void ws2812_init(void) {
+#if defined(USE_GPIOV1)
+ palSetLineMode(RGB_DI_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL);
+#else
+ palSetLineMode(RGB_DI_PIN, PAL_MODE_ALTERNATE(WS2812_SPI_MOSI_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL);
+#endif
+
+ // TODO: more dynamic baudrate
+ static const SPIConfig spicfg = {
+ 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)
+ };
+
+ spiAcquireBus(&WS2812_SPI); /* Acquire ownership of the bus. */
+ spiStart(&WS2812_SPI, &spicfg); /* Setup transfer parameters. */
+ spiSelect(&WS2812_SPI); /* Slave Select assertion. */
+}
+
+void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) {
+ static bool s_init = false;
+ if (!s_init) {
+ ws2812_init();
+ s_init = true;
+ }
+
+ for (uint8_t i = 0; i < leds; i++) {
+ set_led_color_rgb(ledarray[i], i);
+ }
+
+ // 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
+ spiSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf);
+#else
+ spiStartSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf);
+#endif
+}