aboutsummaryrefslogtreecommitdiff
path: root/platforms/chibios/drivers/ws2812.c
diff options
context:
space:
mode:
Diffstat (limited to 'platforms/chibios/drivers/ws2812.c')
-rw-r--r--platforms/chibios/drivers/ws2812.c114
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
56void 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
77void ws2812_init(void) { palSetLineMode(RGB_DI_PIN, WS2812_OUTPUT_MODE); }
78
79// Setleds for standard RGB
80void 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}