]> git.donarmstrong.com Git - qmk_firmware.git/blob - quantum/light_ws2812.c
aac058f53438bb9a0f2da5a703e11ae82a55fa79
[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(LED_TYPE *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(LED_TYPE *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(LED_TYPE *ledarray, uint16_t leds)
154 {
155
156   #ifdef RGBW_BB_TWI
157     uint8_t sreg_prev, twcr_prev;
158     sreg_prev=SREG;
159     twcr_prev=TWCR;
160     cli();
161     TWCR &= ~(1<<TWEN);
162     I2C_Init();
163     I2C_Start();
164     I2C_Write(0x84);
165     uint16_t datlen = leds<<2;
166     uint8_t curbyte;
167     uint8_t * data = (uint8_t*)ledarray;
168     while (datlen--) {
169       curbyte=*data++;
170       I2C_Write(curbyte);
171     }
172     I2C_Stop();
173     SREG=sreg_prev;
174     TWCR=twcr_prev;
175   #endif
176
177
178   // ws2812_DDRREG |= _BV(ws2812_pin); // Enable DDR
179   // new universal format (DDR)
180   _SFR_IO8((RGB_DI_PIN >> 4) + 1) |= _BV(RGB_DI_PIN & 0xF);
181
182   ws2812_sendarray_mask((uint8_t*)ledarray,leds<<2,_BV(RGB_DI_PIN & 0xF));
183
184
185   #ifndef RGBW_BB_TWI
186     _delay_us(80);
187   #endif
188 }
189
190 void ws2812_sendarray(uint8_t *data,uint16_t datlen)
191 {
192   ws2812_sendarray_mask(data,datlen,_BV(RGB_DI_PIN & 0xF));
193 }
194
195 /*
196   This routine writes an array of bytes with RGB values to the Dataout pin
197   using the fast 800kHz clockless WS2811/2812 protocol.
198 */
199
200 // Timing in ns
201 #define w_zeropulse   350
202 #define w_onepulse    900
203 #define w_totalperiod 1250
204
205 // Fixed cycles used by the inner loop
206 #define w_fixedlow    2
207 #define w_fixedhigh   4
208 #define w_fixedtotal  8
209
210 // Insert NOPs to match the timing, if possible
211 #define w_zerocycles    (((F_CPU/1000)*w_zeropulse          )/1000000)
212 #define w_onecycles     (((F_CPU/1000)*w_onepulse    +500000)/1000000)
213 #define w_totalcycles   (((F_CPU/1000)*w_totalperiod +500000)/1000000)
214
215 // w1 - nops between rising edge and falling edge - low
216 #define w1 (w_zerocycles-w_fixedlow)
217 // w2   nops between fe low and fe high
218 #define w2 (w_onecycles-w_fixedhigh-w1)
219 // w3   nops to complete loop
220 #define w3 (w_totalcycles-w_fixedtotal-w1-w2)
221
222 #if w1>0
223   #define w1_nops w1
224 #else
225   #define w1_nops  0
226 #endif
227
228 // The only critical timing parameter is the minimum pulse length of the "0"
229 // Warn or throw error if this timing can not be met with current F_CPU settings.
230 #define w_lowtime ((w1_nops+w_fixedlow)*1000000)/(F_CPU/1000)
231 #if w_lowtime>550
232    #error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?"
233 #elif w_lowtime>450
234    #warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)."
235    #warning "Please consider a higher clockspeed, if possible"
236 #endif
237
238 #if w2>0
239 #define w2_nops w2
240 #else
241 #define w2_nops  0
242 #endif
243
244 #if w3>0
245 #define w3_nops w3
246 #else
247 #define w3_nops  0
248 #endif
249
250 #define w_nop1  "nop      \n\t"
251 #define w_nop2  "rjmp .+0 \n\t"
252 #define w_nop4  w_nop2 w_nop2
253 #define w_nop8  w_nop4 w_nop4
254 #define w_nop16 w_nop8 w_nop8
255
256 void inline ws2812_sendarray_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi)
257 {
258   uint8_t curbyte,ctr,masklo;
259   uint8_t sreg_prev;
260
261   // masklo  =~maskhi&ws2812_PORTREG;
262   // maskhi |=        ws2812_PORTREG;
263   masklo  =~maskhi&_SFR_IO8((RGB_DI_PIN >> 4) + 2);
264   maskhi |=        _SFR_IO8((RGB_DI_PIN >> 4) + 2);
265   sreg_prev=SREG;
266   cli();
267
268   while (datlen--) {
269     curbyte=(*data++);
270
271     asm volatile(
272     "       ldi   %0,8  \n\t"
273     "loop%=:            \n\t"
274     "       out   %2,%3 \n\t"    //  '1' [01] '0' [01] - re
275 #if (w1_nops&1)
276 w_nop1
277 #endif
278 #if (w1_nops&2)
279 w_nop2
280 #endif
281 #if (w1_nops&4)
282 w_nop4
283 #endif
284 #if (w1_nops&8)
285 w_nop8
286 #endif
287 #if (w1_nops&16)
288 w_nop16
289 #endif
290     "       sbrs  %1,7  \n\t"    //  '1' [03] '0' [02]
291     "       out   %2,%4 \n\t"    //  '1' [--] '0' [03] - fe-low
292     "       lsl   %1    \n\t"    //  '1' [04] '0' [04]
293 #if (w2_nops&1)
294   w_nop1
295 #endif
296 #if (w2_nops&2)
297   w_nop2
298 #endif
299 #if (w2_nops&4)
300   w_nop4
301 #endif
302 #if (w2_nops&8)
303   w_nop8
304 #endif
305 #if (w2_nops&16)
306   w_nop16
307 #endif
308     "       out   %2,%4 \n\t"    //  '1' [+1] '0' [+1] - fe-high
309 #if (w3_nops&1)
310 w_nop1
311 #endif
312 #if (w3_nops&2)
313 w_nop2
314 #endif
315 #if (w3_nops&4)
316 w_nop4
317 #endif
318 #if (w3_nops&8)
319 w_nop8
320 #endif
321 #if (w3_nops&16)
322 w_nop16
323 #endif
324
325     "       dec   %0    \n\t"    //  '1' [+2] '0' [+2]
326     "       brne  loop%=\n\t"    //  '1' [+3] '0' [+4]
327     :   "=&d" (ctr)
328     :   "r" (curbyte), "I" (_SFR_IO_ADDR(_SFR_IO8((RGB_DI_PIN >> 4) + 2))), "r" (maskhi), "r" (masklo)
329     );
330   }
331
332   SREG=sreg_prev;
333 }