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