]> git.donarmstrong.com Git - qmk_firmware.git/blob - quantum/light_ws2812.c
Initial commit for Atreus50 with working audio
[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 #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()\
31 I2C_DDR &= ~ (1 << I2C_DAT);\
32 I2C_PORT |= (1 << I2C_DAT);
33 #define I2C_DATA_LO()\
34 I2C_DDR |= (1 << I2C_DAT);\
35 I2C_PORT &= ~ (1 << I2C_DAT);
36
37 #define I2C_CLOCK_HI()\
38 I2C_DDR &= ~ (1 << I2C_CLK);\
39 I2C_PORT |= (1 << I2C_CLK);
40 #define I2C_CLOCK_LO()\
41 I2C_DDR |= (1 << I2C_CLK);\
42 I2C_PORT &= ~ (1 << I2C_CLK);
43
44 #define I2C_DELAY 1
45
46 void 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 //
73 void I2C_Init(void)
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 //
85 void I2C_Start(void)
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 //
100 void I2C_Stop(void)
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 //
111 unsigned 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
133 // Setleds for standard RGB
134 void inline ws2812_setleds(LED_TYPE *ledarray, uint16_t leds)
135 {
136    // ws2812_setleds_pin(ledarray,leds, _BV(ws2812_pin));
137    ws2812_setleds_pin(ledarray,leds, _BV(RGB_DI_PIN & 0xF));
138 }
139
140 void inline ws2812_setleds_pin(LED_TYPE *ledarray, uint16_t leds, uint8_t pinmask)
141 {
142   // ws2812_DDRREG |= pinmask; // Enable DDR
143   // new universal format (DDR)
144   _SFR_IO8((RGB_DI_PIN >> 4) + 1) |= pinmask;
145
146   ws2812_sendarray_mask((uint8_t*)ledarray,leds+leds+leds,pinmask);
147   _delay_us(50);
148 }
149
150 // Setleds for SK6812RGBW
151 void inline ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t leds)
152 {
153
154   #ifdef RGBW_BB_TWI
155     uint8_t sreg_prev, twcr_prev;
156     sreg_prev=SREG;
157     twcr_prev=TWCR;
158     cli();
159     TWCR &= ~(1<<TWEN);
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++;
168       I2C_Write(curbyte);
169     }
170     I2C_Stop();
171     SREG=sreg_prev;
172     TWCR=twcr_prev;
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   #ifndef RGBW_BB_TWI
184     _delay_us(80);
185   #endif
186 }
187
188 void ws2812_sendarray(uint8_t *data,uint16_t datlen)
189 {
190   ws2812_sendarray_mask(data,datlen,_BV(RGB_DI_PIN & 0xF));
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
254 void 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
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);
263   sreg_prev=SREG;
264   cli();
265
266   while (datlen--) {
267     curbyte=(*data++);
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)
274 w_nop1
275 #endif
276 #if (w1_nops&2)
277 w_nop2
278 #endif
279 #if (w1_nops&4)
280 w_nop4
281 #endif
282 #if (w1_nops&8)
283 w_nop8
284 #endif
285 #if (w1_nops&16)
286 w_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)
308 w_nop1
309 #endif
310 #if (w3_nops&2)
311 w_nop2
312 #endif
313 #if (w3_nops&4)
314 w_nop4
315 #endif
316 #if (w3_nops&8)
317 w_nop8
318 #endif
319 #if (w3_nops&16)
320 w_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)
326     :   "r" (curbyte), "I" (_SFR_IO_ADDR(_SFR_IO8((RGB_DI_PIN >> 4) + 2))), "r" (maskhi), "r" (masklo)
327     );
328   }
329
330   SREG=sreg_prev;
331 }