Commit | Line | Data |
---|---|---|
680ebef0 JC |
1 | #include "quantum.h" |
2 | #include "ws2812.h" | |
3 | ||
4 | /* Adapted from https://github.com/gamazeps/ws2812b-chibios-SPIDMA/ */ | |
5 | ||
6 | #ifdef RGBW | |
7 | # error "RGBW not supported" | |
8 | #endif | |
9 | ||
10 | // Define the spi your LEDs are plugged to here | |
11 | #ifndef WS2812_SPI | |
12 | # define WS2812_SPI SPID1 | |
13 | #endif | |
14 | ||
15 | #ifndef WS2812_SPI_MOSI_PAL_MODE | |
16 | # define WS2812_SPI_MOSI_PAL_MODE 5 | |
17 | #endif | |
18 | ||
19 | #define BYTES_FOR_LED_BYTE 4 | |
20 | #define NB_COLORS 3 | |
21 | #define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * NB_COLORS) | |
22 | #define DATA_SIZE (BYTES_FOR_LED * RGBLED_NUM) | |
23 | #define RESET_SIZE 200 | |
24 | #define PREAMBLE_SIZE 4 | |
25 | ||
26 | static uint8_t txbuf[PREAMBLE_SIZE + DATA_SIZE + RESET_SIZE] = {0}; | |
27 | ||
28 | /* | |
29 | * As the trick here is to use the SPI to send a huge pattern of 0 and 1 to | |
30 | * the ws2812b protocol, we use this helper function to translate bytes into | |
31 | * 0s and 1s for the LED (with the appropriate timing). | |
32 | */ | |
33 | static uint8_t get_protocol_eq(uint8_t data, int pos) { | |
34 | uint8_t eq = 0; | |
35 | if (data & (1 << (2 * (3 - pos)))) | |
36 | eq = 0b1110; | |
37 | else | |
38 | eq = 0b1000; | |
39 | if (data & (2 << (2 * (3 - pos)))) | |
40 | eq += 0b11100000; | |
41 | else | |
42 | eq += 0b10000000; | |
43 | return eq; | |
44 | } | |
45 | ||
46 | static void set_led_color_rgb(LED_TYPE color, int pos) { | |
47 | uint8_t* tx_start = &txbuf[PREAMBLE_SIZE]; | |
48 | ||
49 | for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.g, j); | |
50 | for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.r, j); | |
51 | 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); | |
52 | } | |
53 | ||
54 | void ws2812_init(void) { | |
55 | #if defined(USE_GPIOV1) | |
56 | palSetLineMode(RGB_DI_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL); | |
57 | #else | |
58 | palSetLineMode(RGB_DI_PIN, PAL_MODE_ALTERNATE(WS2812_SPI_MOSI_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL); | |
59 | #endif | |
60 | ||
61 | // TODO: more dynamic baudrate | |
62 | static const SPIConfig spicfg = { | |
26eef35f | 63 | 0, NULL, PAL_PORT(RGB_DI_PIN), PAL_PAD(RGB_DI_PIN), |
680ebef0 JC |
64 | SPI_CR1_BR_1 | SPI_CR1_BR_0 // baudrate : fpclk / 8 => 1tick is 0.32us (2.25 MHz) |
65 | }; | |
66 | ||
67 | spiAcquireBus(&WS2812_SPI); /* Acquire ownership of the bus. */ | |
68 | spiStart(&WS2812_SPI, &spicfg); /* Setup transfer parameters. */ | |
69 | spiSelect(&WS2812_SPI); /* Slave Select assertion. */ | |
70 | } | |
71 | ||
72 | void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) { | |
73 | static bool s_init = false; | |
74 | if (!s_init) { | |
75 | ws2812_init(); | |
76 | s_init = true; | |
77 | } | |
78 | ||
79 | for (uint8_t i = 0; i < leds; i++) { | |
80 | set_led_color_rgb(ledarray[i], i); | |
81 | } | |
82 | ||
83 | // Send async - each led takes ~0.03ms, 50 leds ~1.5ms, animations flushing faster than send will cause issues. | |
84 | // Instead spiSend can be used to send synchronously (or the thread logic can be added back). | |
85 | #ifdef WS2812_SPI_SYNC | |
86 | spiSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf); | |
87 | #else | |
88 | spiStartSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf); | |
89 | #endif | |
90 | } |