ws2812.c 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  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(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. #define NUMBER_NOPS 6
  15. #define CYCLES_PER_SEC (STM32_SYSCLK / NUMBER_NOPS * NOP_FUDGE)
  16. #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
  17. #define NS_PER_CYCLE (NS_PER_SEC / CYCLES_PER_SEC)
  18. #define NS_TO_CYCLES(n) ((n) / NS_PER_CYCLE)
  19. #define wait_ns(x) \
  20. do { \
  21. for (int i = 0; i < NS_TO_CYCLES(x); i++) { \
  22. __asm__ volatile("nop\n\t" \
  23. "nop\n\t" \
  24. "nop\n\t" \
  25. "nop\n\t" \
  26. "nop\n\t" \
  27. "nop\n\t"); \
  28. } \
  29. } while (0)
  30. // These are the timing constraints taken mostly from the WS2812 datasheets
  31. // These are chosen to be conservative and avoid problems rather than for maximum throughput
  32. #define T1H 900 // Width of a 1 bit in ns
  33. #define T1L (1250 - T1H) // Width of a 1 bit in ns
  34. #define T0H 350 // Width of a 0 bit in ns
  35. #define T0L (1250 - T0H) // Width of a 0 bit in ns
  36. // The reset gap can be 6000 ns, but depending on the LED strip it may have to be increased
  37. // to values like 600000 ns. If it is too small, the pixels will show nothing most of the time.
  38. #define RES 10000 // Width of the low gap between bits to cause a frame to latch
  39. void sendByte(uint8_t byte) {
  40. // WS2812 protocol wants most significant bits first
  41. for (unsigned char bit = 0; bit < 8; bit++) {
  42. bool is_one = byte & (1 << (7 - bit));
  43. // using something like wait_ns(is_one ? T1L : T0L) here throws off timings
  44. if (is_one) {
  45. // 1
  46. writePinHigh(RGB_DI_PIN);
  47. wait_ns(T1H);
  48. writePinLow(RGB_DI_PIN);
  49. wait_ns(T1L);
  50. } else {
  51. // 0
  52. writePinHigh(RGB_DI_PIN);
  53. wait_ns(T0H);
  54. writePinLow(RGB_DI_PIN);
  55. wait_ns(T0L);
  56. }
  57. }
  58. }
  59. void ws2812_init(void) { setPinOutput(RGB_DI_PIN); }
  60. // Setleds for standard RGB
  61. void ws2812_setleds(LED_TYPE *ledarray, uint16_t leds) {
  62. static bool s_init = false;
  63. if (!s_init) {
  64. ws2812_init();
  65. s_init = true;
  66. }
  67. // this code is very time dependent, so we need to disable interrupts
  68. chSysLock();
  69. for (uint8_t i = 0; i < leds; i++) {
  70. // WS2812 protocol dictates grb order
  71. sendByte(ledarray[i].g);
  72. sendByte(ledarray[i].r);
  73. sendByte(ledarray[i].b);
  74. #ifdef RGBW
  75. sendByte(ledarray[i].w);
  76. #endif
  77. }
  78. wait_ns(RES);
  79. chSysUnlock();
  80. }