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