|
@@ -1 +1,90 @@
|
|
|
-#error("NOT SUPPORTED")
|
|
|
+#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
|
|
|
+}
|