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