logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

ws2812_spi.c (8844B)


  1. #include "ws2812.h"
  2. #include "gpio.h"
  3. #include "util.h"
  4. #include "chibios_config.h"
  5. /* Adapted from https://github.com/gamazeps/ws2812b-chibios-SPIDMA/ */
  6. // Define the spi your LEDs are plugged to here
  7. #ifndef WS2812_SPI_DRIVER
  8. # define WS2812_SPI_DRIVER SPID1
  9. #endif
  10. #ifndef WS2812_SPI_MOSI_PAL_MODE
  11. # define WS2812_SPI_MOSI_PAL_MODE 5
  12. #endif
  13. #ifndef WS2812_SPI_SCK_PAL_MODE
  14. # define WS2812_SPI_SCK_PAL_MODE 5
  15. #endif
  16. #ifndef WS2812_SPI_DIVISOR
  17. # define WS2812_SPI_DIVISOR 16
  18. #endif
  19. // Push Pull or Open Drain Configuration
  20. // Default Push Pull
  21. #ifndef WS2812_EXTERNAL_PULLUP
  22. # if defined(USE_GPIOV1)
  23. # define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE_PUSHPULL
  24. # else
  25. # define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_MOSI_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL
  26. # endif
  27. #else
  28. # if defined(USE_GPIOV1)
  29. # define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE_OPENDRAIN
  30. # else
  31. # define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_MOSI_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN
  32. # endif
  33. #endif
  34. // Define SPI config speed
  35. // baudrate should target 3.2MHz
  36. #if defined(AT32F415)
  37. # if WS2812_SPI_DIVISOR == 2
  38. # define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (0)
  39. # elif WS2812_SPI_DIVISOR == 4
  40. # define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_0)
  41. # elif WS2812_SPI_DIVISOR == 8
  42. # define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_1)
  43. # elif WS2812_SPI_DIVISOR == 16 // default
  44. # define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_1 | SPI_CTRL1_MDIV_0)
  45. # elif WS2812_SPI_DIVISOR == 32
  46. # define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_2)
  47. # elif WS2812_SPI_DIVISOR == 64
  48. # define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_0)
  49. # elif WS2812_SPI_DIVISOR == 128
  50. # define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_1)
  51. # elif WS2812_SPI_DIVISOR == 256
  52. # define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_1 | SPI_CTRL1_MDIV_0)
  53. # elif WS2812_SPI_DIVISOR == 512
  54. # define WS2812_SPI_DIVISOR_CTRL2_MDIV_X (SPI_CTRL1_MDIV_3)
  55. # elif WS2812_SPI_DIVISOR == 1024
  56. # define WS2812_SPI_DIVISOR_CTRL2_MDIV_X (SPI_CTRL1_MDIV_3)
  57. # define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_0)
  58. # else
  59. # error "Configured WS2812_SPI_DIVISOR value is not supported at this time."
  60. # endif
  61. #else
  62. // F072 fpclk = 48MHz
  63. // 48/16 = 3Mhz
  64. # if WS2812_SPI_DIVISOR == 2
  65. # define WS2812_SPI_DIVISOR_CR1_BR_X (0)
  66. # elif WS2812_SPI_DIVISOR == 4
  67. # define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_0)
  68. # elif WS2812_SPI_DIVISOR == 8
  69. # define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1)
  70. # elif WS2812_SPI_DIVISOR == 16 // default
  71. # define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1 | SPI_CR1_BR_0)
  72. # elif WS2812_SPI_DIVISOR == 32
  73. # define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2)
  74. # elif WS2812_SPI_DIVISOR == 64
  75. # define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_0)
  76. # elif WS2812_SPI_DIVISOR == 128
  77. # define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_1)
  78. # elif WS2812_SPI_DIVISOR == 256
  79. # define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0)
  80. # else
  81. # error "Configured WS2812_SPI_DIVISOR value is not supported at this time."
  82. # endif
  83. #endif
  84. // Use SPI circular buffer
  85. #ifdef WS2812_SPI_USE_CIRCULAR_BUFFER
  86. # define WS2812_SPI_BUFFER_MODE 1 // circular buffer
  87. #else
  88. # define WS2812_SPI_BUFFER_MODE 0 // normal buffer
  89. #endif
  90. #if defined(USE_GPIOV1)
  91. # define WS2812_SCK_OUTPUT_MODE PAL_MODE_ALTERNATE_PUSHPULL
  92. #else
  93. # define WS2812_SCK_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_SCK_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL
  94. #endif
  95. #define BYTES_FOR_LED_BYTE 4
  96. #ifdef WS2812_RGBW
  97. # define WS2812_CHANNELS 4
  98. #else
  99. # define WS2812_CHANNELS 3
  100. #endif
  101. #define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * WS2812_CHANNELS)
  102. #define DATA_SIZE (BYTES_FOR_LED * WS2812_LED_COUNT)
  103. #define RESET_SIZE (1000 * WS2812_TRST_US / (2 * WS2812_TIMING))
  104. #define PREAMBLE_SIZE 4
  105. static uint8_t txbuf[PREAMBLE_SIZE + DATA_SIZE + RESET_SIZE] = {0};
  106. /*
  107. * As the trick here is to use the SPI to send a huge pattern of 0 and 1 to
  108. * the ws2812b protocol, we use this helper function to translate bytes into
  109. * 0s and 1s for the LED (with the appropriate timing).
  110. */
  111. static uint8_t get_protocol_eq(uint8_t data, int pos) {
  112. uint8_t eq = 0;
  113. if (data & (1 << (2 * (3 - pos))))
  114. eq = 0b1110;
  115. else
  116. eq = 0b1000;
  117. if (data & (2 << (2 * (3 - pos))))
  118. eq += 0b11100000;
  119. else
  120. eq += 0b10000000;
  121. return eq;
  122. }
  123. static void set_led_color_rgb(ws2812_led_t color, int pos) {
  124. uint8_t* tx_start = &txbuf[PREAMBLE_SIZE];
  125. #if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)
  126. for (int j = 0; j < 4; j++)
  127. tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.g, j);
  128. for (int j = 0; j < 4; j++)
  129. tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.r, j);
  130. for (int j = 0; j < 4; j++)
  131. tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.b, j);
  132. #elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)
  133. for (int j = 0; j < 4; j++)
  134. tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.r, j);
  135. for (int j = 0; j < 4; j++)
  136. tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.g, j);
  137. for (int j = 0; j < 4; j++)
  138. tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.b, j);
  139. #elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)
  140. for (int j = 0; j < 4; j++)
  141. tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.b, j);
  142. for (int j = 0; j < 4; j++)
  143. tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.g, j);
  144. for (int j = 0; j < 4; j++)
  145. tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.r, j);
  146. #endif
  147. #ifdef WS2812_RGBW
  148. for (int j = 0; j < 4; j++)
  149. tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 3 + j] = get_protocol_eq(color.w, j);
  150. #endif
  151. }
  152. ws2812_led_t ws2812_leds[WS2812_LED_COUNT];
  153. void ws2812_init(void) {
  154. palSetLineMode(WS2812_DI_PIN, WS2812_MOSI_OUTPUT_MODE);
  155. #ifdef WS2812_SPI_SCK_PIN
  156. palSetLineMode(WS2812_SPI_SCK_PIN, WS2812_SCK_OUTPUT_MODE);
  157. #endif // WS2812_SPI_SCK_PIN
  158. // TODO: more dynamic baudrate
  159. static const SPIConfig spicfg = {
  160. #ifndef HAL_LLD_SELECT_SPI_V2
  161. // HAL_SPI_V1
  162. # if SPI_SUPPORTS_CIRCULAR == TRUE
  163. WS2812_SPI_BUFFER_MODE,
  164. # endif
  165. NULL, // end_cb
  166. PAL_PORT(WS2812_DI_PIN),
  167. PAL_PAD(WS2812_DI_PIN),
  168. # if defined(WB32F3G71xx) || defined(WB32FQ95xx)
  169. 0,
  170. 0,
  171. WS2812_SPI_DIVISOR
  172. # else
  173. WS2812_SPI_DIVISOR_CR1_BR_X,
  174. 0
  175. # endif
  176. #else
  177. // HAL_SPI_V2
  178. # if SPI_SUPPORTS_CIRCULAR == TRUE
  179. WS2812_SPI_BUFFER_MODE,
  180. # endif
  181. # if SPI_SUPPORTS_SLAVE_MODE == TRUE
  182. false,
  183. # endif
  184. NULL, // data_cb
  185. NULL, // error_cb
  186. PAL_PORT(WS2812_DI_PIN),
  187. PAL_PAD(WS2812_DI_PIN),
  188. # if defined(AT32F415)
  189. WS2812_SPI_DIVISOR_CTRL1_MDIV_X,
  190. # if (WS2812_SPI_DIVISOR == 512 || WS2812_SPI_DIVISOR == 1024)
  191. WS2812_SPI_DIVISOR_CTRL2_MDIV_X,
  192. # endif
  193. 0
  194. # else
  195. WS2812_SPI_DIVISOR_CR1_BR_X,
  196. 0
  197. # endif
  198. #endif
  199. };
  200. spiAcquireBus(&WS2812_SPI_DRIVER); /* Acquire ownership of the bus. */
  201. spiStart(&WS2812_SPI_DRIVER, &spicfg); /* Setup transfer parameters. */
  202. spiSelect(&WS2812_SPI_DRIVER); /* Slave Select assertion. */
  203. #ifdef WS2812_SPI_USE_CIRCULAR_BUFFER
  204. spiStartSend(&WS2812_SPI_DRIVER, ARRAY_SIZE(txbuf), txbuf);
  205. #endif
  206. }
  207. void ws2812_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
  208. ws2812_leds[index].r = red;
  209. ws2812_leds[index].g = green;
  210. ws2812_leds[index].b = blue;
  211. #if defined(WS2812_RGBW)
  212. ws2812_rgb_to_rgbw(&ws2812_leds[index]);
  213. #endif
  214. }
  215. void ws2812_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
  216. for (int i = 0; i < WS2812_LED_COUNT; i++) {
  217. ws2812_set_color(i, red, green, blue);
  218. }
  219. }
  220. void ws2812_flush(void) {
  221. for (int i = 0; i < WS2812_LED_COUNT; i++) {
  222. set_led_color_rgb(ws2812_leds[i], i);
  223. }
  224. // Send async - each led takes ~0.03ms, 50 leds ~1.5ms, animations flushing faster than send will cause issues.
  225. // Instead spiSend can be used to send synchronously (or the thread logic can be added back).
  226. #ifndef WS2812_SPI_USE_CIRCULAR_BUFFER
  227. # ifdef WS2812_SPI_SYNC
  228. spiSend(&WS2812_SPI_DRIVER, ARRAY_SIZE(txbuf), txbuf);
  229. # else
  230. spiStartSend(&WS2812_SPI_DRIVER, ARRAY_SIZE(txbuf), txbuf);
  231. # endif
  232. #endif
  233. }