-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathHCWS2812.cpp
511 lines (426 loc) · 12.1 KB
/
HCWS2812.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
/* FILE: HCMAX7219.cpp
DATE: 22/02/24
VERSION: 2.0
AUTHOR: Andrew Davies
11/03/15 version 0.1: Original version
13/04/15 version 0.2: Updated timings to work with 1.x.x versions of Arduino IDE
30/11/16 version 0.3: Updated to support ESP8266
Text font moved to program memory to save ram.
14/07/23 version 1.0: Updated to allow user to specify the output pin
22/02/24 version 2.0: Implemented output code into in-line assembly for 8bit AVR
microcontrollers for better timing.
Removed option for setting output pin via library constructor
as 8bit AVRs are not fast enough to support - Uncomment
#define DOUT_PIN line HCMAX7219.h file and set pin there.
Library for WS2812 serial RGB LEDs.
You may copy, alter and reuse this code in any way you like, but please leave
reference to HobbyComponents.com in your comments if you redistribute this code.
This software may not be used directly for the purpose of selling products that
directly compete with Hobby Components Ltd's own range of products.
THIS SOFTWARE IS PROVIDED "AS IS". HOBBY COMPONENTS MAKES NO WARRANTIES, WHETHER
EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ACCURACY OR LACK OF NEGLIGENCE.
HOBBY COMPONENTS SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR ANY DAMAGES,
INCLUDING, BUT NOT LIMITED TO, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY
REASON WHATSOEVER.
*/
#include "HCWS2812.h"
#include "Arduino.h"
/* Output buffer */
byte RGBBuffer[3][NUMBEROFLEDS];
/* Constructor to initialise the GPIO and library */
HCWS2812::HCWS2812(void)
{
// Define the output pin
#if defined(DOUT_PIN)
pinMode(DOUT_PIN, OUTPUT);
#else
pinMode(DEF_DOUT_PIN, OUTPUT);
#endif
// Clear the output buffer with the selected background colour
SetBG(0, 0, 0);
ClearBuffer();
#ifdef DOTMATRIX
SetFontFG(100, 100, 100);
SetFontBG(0, 0, 0);
#endif
}
/* Loads a string of text to the output buffer */
#ifdef DOTMATRIX
void HCWS2812::print(char TextString[], unsigned int Offset)
{
_CopyToBuffer(TextString, strlen(TextString), Offset);
}
#endif
#ifdef DOTMATRIX
/* Loads a decimal number into the output buffer.
Number is a decimal number of type int.
Offset is the display column number to to start the text from. */
void HCWS2812::print(long number, unsigned int Offset)
{
char Digits[10];
byte index = 0;
long Temp;
Temp = number;
/*Is the number negative ? If so then remove the sign */
if (Temp < 0)
Temp *= -1;
/* Is the number zero ? */
if (Temp == 0)
{
Digits[index] = '0';
index++;
}else
{
/* Convert the number to an ASCII decimal string */
while (Temp)
{
Digits[index] = (Temp % 10) + 48;
Temp /= 10;
index++;
}
}
/* If the number was negative add the sign */
if (number < 0)
{
Digits[index] = '-';
index++;
}
_ReverseArray(Digits, index);
/* Copy the number to the output buffer */
_CopyToBuffer(Digits, index, Offset);
}
#endif
#ifdef DOTMATRIX
/* Loads a decimal number with decimal point into the output buffer.
Number is a decimal number of type int.
decimalPlace is the digit position to add a decimal point to.
Offset is the display column number to to start the text from */
void HCWS2812::print(long number, byte decimalPlace, unsigned int Offset)
{
char Digits[10];
unsigned charindex;
byte index = 0;
long Temp;
unsigned int bufferindex;
Temp = number;
/*Is the number negative ? If so then remove the sign */
if (Temp < 0)
Temp *= -1;
/* Is the number zero ? */
if (Temp == 0)
{
Digits[index] = '0';
index++;
}else
{
/* Convert the number to an ASCII decimal string */
while (Temp)
{
if(index == decimalPlace)
{
Digits[index] = '.';
index++;
}
Digits[index] = (Temp % 10) + 48;
Temp /= 10;
index++;
}
}
/* If decimal place is at the beginning of the number then pad it
with a zero */
if (decimalPlace == index)
{
Digits[index] = '.';
index++;
Digits[index] = '0';
index++;
}
/* If the number was negative add the sign */
if (number < 0)
{
Digits[index] = '-';
index++;
}
_ReverseArray(Digits, index);
/* Copy the number to the output buffer */
_CopyToBuffer(Digits, index, Offset);
}
#endif
/* Clears the output buffer using the background colour */
void HCWS2812::ClearBuffer(void)
{
int index;
for(index = 0; index < NUMBEROFLEDS; index++)
{
RGBBuffer[GREEN][index] = _BGColour[GREEN];
RGBBuffer[RED][index] = _BGColour[RED];
RGBBuffer[BLUE][index] = _BGColour[BLUE];
}
}
#ifdef DOTMATRIX
/* Copy a character array to the output buffer */
void HCWS2812::_CopyToBuffer(char Array[], int ArraySize, unsigned int Offset)
{
unsigned int bufferindex;
unsigned int charindex;
byte CurCharacter;
byte PixelIndex;
if(Offset)
{
/* Set output buffer pointer */
bufferindex = Offset * 8;
if(bufferindex >= NUMBEROFLEDS)
{
bufferindex = NUMBEROFLEDS;
}
/*If text runs beyond the output buffer then crop it */
charindex = 0;
if (Offset > (NUMBEROFLEDS / 8))
charindex = Offset - (NUMBEROFLEDS / 8);
/* Copy the text into the buffer */
while(bufferindex && charindex < (ArraySize * 8))
{
CurCharacter = Array[charindex / 8] - 32;
for(PixelIndex = 0; PixelIndex < 8; PixelIndex++)
{
bufferindex--;
if((pgm_read_byte ( &(Font8x8[CurCharacter][(charindex%8)])) >> PixelIndex) & 0x01)
{
RGBBuffer[BLUE][bufferindex] = _FontFGColour[BLUE];
RGBBuffer[RED][bufferindex] = _FontFGColour[RED];
RGBBuffer[GREEN][bufferindex] = _FontFGColour[GREEN];
}else
{
RGBBuffer[RED][bufferindex] = _FontBGColour[RED];
RGBBuffer[BLUE][bufferindex] = _FontBGColour[BLUE];
RGBBuffer[GREEN][bufferindex] = _FontBGColour[GREEN];
}
}
charindex++;
}
}
}
#endif
/* Write the contents of the output buffer to the LED's.
To ensure accurate timing interrupts are disabled whilst this
function is executed */
void HCWS2812::Refresh(void)
{
uint8_t r , g, b;
// Disable interrupts
cli();
for(uint16_t index = 0; index < NUMBEROFLEDS; index++)
{
r = RGBBuffer[RED][index];
g = RGBBuffer[GREEN][index];
b = RGBBuffer[BLUE][index];
_writeRGB(r, g, b);
}
// Enable interrupts
sei();
}
#ifdef DOTMATRIX
/* Sets the RGB colour if the font
R is an integer number of type byte between 0 and 255
which sets the intensity of the red element.
G is an integer number of type byte between 0 and 255
which sets the intensity of the green element.
B is an integer number of type byte between 0 and 255
which sets the intensity of the blue element. */
void HCWS2812::SetFontFG(byte R, byte G, byte B)
{
_FontFGColour[RED] = R;
_FontFGColour[GREEN] = G;
_FontFGColour[BLUE] = B;
}
#endif
#ifdef DOTMATRIX
/* Sets the RGB colour if the fonts background.
R is an integer number of type byte between 0 and 255
which sets the intensity of the red element.
G is an integer number of type byte between 0 and 255
which sets the intensity of the green element.
B is an integer number of type byte between 0 and 255
which sets the intensity of the blue element. */
void HCWS2812::SetFontBG(byte R, byte G, byte B)
{
_FontBGColour[RED] = R;
_FontBGColour[GREEN] = G;
_FontBGColour[BLUE] = B;
}
#endif
/* Sets the RGB colour if the background.
R is an integer number of type byte between 0 and 255
which sets the intensity of the red element.
G is an integer number of type byte between 0 and 255
which sets the intensity of the green element.
B is an integer number of type byte between 0 and 255
which sets the intensity of the blue element. */
void HCWS2812::SetBG(byte R, byte G, byte B)
{
_BGColour[RED] = R;
_BGColour[GREEN] = G;
_BGColour[BLUE] = B;
}
/* Internal function that writes the RGB data to the WS2812 LEDs */
void HCWS2812::_writeRGB(uint8_t r, uint8_t g, uint8_t b)
{
// 8 Bit AVR based microcontrollers only
#if defined (__AVR_ATmega328P__) || (__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__)
asm volatile
(
"mov __tmp_reg__, %2 \n\t"
"rcall WRITEBYTE \n\t"
"mov __tmp_reg__, %3 \n\t"
"rcall WRITEBYTE \n\t"
"mov __tmp_reg__, %4 \n\t"
"rcall WRITEBYTE \n\t"
"jmp QUIT \n\t"
"WRITEBYTE:\n\t" // Write out 8 bits
"rcall WRITEBIT \n\t"
"rcall WRITEBIT \n\t"
"rcall WRITEBIT \n\t"
"rcall WRITEBIT \n\t"
"rcall WRITEBIT \n\t"
"rcall WRITEBIT \n\t"
"rcall WRITEBIT \n\t"
"rcall WRITEBIT \n\t"
"ret\n\t"
"WRITEBIT: \n\t" // Write 1 bit
"rol __tmp_reg__ \n\t"
"brcs BITHIGH\n\t" // Bit is low
"rcall WRITEBITLOW\n\t"
"ret\n\t"
"BITHIGH:\n\t" // Bit is high
"rcall WRITEBITHIGH\n\t"
"ret\n\t"
"WRITEBITHIGH:\n\t" // Write a high bit (800ns high / low < 50 us)
"sbi %0, %1 \n\t"
"nop \n\t"
"nop \n\t"
"nop \n\t"
"nop \n\t"
"nop \n\t"
"nop \n\t"
"nop \n\t"
"nop \n\t"
"nop \n\t"
"nop \n\t"
"nop \n\t"
"cbi %0, %1 \n\t"
"ret\n\t"
"WRITEBITLOW:\n\t" // Write a low bit (400ns high / low < 50 us)
"sbi %0, %1 \n\t"
"nop \n\t"
"nop \n\t"
"nop \n\t"
"nop \n\t"
"cbi %0, %1 \n\t"
"ret\n\t"
"QUIT:\n\t" // Exit
#if defined(DOUT_PIN)
:: "I" (dOutPort[DOUT_PIN]), "I" (dOutPin[DOUT_PIN]), "r" (g), "r" (r), "r" (b)
#else
:: "I" (dOutPort[DEF_DOUT_PIN]), "I" (dOutPin[DEF_DOUT_PIN]), "r" (g), "r" (r), "r" (b)
#endif
);
// ESP8266 based microcontrollers only
#elif defined (ESP8266)
_sendByte(g);
_sendByte(r);
_sendByte(b);
#endif
}
/* Faster microcontrollers can use less in-line assembly
to write data out */
#if defined (ESP8266)
void HCWS2812::_sendByte(uint8_t data)
{
for(uint8_t i = 0; i < 8; i++)
{
if(data & 0x80)
{
#if defined(DOUT_PIN)
GPOS = 1 << (DOUT_PIN);
#else
GPOS = 1 << (DEF_DOUT_PIN);
#endif
// 0.8us delay
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;");
#if (F_CPU == 160000000)
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;");
#endif
#if defined(DOUT_PIN)
GPOC = 1 << (DOUT_PIN);
#else
GPOC = 1 << (DEF_DOUT_PIN);
#endif
// 0.45us delay
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop;");
}else
{
#if defined(DOUT_PIN)
GPOS = 1 << (DOUT_PIN);
#else
GPOS = 1 << (DEF_DOUT_PIN);
#endif
// 0.4us delay
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop;");
#if (F_CPU == 160000000)
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;");
#endif
#if defined(DOUT_PIN)
GPOC = 1 << (DOUT_PIN);
#else
GPOC = 1 << (DEF_DOUT_PIN);
#endif
// 0.85us delay
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop;");
}
data <<= 1;
}
}
#endif
#ifdef DOTMATRIX
/* Used in the print functions */
void HCWS2812::_ReverseArray(char Array[], byte size)
{
byte index1, index2;
byte temp;
for(index1=0, index2=size-1; index1<size/2; index1++,index2--)
{
temp=Array[index1];
Array[index1]=Array[index2];
Array[index2]=temp;
}
}
#endif