You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

114 lines
3.8 KiB

  1. #include "quantum.h"
  2. #include "ws2812.h"
  3. #include <ch.h>
  4. #include <hal.h>
  5. /* Adapted from https://github.com/bigjosh/SimpleNeoPixelDemo/ */
  6. #ifndef NOP_FUDGE
  7. # if defined(STM32F0XX) || defined(STM32F1XX) || defined(GD32VF103) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX)
  8. # define NOP_FUDGE 0.4
  9. # else
  10. # error("NOP_FUDGE configuration required")
  11. # define NOP_FUDGE 1 // this just pleases the compile so the above error is easier to spot
  12. # endif
  13. #endif
  14. // Push Pull or Open Drain Configuration
  15. // Default Push Pull
  16. #ifndef WS2812_EXTERNAL_PULLUP
  17. # define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_PUSHPULL
  18. #else
  19. # define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_OPENDRAIN
  20. #endif
  21. #define NUMBER_NOPS 6
  22. #define CYCLES_PER_SEC (CPU_CLOCK / NUMBER_NOPS * NOP_FUDGE)
  23. #define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives
  24. #define NS_PER_CYCLE (NS_PER_SEC / CYCLES_PER_SEC)
  25. #define NS_TO_CYCLES(n) ((n) / NS_PER_CYCLE)
  26. #define wait_ns(x) \
  27. do { \
  28. for (int i = 0; i < NS_TO_CYCLES(x); i++) { \
  29. __asm__ volatile("nop\n\t" \
  30. "nop\n\t" \
  31. "nop\n\t" \
  32. "nop\n\t" \
  33. "nop\n\t" \
  34. "nop\n\t"); \
  35. } \
  36. } while (0)
  37. // These are the timing constraints taken mostly from the WS2812 datasheets
  38. // These are chosen to be conservative and avoid problems rather than for maximum throughput
  39. #define T1H 900 // Width of a 1 bit in ns
  40. #define T1L (1250 - T1H) // Width of a 1 bit in ns
  41. #define T0H 350 // Width of a 0 bit in ns
  42. #define T0L (1250 - T0H) // Width of a 0 bit in ns
  43. // The reset gap can be 6000 ns, but depending on the LED strip it may have to be increased
  44. // to values like 600000 ns. If it is too small, the pixels will show nothing most of the time.
  45. #define RES (1000 * WS2812_TRST_US) // Width of the low gap between bits to cause a frame to latch
  46. void sendByte(uint8_t byte) {
  47. // WS2812 protocol wants most significant bits first
  48. for (unsigned char bit = 0; bit < 8; bit++) {
  49. bool is_one = byte & (1 << (7 - bit));
  50. // using something like wait_ns(is_one ? T1L : T0L) here throws off timings
  51. if (is_one) {
  52. // 1
  53. writePinHigh(RGB_DI_PIN);
  54. wait_ns(T1H);
  55. writePinLow(RGB_DI_PIN);
  56. wait_ns(T1L);
  57. } else {
  58. // 0
  59. writePinHigh(RGB_DI_PIN);
  60. wait_ns(T0H);
  61. writePinLow(RGB_DI_PIN);
  62. wait_ns(T0L);
  63. }
  64. }
  65. }
  66. void ws2812_init(void) { palSetLineMode(RGB_DI_PIN, WS2812_OUTPUT_MODE); }
  67. // Setleds for standard RGB
  68. void ws2812_setleds(LED_TYPE *ledarray, uint16_t leds) {
  69. static bool s_init = false;
  70. if (!s_init) {
  71. ws2812_init();
  72. s_init = true;
  73. }
  74. // this code is very time dependent, so we need to disable interrupts
  75. chSysLock();
  76. for (uint8_t i = 0; i < leds; i++) {
  77. // WS2812 protocol dictates grb order
  78. #if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)
  79. sendByte(ledarray[i].g);
  80. sendByte(ledarray[i].r);
  81. sendByte(ledarray[i].b);
  82. #elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)
  83. sendByte(ledarray[i].r);
  84. sendByte(ledarray[i].g);
  85. sendByte(ledarray[i].b);
  86. #elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)
  87. sendByte(ledarray[i].b);
  88. sendByte(ledarray[i].g);
  89. sendByte(ledarray[i].r);
  90. #endif
  91. #ifdef RGBW
  92. sendByte(ledarray[i].w);
  93. #endif
  94. }
  95. wait_ns(RES);
  96. chSysUnlock();
  97. }