fix whitespace
[jackhill/qmk/firmware.git] / quantum / light_ws2812.c
CommitLineData
0a40654b
YL
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
5f91fb41
JH
19#ifdef RGBW_BB_TWI
20
21// Port for the I2C
22#define I2C_DDR DDRD
23#define I2C_PIN PIND
24#define I2C_PORT PORTD
25
26// Pins to be used in the bit banging
27#define I2C_CLK 0
28#define I2C_DAT 1
29
30#define I2C_DATA_HI()\
31I2C_DDR &= ~ (1 << I2C_DAT);\
32I2C_PORT |= (1 << I2C_DAT);
33#define I2C_DATA_LO()\
34I2C_DDR |= (1 << I2C_DAT);\
35I2C_PORT &= ~ (1 << I2C_DAT);
36
37#define I2C_CLOCK_HI()\
38I2C_DDR &= ~ (1 << I2C_CLK);\
39I2C_PORT |= (1 << I2C_CLK);
40#define I2C_CLOCK_LO()\
41I2C_DDR |= (1 << I2C_CLK);\
42I2C_PORT &= ~ (1 << I2C_CLK);
43
44#define I2C_DELAY 1
45
46void I2C_WriteBit(unsigned char c)
47{
48 if (c > 0)
49 {
50 I2C_DATA_HI();
51 }
52 else
53 {
54 I2C_DATA_LO();
55 }
56
57 I2C_CLOCK_HI();
58 _delay_us(I2C_DELAY);
59
60 I2C_CLOCK_LO();
61 _delay_us(I2C_DELAY);
62
63 if (c > 0)
64 {
65 I2C_DATA_LO();
66 }
67
68 _delay_us(I2C_DELAY);
69}
70
71// Inits bitbanging port, must be called before using the functions below
72//
e667e9f6 73void I2C_Init(void)
5f91fb41
JH
74{
75 I2C_PORT &= ~ ((1 << I2C_DAT) | (1 << I2C_CLK));
76
77 I2C_CLOCK_HI();
78 I2C_DATA_HI();
79
80 _delay_us(I2C_DELAY);
81}
82
83// Send a START Condition
84//
e667e9f6 85void I2C_Start(void)
5f91fb41
JH
86{
87 // set both to high at the same time
88 I2C_DDR &= ~ ((1 << I2C_DAT) | (1 << I2C_CLK));
89 _delay_us(I2C_DELAY);
90
91 I2C_DATA_LO();
92 _delay_us(I2C_DELAY);
93
94 I2C_CLOCK_LO();
95 _delay_us(I2C_DELAY);
96}
97
98// Send a STOP Condition
99//
e667e9f6 100void I2C_Stop(void)
5f91fb41
JH
101{
102 I2C_CLOCK_HI();
103 _delay_us(I2C_DELAY);
104
105 I2C_DATA_HI();
106 _delay_us(I2C_DELAY);
107}
108
109// write a byte to the I2C slave device
110//
111unsigned char I2C_Write(unsigned char c)
112{
113 for (char i = 0; i < 8; i++)
114 {
115 I2C_WriteBit(c & 128);
116
117 c <<= 1;
118 }
119
120
121 I2C_WriteBit(0);
122 _delay_us(I2C_DELAY);
123 _delay_us(I2C_DELAY);
124
125 // _delay_us(I2C_DELAY);
126 //return I2C_ReadBit();
127 return 0;
128}
129
130
131#endif
132
0a40654b 133// Setleds for standard RGB
e9f74875 134void inline ws2812_setleds(LED_TYPE *ledarray, uint16_t leds)
0a40654b 135{
57e08eb8
JH
136 // ws2812_setleds_pin(ledarray,leds, _BV(ws2812_pin));
137 ws2812_setleds_pin(ledarray,leds, _BV(RGB_DI_PIN & 0xF));
0a40654b
YL
138}
139
e9f74875 140void inline ws2812_setleds_pin(LED_TYPE *ledarray, uint16_t leds, uint8_t pinmask)
0a40654b 141{
57e08eb8
JH
142 // ws2812_DDRREG |= pinmask; // Enable DDR
143 // new universal format (DDR)
144 _SFR_IO8((RGB_DI_PIN >> 4) + 1) |= pinmask;
145
0a40654b
YL
146 ws2812_sendarray_mask((uint8_t*)ledarray,leds+leds+leds,pinmask);
147 _delay_us(50);
148}
149
150// Setleds for SK6812RGBW
e9f74875 151void inline ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t leds)
0a40654b 152{
5f91fb41
JH
153
154 #ifdef RGBW_BB_TWI
e9f74875
JH
155 uint8_t sreg_prev, twcr_prev;
156 sreg_prev=SREG;
157 twcr_prev=TWCR;
5f91fb41 158 cli();
e9f74875 159 TWCR &= ~(1<<TWEN);
5f91fb41
JH
160 I2C_Init();
161 I2C_Start();
162 I2C_Write(0x84);
163 uint16_t datlen = leds<<2;
164 uint8_t curbyte;
165 uint8_t * data = (uint8_t*)ledarray;
166 while (datlen--) {
167 curbyte=*data++;
33e62c08 168 I2C_Write(curbyte);
5f91fb41
JH
169 }
170 I2C_Stop();
e9f74875 171 SREG=sreg_prev;
e9f74875 172 TWCR=twcr_prev;
5f91fb41
JH
173 #endif
174
175
57e08eb8
JH
176 // ws2812_DDRREG |= _BV(ws2812_pin); // Enable DDR
177 // new universal format (DDR)
178 _SFR_IO8((RGB_DI_PIN >> 4) + 1) |= _BV(RGB_DI_PIN & 0xF);
179
180 ws2812_sendarray_mask((uint8_t*)ledarray,leds<<2,_BV(RGB_DI_PIN & 0xF));
33e62c08
JH
181
182
e9f74875
JH
183 #ifndef RGBW_BB_TWI
184 _delay_us(80);
185 #endif
0a40654b
YL
186}
187
188void ws2812_sendarray(uint8_t *data,uint16_t datlen)
189{
57e08eb8 190 ws2812_sendarray_mask(data,datlen,_BV(RGB_DI_PIN & 0xF));
0a40654b
YL
191}
192
193/*
194 This routine writes an array of bytes with RGB values to the Dataout pin
195 using the fast 800kHz clockless WS2811/2812 protocol.
196*/
197
198// Timing in ns
199#define w_zeropulse 350
200#define w_onepulse 900
201#define w_totalperiod 1250
202
203// Fixed cycles used by the inner loop
204#define w_fixedlow 2
205#define w_fixedhigh 4
206#define w_fixedtotal 8
207
208// Insert NOPs to match the timing, if possible
209#define w_zerocycles (((F_CPU/1000)*w_zeropulse )/1000000)
210#define w_onecycles (((F_CPU/1000)*w_onepulse +500000)/1000000)
211#define w_totalcycles (((F_CPU/1000)*w_totalperiod +500000)/1000000)
212
213// w1 - nops between rising edge and falling edge - low
214#define w1 (w_zerocycles-w_fixedlow)
215// w2 nops between fe low and fe high
216#define w2 (w_onecycles-w_fixedhigh-w1)
217// w3 nops to complete loop
218#define w3 (w_totalcycles-w_fixedtotal-w1-w2)
219
220#if w1>0
221 #define w1_nops w1
222#else
223 #define w1_nops 0
224#endif
225
226// The only critical timing parameter is the minimum pulse length of the "0"
227// Warn or throw error if this timing can not be met with current F_CPU settings.
228#define w_lowtime ((w1_nops+w_fixedlow)*1000000)/(F_CPU/1000)
229#if w_lowtime>550
230 #error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?"
231#elif w_lowtime>450
232 #warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)."
233 #warning "Please consider a higher clockspeed, if possible"
234#endif
235
236#if w2>0
237#define w2_nops w2
238#else
239#define w2_nops 0
240#endif
241
242#if w3>0
243#define w3_nops w3
244#else
245#define w3_nops 0
246#endif
247
248#define w_nop1 "nop \n\t"
249#define w_nop2 "rjmp .+0 \n\t"
250#define w_nop4 w_nop2 w_nop2
251#define w_nop8 w_nop4 w_nop4
252#define w_nop16 w_nop8 w_nop8
253
254void inline ws2812_sendarray_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi)
255{
256 uint8_t curbyte,ctr,masklo;
257 uint8_t sreg_prev;
258
57e08eb8
JH
259 // masklo =~maskhi&ws2812_PORTREG;
260 // maskhi |= ws2812_PORTREG;
261 masklo =~maskhi&_SFR_IO8((RGB_DI_PIN >> 4) + 2);
262 maskhi |= _SFR_IO8((RGB_DI_PIN >> 4) + 2);
0a40654b
YL
263 sreg_prev=SREG;
264 cli();
265
266 while (datlen--) {
33e62c08 267 curbyte=(*data++);
0a40654b
YL
268
269 asm volatile(
270 " ldi %0,8 \n\t"
271 "loop%=: \n\t"
272 " out %2,%3 \n\t" // '1' [01] '0' [01] - re
273#if (w1_nops&1)
274w_nop1
275#endif
276#if (w1_nops&2)
277w_nop2
278#endif
279#if (w1_nops&4)
280w_nop4
281#endif
282#if (w1_nops&8)
283w_nop8
284#endif
285#if (w1_nops&16)
286w_nop16
287#endif
288 " sbrs %1,7 \n\t" // '1' [03] '0' [02]
289 " out %2,%4 \n\t" // '1' [--] '0' [03] - fe-low
290 " lsl %1 \n\t" // '1' [04] '0' [04]
291#if (w2_nops&1)
292 w_nop1
293#endif
294#if (w2_nops&2)
295 w_nop2
296#endif
297#if (w2_nops&4)
298 w_nop4
299#endif
300#if (w2_nops&8)
301 w_nop8
302#endif
303#if (w2_nops&16)
304 w_nop16
305#endif
306 " out %2,%4 \n\t" // '1' [+1] '0' [+1] - fe-high
307#if (w3_nops&1)
308w_nop1
309#endif
310#if (w3_nops&2)
311w_nop2
312#endif
313#if (w3_nops&4)
314w_nop4
315#endif
316#if (w3_nops&8)
317w_nop8
318#endif
319#if (w3_nops&16)
320w_nop16
321#endif
322
323 " dec %0 \n\t" // '1' [+2] '0' [+2]
324 " brne loop%=\n\t" // '1' [+3] '0' [+4]
325 : "=&d" (ctr)
57e08eb8 326 : "r" (curbyte), "I" (_SFR_IO_ADDR(_SFR_IO8((RGB_DI_PIN >> 4) + 2))), "r" (maskhi), "r" (masklo)
0a40654b
YL
327 );
328 }
329
330 SREG=sreg_prev;
331}