diff options
Diffstat (limited to 'platforms/chibios/drivers/ws2812.c')
-rw-r--r-- | platforms/chibios/drivers/ws2812.c | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/platforms/chibios/drivers/ws2812.c b/platforms/chibios/drivers/ws2812.c new file mode 100644 index 000000000..0d12e2fb7 --- /dev/null +++ b/platforms/chibios/drivers/ws2812.c | |||
@@ -0,0 +1,114 @@ | |||
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 | // Push Pull or Open Drain Configuration | ||
18 | // Default Push Pull | ||
19 | #ifndef WS2812_EXTERNAL_PULLUP | ||
20 | # define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_PUSHPULL | ||
21 | #else | ||
22 | # define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_OPENDRAIN | ||
23 | #endif | ||
24 | |||
25 | #define NUMBER_NOPS 6 | ||
26 | #define CYCLES_PER_SEC (STM32_SYSCLK / NUMBER_NOPS * NOP_FUDGE) | ||
27 | #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 | ||
28 | #define NS_PER_CYCLE (NS_PER_SEC / CYCLES_PER_SEC) | ||
29 | #define NS_TO_CYCLES(n) ((n) / NS_PER_CYCLE) | ||
30 | |||
31 | #define wait_ns(x) \ | ||
32 | do { \ | ||
33 | for (int i = 0; i < NS_TO_CYCLES(x); i++) { \ | ||
34 | __asm__ volatile("nop\n\t" \ | ||
35 | "nop\n\t" \ | ||
36 | "nop\n\t" \ | ||
37 | "nop\n\t" \ | ||
38 | "nop\n\t" \ | ||
39 | "nop\n\t"); \ | ||
40 | } \ | ||
41 | } while (0) | ||
42 | |||
43 | // These are the timing constraints taken mostly from the WS2812 datasheets | ||
44 | // These are chosen to be conservative and avoid problems rather than for maximum throughput | ||
45 | |||
46 | #define T1H 900 // Width of a 1 bit in ns | ||
47 | #define T1L (1250 - T1H) // Width of a 1 bit in ns | ||
48 | |||
49 | #define T0H 350 // Width of a 0 bit in ns | ||
50 | #define T0L (1250 - T0H) // Width of a 0 bit in ns | ||
51 | |||
52 | // The reset gap can be 6000 ns, but depending on the LED strip it may have to be increased | ||
53 | // to values like 600000 ns. If it is too small, the pixels will show nothing most of the time. | ||
54 | #define RES (1000 * WS2812_TRST_US) // Width of the low gap between bits to cause a frame to latch | ||
55 | |||
56 | void sendByte(uint8_t byte) { | ||
57 | // WS2812 protocol wants most significant bits first | ||
58 | for (unsigned char bit = 0; bit < 8; bit++) { | ||
59 | bool is_one = byte & (1 << (7 - bit)); | ||
60 | // using something like wait_ns(is_one ? T1L : T0L) here throws off timings | ||
61 | if (is_one) { | ||
62 | // 1 | ||
63 | writePinHigh(RGB_DI_PIN); | ||
64 | wait_ns(T1H); | ||
65 | writePinLow(RGB_DI_PIN); | ||
66 | wait_ns(T1L); | ||
67 | } else { | ||
68 | // 0 | ||
69 | writePinHigh(RGB_DI_PIN); | ||
70 | wait_ns(T0H); | ||
71 | writePinLow(RGB_DI_PIN); | ||
72 | wait_ns(T0L); | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | |||
77 | void ws2812_init(void) { palSetLineMode(RGB_DI_PIN, WS2812_OUTPUT_MODE); } | ||
78 | |||
79 | // Setleds for standard RGB | ||
80 | void ws2812_setleds(LED_TYPE *ledarray, uint16_t leds) { | ||
81 | static bool s_init = false; | ||
82 | if (!s_init) { | ||
83 | ws2812_init(); | ||
84 | s_init = true; | ||
85 | } | ||
86 | |||
87 | // this code is very time dependent, so we need to disable interrupts | ||
88 | chSysLock(); | ||
89 | |||
90 | for (uint8_t i = 0; i < leds; i++) { | ||
91 | // WS2812 protocol dictates grb order | ||
92 | #if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB) | ||
93 | sendByte(ledarray[i].g); | ||
94 | sendByte(ledarray[i].r); | ||
95 | sendByte(ledarray[i].b); | ||
96 | #elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB) | ||
97 | sendByte(ledarray[i].r); | ||
98 | sendByte(ledarray[i].g); | ||
99 | sendByte(ledarray[i].b); | ||
100 | #elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR) | ||
101 | sendByte(ledarray[i].b); | ||
102 | sendByte(ledarray[i].g); | ||
103 | sendByte(ledarray[i].r); | ||
104 | #endif | ||
105 | |||
106 | #ifdef RGBW | ||
107 | sendByte(ledarray[i].w); | ||
108 | #endif | ||
109 | } | ||
110 | |||
111 | wait_ns(RES); | ||
112 | |||
113 | chSysUnlock(); | ||
114 | } | ||