aboutsummaryrefslogtreecommitdiff
path: root/quantum/light_ws2812.c
diff options
context:
space:
mode:
Diffstat (limited to 'quantum/light_ws2812.c')
-rwxr-xr-xquantum/light_ws2812.c181
1 files changed, 181 insertions, 0 deletions
diff --git a/quantum/light_ws2812.c b/quantum/light_ws2812.c
new file mode 100755
index 000000000..f20043067
--- /dev/null
+++ b/quantum/light_ws2812.c
@@ -0,0 +1,181 @@
1/*
2* light weight WS2812 lib V2.0b
3*
4* Controls WS2811/WS2812/WS2812B RGB-LEDs
5* Author: Tim (cpldcpu@gmail.com)
6*
7* Jan 18th, 2014 v2.0b Initial Version
8* Nov 29th, 2015 v2.3 Added SK6812RGBW support
9*
10* License: GNU GPL v2 (see License.txt)
11*/
12
13#include "light_ws2812.h"
14#include <avr/interrupt.h>
15#include <avr/io.h>
16#include <util/delay.h>
17#include "debug.h"
18
19// Setleds for standard RGB
20void inline ws2812_setleds(struct cRGB *ledarray, uint16_t leds)
21{
22 ws2812_setleds_pin(ledarray,leds, _BV(ws2812_pin));
23}
24
25void inline ws2812_setleds_pin(struct cRGB *ledarray, uint16_t leds, uint8_t pinmask)
26{
27 ws2812_DDRREG |= pinmask; // Enable DDR
28 ws2812_sendarray_mask((uint8_t*)ledarray,leds+leds+leds,pinmask);
29 _delay_us(50);
30}
31
32// Setleds for SK6812RGBW
33void inline ws2812_setleds_rgbw(struct cRGBW *ledarray, uint16_t leds)
34{
35 ws2812_DDRREG |= _BV(ws2812_pin); // Enable DDR
36 ws2812_sendarray_mask((uint8_t*)ledarray,leds<<2,_BV(ws2812_pin));
37 _delay_us(80);
38}
39
40void ws2812_sendarray(uint8_t *data,uint16_t datlen)
41{
42 ws2812_sendarray_mask(data,datlen,_BV(ws2812_pin));
43}
44
45/*
46 This routine writes an array of bytes with RGB values to the Dataout pin
47 using the fast 800kHz clockless WS2811/2812 protocol.
48*/
49
50// Timing in ns
51#define w_zeropulse 350
52#define w_onepulse 900
53#define w_totalperiod 1250
54
55// Fixed cycles used by the inner loop
56#define w_fixedlow 2
57#define w_fixedhigh 4
58#define w_fixedtotal 8
59
60// Insert NOPs to match the timing, if possible
61#define w_zerocycles (((F_CPU/1000)*w_zeropulse )/1000000)
62#define w_onecycles (((F_CPU/1000)*w_onepulse +500000)/1000000)
63#define w_totalcycles (((F_CPU/1000)*w_totalperiod +500000)/1000000)
64
65// w1 - nops between rising edge and falling edge - low
66#define w1 (w_zerocycles-w_fixedlow)
67// w2 nops between fe low and fe high
68#define w2 (w_onecycles-w_fixedhigh-w1)
69// w3 nops to complete loop
70#define w3 (w_totalcycles-w_fixedtotal-w1-w2)
71
72#if w1>0
73 #define w1_nops w1
74#else
75 #define w1_nops 0
76#endif
77
78// The only critical timing parameter is the minimum pulse length of the "0"
79// Warn or throw error if this timing can not be met with current F_CPU settings.
80#define w_lowtime ((w1_nops+w_fixedlow)*1000000)/(F_CPU/1000)
81#if w_lowtime>550
82 #error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?"
83#elif w_lowtime>450
84 #warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)."
85 #warning "Please consider a higher clockspeed, if possible"
86#endif
87
88#if w2>0
89#define w2_nops w2
90#else
91#define w2_nops 0
92#endif
93
94#if w3>0
95#define w3_nops w3
96#else
97#define w3_nops 0
98#endif
99
100#define w_nop1 "nop \n\t"
101#define w_nop2 "rjmp .+0 \n\t"
102#define w_nop4 w_nop2 w_nop2
103#define w_nop8 w_nop4 w_nop4
104#define w_nop16 w_nop8 w_nop8
105
106void inline ws2812_sendarray_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi)
107{
108 uint8_t curbyte,ctr,masklo;
109 uint8_t sreg_prev;
110
111 masklo =~maskhi&ws2812_PORTREG;
112 maskhi |= ws2812_PORTREG;
113 sreg_prev=SREG;
114 cli();
115
116 while (datlen--) {
117 curbyte=*data++;
118
119 asm volatile(
120 " ldi %0,8 \n\t"
121 "loop%=: \n\t"
122 " out %2,%3 \n\t" // '1' [01] '0' [01] - re
123#if (w1_nops&1)
124w_nop1
125#endif
126#if (w1_nops&2)
127w_nop2
128#endif
129#if (w1_nops&4)
130w_nop4
131#endif
132#if (w1_nops&8)
133w_nop8
134#endif
135#if (w1_nops&16)
136w_nop16
137#endif
138 " sbrs %1,7 \n\t" // '1' [03] '0' [02]
139 " out %2,%4 \n\t" // '1' [--] '0' [03] - fe-low
140 " lsl %1 \n\t" // '1' [04] '0' [04]
141#if (w2_nops&1)
142 w_nop1
143#endif
144#if (w2_nops&2)
145 w_nop2
146#endif
147#if (w2_nops&4)
148 w_nop4
149#endif
150#if (w2_nops&8)
151 w_nop8
152#endif
153#if (w2_nops&16)
154 w_nop16
155#endif
156 " out %2,%4 \n\t" // '1' [+1] '0' [+1] - fe-high
157#if (w3_nops&1)
158w_nop1
159#endif
160#if (w3_nops&2)
161w_nop2
162#endif
163#if (w3_nops&4)
164w_nop4
165#endif
166#if (w3_nops&8)
167w_nop8
168#endif
169#if (w3_nops&16)
170w_nop16
171#endif
172
173 " dec %0 \n\t" // '1' [+2] '0' [+2]
174 " brne loop%=\n\t" // '1' [+3] '0' [+4]
175 : "=&d" (ctr)
176 : "r" (curbyte), "I" (_SFR_IO_ADDR(ws2812_PORTREG)), "r" (maskhi), "r" (masklo)
177 );
178 }
179
180 SREG=sreg_prev;
181}