Skip to content

Commit 97cd64a

Browse files
committed
Add WS2811 RGB LED driver
Add include guard to arduino_pins.h Add ARRAYLEN macro to utils.h
0 parents  commit 97cd64a

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed

WS2811.h

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Copyright 2012 Alan Burlison, alan@bleaklow.com. All rights reserved.
3+
* Use is subject to license terms.
4+
*/
5+
6+
/*
7+
* WS2811 RGB LED driver.
8+
*/
9+
10+
#ifndef WS2811_h
11+
#define WS2811_h
12+
13+
// RGB value structure.
14+
typedef struct __attribute__ ((__packed__)) {
15+
uint8_t r;
16+
uint8_t g;
17+
uint8_t b;
18+
} RGB_t;
19+
20+
#ifndef ARRAYLEN
21+
#define ARRAYLEN(A) (sizeof(A) / sizeof(A[0]))
22+
#endif
23+
24+
/*
25+
* Inline asm macro to output 24-bit RGB value in (G,R,B) order, MSBit first.
26+
* 0 bits are 250ns hi, 1000ns lo, 1 bits are 1000ns hi, 250ns lo.
27+
* r18 = red byte to be output
28+
* r19 = green byte to be output
29+
* r20 = blue byte to be output
30+
* r26 = saved SREG
31+
* r27 = inner loop counter
32+
*/
33+
#define WS2811(PORT, PIN, RGB, LEN) \
34+
asm volatile( \
35+
/* initialise */ \
36+
" cp %A[len], r1 ; check len > 0, return immediately if it is\n" \
37+
" cpc %B[len], r1\n" \
38+
" brne 1f\n" \
39+
" rjmp 16f\n" \
40+
"1: ld r18, Z+ ; load in first red byte to be output\n" \
41+
" ld r19, Z+ ; load in first green byte to be output\n" \
42+
" ld r20, Z+ ; load in first blue byte to be output\n" \
43+
" ldi r27, 8 ; load inner loop counter\n" \
44+
" in r26, __SREG__ ; timing-critical, so no interrupts\n" \
45+
" cli\n" \
46+
/* green - loop over 8 bits */ \
47+
"2: sbi %[port], %[pin] ; pin lo -> hi\n" \
48+
" sbrc r19, 7 ; test hi bit clear\n" \
49+
" rjmp 3f ; true, skip pin hi -> lo\n" \
50+
" cbi %[port], %[pin] ; false, pin hi -> lo\n" \
51+
"3: sbrc r19, 7 ; equalise delay of both code paths\n" \
52+
" rjmp 4f\n" \
53+
"4: nop ; pulse timing delay\n" \
54+
" nop\n" \
55+
" nop\n" \
56+
" nop\n" \
57+
" nop\n" \
58+
" nop\n" \
59+
" lsl r19 ; shift to next bit\n" \
60+
" dec r27 ; decrement loop counter\n" \
61+
" cbi %[port], %[pin] ; pin hi -> lo\n" \
62+
" brne 2b\n ; loop if required\n" \
63+
" ldi r27, 7 ; reload inner loop counter\n" \
64+
/* red - loop over first 7 bits */ \
65+
"5: sbi %[port], %[pin] ; pin lo -> hi\n" \
66+
" sbrc r18, 7 ; test hi bit clear\n" \
67+
" rjmp 6f ; true, skip pin hi -> lo\n" \
68+
" cbi %[port], %[pin] ; false, pin hi -> lo\n" \
69+
"6: sbrc r18, 7 ; equalise delay of both code paths\n" \
70+
" rjmp 7f\n" \
71+
"7: nop ; pulse timing delay\n" \
72+
" nop\n" \
73+
" nop\n" \
74+
" nop\n" \
75+
" nop\n" \
76+
" nop\n" \
77+
" lsl r18 ; shift to next bit\n" \
78+
" dec r27 ; decrement inner loop counter\n" \
79+
" cbi %[port], %[pin] ; pin hi -> lo\n" \
80+
" brne 5b ; inner loop, if required\n" \
81+
" nop ; equalise delay of both code paths\n" \
82+
/* red, 8th bit - output & fetch next values */ \
83+
" sbi %[port], %[pin] ; pin lo -> hi\n" \
84+
" sbrc r18, 7 ; test hi bit clear\n" \
85+
" rjmp 8f ; true, skip pin hi -> lo\n" \
86+
" cbi %[port], %[pin] ; false, pin hi -> lo\n" \
87+
"8: sbrc r18, 7 ; equalise delay of both code paths\n" \
88+
" rjmp 9f\n" \
89+
"9: nop ; pulse timing delay\n" \
90+
" nop\n" \
91+
" nop\n" \
92+
" ld r18, Z+ ; load next red byte\n" \
93+
" ld r19, Z+ ; load next green byte\n" \
94+
" ldi r27, 7 ; reload inner loop counter\n" \
95+
" cbi %[port], %[pin] ; pin hi -> lo\n" \
96+
" nop ; pulse timing delay\n" \
97+
" nop\n" \
98+
/* blue - loop over first 7 bits */ \
99+
"10: sbi %[port], %[pin] ; pin lo -> hi\n" \
100+
" sbrc r20, 7 ; test hi bit clear\n" \
101+
" rjmp 11f ; true, skip pin hi -> lo\n" \
102+
" cbi %[port], %[pin] ; false, pin hi -> lo\n" \
103+
"11: sbrc r20, 7 ; equalise delay of both code paths\n" \
104+
" rjmp 12f\n" \
105+
"12: nop ; pulse timing delay\n" \
106+
" nop\n" \
107+
" nop\n" \
108+
" nop\n" \
109+
" nop\n" \
110+
" nop\n" \
111+
" lsl r20 ; shift to next bit\n" \
112+
" dec r27 ; decrement inner loop counter\n" \
113+
" cbi %[port], %[pin] ; pin hi -> lo\n" \
114+
" brne 10b ; inner loop, if required\n" \
115+
" nop ; equalise delay of both code paths\n" \
116+
/* blue, 8th bit - output & handle outer loop */ \
117+
" sbi %[port], %[pin] ; pin lo -> hi\n" \
118+
" sbrc r20, 7 ; test hi bit clear\n" \
119+
" rjmp 13f ; true, skip pin hi -> lo\n" \
120+
" cbi %[port], %[pin] ; false, pin hi -> lo\n" \
121+
"13: sbrc r20, 7 ; equalise delay of both code paths\n" \
122+
" rjmp 14f\n" \
123+
"14: nop ; pulse timing delay\n" \
124+
" nop\n" \
125+
" ldi r27, 8 ; reload inner loop counter\n" \
126+
" sbiw %A[len], 1 ; decrement outer loop counter\n" \
127+
" breq 15f ; exit outer loop if zero\n" \
128+
" ld r20, Z+ ; load in next blue byte\n" \
129+
" cbi %[port], %[pin] ; pin hi -> lo\n" \
130+
" rjmp 2b ; outer loop, if required\n" \
131+
"15: nop ; pulse timing delay\n" \
132+
" cbi %[port], %[pin] ; pin hi -> lo\n" \
133+
" nop ; pulse timing delay\n" \
134+
" nop\n" \
135+
" out __SREG__, r26 ; reenable interrupts\n" \
136+
"16:\n" \
137+
" sbi %[port], %[pin] ; pin hi -> lo\n" \
138+
" cbi %[port], %[pin] ; pin hi -> lo\n" \
139+
: \
140+
: [rgb] "z" (RGB), \
141+
[len] "w" (LEN), \
142+
[port] "I" (_SFR_IO_ADDR(PORT)), \
143+
[pin] "I" (PIN) \
144+
: "r18", "r19", "r20", "r26", "r27", "cc", "memory" \
145+
)
146+
147+
/*
148+
* Define a C function to wrap the inline WS2811 macro for a given port and pin.
149+
*/
150+
#define DEFINE_WS2811_FN(NAME, PORT, PIN) \
151+
extern void NAME(const RGB_t *rgb, uint16_t len) __attribute__((noinline)); \
152+
void NAME(const RGB_t *rgb, uint16_t len) { WS2811(PORT, PIN, rgb, len); }
153+
154+
#endif /* WS2811_h */

0 commit comments

Comments
 (0)