diff options
Diffstat (limited to 'drivers/chibios/ws2812.c')
| -rw-r--r-- | drivers/chibios/ws2812.c | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/drivers/chibios/ws2812.c b/drivers/chibios/ws2812.c new file mode 100644 index 000000000..bdca565d8 --- /dev/null +++ b/drivers/chibios/ws2812.c | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | #include "quantum.h" | ||
| 2 | #include "ws2812.h" | ||
| 3 | #include "ch.h" | ||
| 4 | #include "hal.h" | ||
| 5 | |||
| 6 | /* Adapted from https://github.com/bigjosh/SimpleNeoPixelDemo/ */ | ||
| 7 | |||
| 8 | #ifndef NOP_FUDGE | ||
| 9 | # if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) | ||
| 10 | # define NOP_FUDGE 0.4 | ||
| 11 | # else | ||
| 12 | # error("NOP_FUDGE configuration required") | ||
| 13 | # define NOP_FUDGE 1 // this just pleases the compile so the above error is easier to spot | ||
| 14 | # endif | ||
| 15 | #endif | ||
| 16 | |||
| 17 | #define NUMBER_NOPS 6 | ||
| 18 | #define CYCLES_PER_SEC (STM32_SYSCLK / NUMBER_NOPS * NOP_FUDGE) | ||
| 19 | #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 | ||
| 20 | #define NS_PER_CYCLE (NS_PER_SEC / CYCLES_PER_SEC) | ||
| 21 | #define NS_TO_CYCLES(n) ((n) / NS_PER_CYCLE) | ||
| 22 | |||
| 23 | #define wait_ns(x) \ | ||
| 24 | do { \ | ||
| 25 | for (int i = 0; i < NS_TO_CYCLES(x); i++) { \ | ||
| 26 | __asm__ volatile("nop\n\t" \ | ||
| 27 | "nop\n\t" \ | ||
| 28 | "nop\n\t" \ | ||
| 29 | "nop\n\t" \ | ||
| 30 | "nop\n\t" \ | ||
| 31 | "nop\n\t"); \ | ||
| 32 | } \ | ||
| 33 | } while (0) | ||
| 34 | |||
| 35 | // These are the timing constraints taken mostly from the WS2812 datasheets | ||
| 36 | // These are chosen to be conservative and avoid problems rather than for maximum throughput | ||
| 37 | |||
| 38 | #define T1H 900 // Width of a 1 bit in ns | ||
| 39 | #define T1L (1250 - T1H) // Width of a 1 bit in ns | ||
| 40 | |||
| 41 | #define T0H 350 // Width of a 0 bit in ns | ||
| 42 | #define T0L (1250 - T0H) // Width of a 0 bit in ns | ||
| 43 | |||
| 44 | // The reset gap can be 6000 ns, but depending on the LED strip it may have to be increased | ||
| 45 | // to values like 600000 ns. If it is too small, the pixels will show nothing most of the time. | ||
| 46 | #define RES 10000 // Width of the low gap between bits to cause a frame to latch | ||
| 47 | |||
| 48 | void sendByte(uint8_t byte) { | ||
| 49 | // WS2812 protocol wants most significant bits first | ||
| 50 | for (unsigned char bit = 0; bit < 8; bit++) { | ||
| 51 | bool is_one = byte & (1 << (7 - bit)); | ||
| 52 | // using something like wait_ns(is_one ? T1L : T0L) here throws off timings | ||
| 53 | if (is_one) { | ||
| 54 | // 1 | ||
| 55 | writePinHigh(RGB_DI_PIN); | ||
| 56 | wait_ns(T1H); | ||
| 57 | writePinLow(RGB_DI_PIN); | ||
| 58 | wait_ns(T1L); | ||
| 59 | } else { | ||
| 60 | // 0 | ||
| 61 | writePinHigh(RGB_DI_PIN); | ||
| 62 | wait_ns(T0H); | ||
| 63 | writePinLow(RGB_DI_PIN); | ||
| 64 | wait_ns(T0L); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | void ws2812_init(void) { setPinOutput(RGB_DI_PIN); } | ||
| 70 | |||
| 71 | // Setleds for standard RGB | ||
| 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 | // this code is very time dependent, so we need to disable interrupts | ||
| 80 | chSysLock(); | ||
| 81 | |||
| 82 | for (uint8_t i = 0; i < leds; i++) { | ||
| 83 | // WS2812 protocol dictates grb order | ||
| 84 | sendByte(ledarray[i].g); | ||
| 85 | sendByte(ledarray[i].r); | ||
| 86 | sendByte(ledarray[i].b); | ||
| 87 | #ifdef RGBW | ||
| 88 | sendByte(ledarray[i].w); | ||
| 89 | #endif | ||
| 90 | } | ||
| 91 | |||
| 92 | wait_ns(RES); | ||
| 93 | |||
| 94 | chSysUnlock(); | ||
| 95 | } | ||
