aboutsummaryrefslogtreecommitdiff
path: root/drivers/avr/ws2812.c
diff options
context:
space:
mode:
authorJack Humbert <jack.humb@gmail.com>2017-07-10 11:18:47 -0400
committerGitHub <noreply@github.com>2017-07-10 11:18:47 -0400
commit42d5a324eb673b2214a87c8911d850105c3bdaab (patch)
treec14aca23eb74164d5cd7e69f50e6d347b3ace51a /drivers/avr/ws2812.c
parent8d190d5e25b3374156264fde0ba5d78696cc74aa (diff)
downloadqmk_firmware-42d5a324eb673b2214a87c8911d850105c3bdaab.tar.gz
qmk_firmware-42d5a324eb673b2214a87c8911d850105c3bdaab.zip
Start mvoing hardware drivers to /drivers/ (#1433)
* start driver isolation * update nyquist and orthodox boards * update atreus62 * move drivers to avr * update avr conditional
Diffstat (limited to 'drivers/avr/ws2812.c')
-rw-r--r--drivers/avr/ws2812.c342
1 files changed, 342 insertions, 0 deletions
diff --git a/drivers/avr/ws2812.c b/drivers/avr/ws2812.c
new file mode 100644
index 000000000..59e032bf7
--- /dev/null
+++ b/drivers/avr/ws2812.c
@@ -0,0 +1,342 @@
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* This program is free software: you can redistribute it and/or modify
11* it under the terms of the GNU General Public License as published by
12* the Free Software Foundation, either version 2 of the License, or
13* (at your option) any later version.
14*
15* This program is distributed in the hope that it will be useful,
16* but WITHOUT ANY WARRANTY; without even the implied warranty of
17* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18* GNU General Public License for more details.
19*
20* You should have received a copy of the GNU General Public License
21* along with this program. If not, see <http://www.gnu.org/licenses/>.
22*/
23
24#include "ws2812.h"
25#include <avr/interrupt.h>
26#include <avr/io.h>
27#include <util/delay.h>
28#include "debug.h"
29
30#ifdef RGBW_BB_TWI
31
32// Port for the I2C
33#define I2C_DDR DDRD
34#define I2C_PIN PIND
35#define I2C_PORT PORTD
36
37// Pins to be used in the bit banging
38#define I2C_CLK 0
39#define I2C_DAT 1
40
41#define I2C_DATA_HI()\
42I2C_DDR &= ~ (1 << I2C_DAT);\
43I2C_PORT |= (1 << I2C_DAT);
44#define I2C_DATA_LO()\
45I2C_DDR |= (1 << I2C_DAT);\
46I2C_PORT &= ~ (1 << I2C_DAT);
47
48#define I2C_CLOCK_HI()\
49I2C_DDR &= ~ (1 << I2C_CLK);\
50I2C_PORT |= (1 << I2C_CLK);
51#define I2C_CLOCK_LO()\
52I2C_DDR |= (1 << I2C_CLK);\
53I2C_PORT &= ~ (1 << I2C_CLK);
54
55#define I2C_DELAY 1
56
57void I2C_WriteBit(unsigned char c)
58{
59 if (c > 0)
60 {
61 I2C_DATA_HI();
62 }
63 else
64 {
65 I2C_DATA_LO();
66 }
67
68 I2C_CLOCK_HI();
69 _delay_us(I2C_DELAY);
70
71 I2C_CLOCK_LO();
72 _delay_us(I2C_DELAY);
73
74 if (c > 0)
75 {
76 I2C_DATA_LO();
77 }
78
79 _delay_us(I2C_DELAY);
80}
81
82// Inits bitbanging port, must be called before using the functions below
83//
84void I2C_Init(void)
85{
86 I2C_PORT &= ~ ((1 << I2C_DAT) | (1 << I2C_CLK));
87
88 I2C_CLOCK_HI();
89 I2C_DATA_HI();
90
91 _delay_us(I2C_DELAY);
92}
93
94// Send a START Condition
95//
96void I2C_Start(void)
97{
98 // set both to high at the same time
99 I2C_DDR &= ~ ((1 << I2C_DAT) | (1 << I2C_CLK));
100 _delay_us(I2C_DELAY);
101
102 I2C_DATA_LO();
103 _delay_us(I2C_DELAY);
104
105 I2C_CLOCK_LO();
106 _delay_us(I2C_DELAY);
107}
108
109// Send a STOP Condition
110//
111void I2C_Stop(void)
112{
113 I2C_CLOCK_HI();
114 _delay_us(I2C_DELAY);
115
116 I2C_DATA_HI();
117 _delay_us(I2C_DELAY);
118}
119
120// write a byte to the I2C slave device
121//
122unsigned char I2C_Write(unsigned char c)
123{
124 for (char i = 0; i < 8; i++)
125 {
126 I2C_WriteBit(c & 128);
127
128 c <<= 1;
129 }
130
131
132 I2C_WriteBit(0);
133 _delay_us(I2C_DELAY);
134 _delay_us(I2C_DELAY);
135
136 // _delay_us(I2C_DELAY);
137 //return I2C_ReadBit();
138 return 0;
139}
140
141
142#endif
143
144// Setleds for standard RGB
145void inline ws2812_setleds(LED_TYPE *ledarray, uint16_t leds)
146{
147 // ws2812_setleds_pin(ledarray,leds, _BV(ws2812_pin));
148 ws2812_setleds_pin(ledarray,leds, _BV(RGB_DI_PIN & 0xF));
149}
150
151void inline ws2812_setleds_pin(LED_TYPE *ledarray, uint16_t leds, uint8_t pinmask)
152{
153 // ws2812_DDRREG |= pinmask; // Enable DDR
154 // new universal format (DDR)
155 _SFR_IO8((RGB_DI_PIN >> 4) + 1) |= pinmask;
156
157 ws2812_sendarray_mask((uint8_t*)ledarray,leds+leds+leds,pinmask);
158 _delay_us(50);
159}
160
161// Setleds for SK6812RGBW
162void inline ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t leds)
163{
164
165 #ifdef RGBW_BB_TWI
166 uint8_t sreg_prev, twcr_prev;
167 sreg_prev=SREG;
168 twcr_prev=TWCR;
169 cli();
170 TWCR &= ~(1<<TWEN);
171 I2C_Init();
172 I2C_Start();
173 I2C_Write(0x84);
174 uint16_t datlen = leds<<2;
175 uint8_t curbyte;
176 uint8_t * data = (uint8_t*)ledarray;
177 while (datlen--) {
178 curbyte=*data++;
179 I2C_Write(curbyte);
180 }
181 I2C_Stop();
182 SREG=sreg_prev;
183 TWCR=twcr_prev;
184 #endif
185
186
187 // ws2812_DDRREG |= _BV(ws2812_pin); // Enable DDR
188 // new universal format (DDR)
189 _SFR_IO8((RGB_DI_PIN >> 4) + 1) |= _BV(RGB_DI_PIN & 0xF);
190
191 ws2812_sendarray_mask((uint8_t*)ledarray,leds<<2,_BV(RGB_DI_PIN & 0xF));
192
193
194 #ifndef RGBW_BB_TWI
195 _delay_us(80);
196 #endif
197}
198
199void ws2812_sendarray(uint8_t *data,uint16_t datlen)
200{
201 ws2812_sendarray_mask(data,datlen,_BV(RGB_DI_PIN & 0xF));
202}
203
204/*
205 This routine writes an array of bytes with RGB values to the Dataout pin
206 using the fast 800kHz clockless WS2811/2812 protocol.
207*/
208
209// Timing in ns
210#define w_zeropulse 350
211#define w_onepulse 900
212#define w_totalperiod 1250
213
214// Fixed cycles used by the inner loop
215#define w_fixedlow 2
216#define w_fixedhigh 4
217#define w_fixedtotal 8
218
219// Insert NOPs to match the timing, if possible
220#define w_zerocycles (((F_CPU/1000)*w_zeropulse )/1000000)
221#define w_onecycles (((F_CPU/1000)*w_onepulse +500000)/1000000)
222#define w_totalcycles (((F_CPU/1000)*w_totalperiod +500000)/1000000)
223
224// w1 - nops between rising edge and falling edge - low
225#define w1 (w_zerocycles-w_fixedlow)
226// w2 nops between fe low and fe high
227#define w2 (w_onecycles-w_fixedhigh-w1)
228// w3 nops to complete loop
229#define w3 (w_totalcycles-w_fixedtotal-w1-w2)
230
231#if w1>0
232 #define w1_nops w1
233#else
234 #define w1_nops 0
235#endif
236
237// The only critical timing parameter is the minimum pulse length of the "0"
238// Warn or throw error if this timing can not be met with current F_CPU settings.
239#define w_lowtime ((w1_nops+w_fixedlow)*1000000)/(F_CPU/1000)
240#if w_lowtime>550
241 #error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?"
242#elif w_lowtime>450
243 #warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)."
244 #warning "Please consider a higher clockspeed, if possible"
245#endif
246
247#if w2>0
248#define w2_nops w2
249#else
250#define w2_nops 0
251#endif
252
253#if w3>0
254#define w3_nops w3
255#else
256#define w3_nops 0
257#endif
258
259#define w_nop1 "nop \n\t"
260#define w_nop2 "rjmp .+0 \n\t"
261#define w_nop4 w_nop2 w_nop2
262#define w_nop8 w_nop4 w_nop4
263#define w_nop16 w_nop8 w_nop8
264
265void inline ws2812_sendarray_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi)
266{
267 uint8_t curbyte,ctr,masklo;
268 uint8_t sreg_prev;
269
270 // masklo =~maskhi&ws2812_PORTREG;
271 // maskhi |= ws2812_PORTREG;
272 masklo =~maskhi&_SFR_IO8((RGB_DI_PIN >> 4) + 2);
273 maskhi |= _SFR_IO8((RGB_DI_PIN >> 4) + 2);
274 sreg_prev=SREG;
275 cli();
276
277 while (datlen--) {
278 curbyte=(*data++);
279
280 asm volatile(
281 " ldi %0,8 \n\t"
282 "loop%=: \n\t"
283 " out %2,%3 \n\t" // '1' [01] '0' [01] - re
284#if (w1_nops&1)
285w_nop1
286#endif
287#if (w1_nops&2)
288w_nop2
289#endif
290#if (w1_nops&4)
291w_nop4
292#endif
293#if (w1_nops&8)
294w_nop8
295#endif
296#if (w1_nops&16)
297w_nop16
298#endif
299 " sbrs %1,7 \n\t" // '1' [03] '0' [02]
300 " out %2,%4 \n\t" // '1' [--] '0' [03] - fe-low
301 " lsl %1 \n\t" // '1' [04] '0' [04]
302#if (w2_nops&1)
303 w_nop1
304#endif
305#if (w2_nops&2)
306 w_nop2
307#endif
308#if (w2_nops&4)
309 w_nop4
310#endif
311#if (w2_nops&8)
312 w_nop8
313#endif
314#if (w2_nops&16)
315 w_nop16
316#endif
317 " out %2,%4 \n\t" // '1' [+1] '0' [+1] - fe-high
318#if (w3_nops&1)
319w_nop1
320#endif
321#if (w3_nops&2)
322w_nop2
323#endif
324#if (w3_nops&4)
325w_nop4
326#endif
327#if (w3_nops&8)
328w_nop8
329#endif
330#if (w3_nops&16)
331w_nop16
332#endif
333
334 " dec %0 \n\t" // '1' [+2] '0' [+2]
335 " brne loop%=\n\t" // '1' [+3] '0' [+4]
336 : "=&d" (ctr)
337 : "r" (curbyte), "I" (_SFR_IO_ADDR(_SFR_IO8((RGB_DI_PIN >> 4) + 2))), "r" (maskhi), "r" (masklo)
338 );
339 }
340
341 SREG=sreg_prev;
342}