Commit | Line | Data |
---|---|---|
5fc23db2 JM |
1 | /******************************************************************************/ |
2 | /* Files to Include */ | |
3 | /******************************************************************************/ | |
4 | ||
5 | #include <stdint.h> /* For uint8_t definition */ | |
6 | #include <stdbool.h> /* For true/false definition */ | |
7 | ||
8 | #include "user.h" /* User funct/params, such as InitApp */ | |
9 | ||
10 | #include "ws2811.h" | |
11 | #include "serial.h" | |
12 | ||
13 | ||
14 | /******************************************************************************/ | |
15 | /* User Global Variable Declaration */ | |
16 | /******************************************************************************/ | |
17 | ||
18 | /* i.e. uint8_t <variable_name>; */ | |
19 | ||
20 | ||
21 | void RGBWheel(uint16_t pos, uint8_t *r, uint8_t *g, uint8_t *b) | |
22 | { | |
23 | switch (pos / 128) | |
24 | { | |
25 | case 0: | |
26 | *r = (127 - pos % 128); | |
27 | *g = pos % 128; | |
28 | *b = 0; | |
29 | break; | |
30 | case 1: | |
31 | *g = (127 - pos % 128); | |
32 | *b = pos % 128; | |
33 | *r = 0; | |
34 | break; | |
35 | case 2: | |
36 | *b = 127 - pos % 128; | |
37 | *r = pos % 128; | |
38 | *g = 0; | |
39 | break; | |
40 | } | |
41 | } | |
42 | ||
43 | /* connect Fixed Voltage Reference to A2D convertor | |
44 | * input, set Vdd as reference, read value. | |
45 | * (val / 1024) * Vref == Vdd | |
46 | */ | |
47 | bool test_battery(void) | |
48 | { | |
49 | uint16_t volt; | |
50 | // read battery | |
51 | // Read Vdd by sampling FVR | |
52 | FVRCONbits.ADFVR = 0b10; // 2.048v // 1.024v | |
53 | FVRCONbits.FVREN = 1; // enable | |
54 | while (!FVRCONbits.FVRRDY) {}; // wait for it to stabilise | |
55 | ||
56 | ADCON1bits.ADCS = 0b110; // slowest sample clock | |
57 | ADCON1bits.ADFM = 1; // right justify | |
58 | ADCON0bits.CHS = 0b11111; // FVR input | |
59 | ADCON1bits.ADPREF = 0b00; // +ve ref Vdd | |
60 | ADCON0bits.ADON = 1; // enable ADC | |
61 | ||
62 | ADCON0bits.GO = 1; // start conversion | |
63 | while (ADCON0bits.GO) {}; // wait til finished | |
64 | ||
65 | volt = (ADRESH << 8) | ADRESL; | |
66 | ||
67 | // 3.0 V is >= 0x02BB | |
68 | if (volt >= 0x02BB) { | |
69 | unsigned char i; | |
70 | unsigned char *p; | |
71 | ||
72 | // first pixel faint red, rest black | |
73 | // battery below 3.0v | |
74 | p = rgbdata; | |
75 | for (i=0; i<RGB_COUNT; i++) *p++=0; | |
76 | p = rgbdata; | |
77 | p++; | |
78 | *p = 0x08; | |
79 | ||
80 | ws2811_transmit(); | |
81 | return true; | |
82 | } | |
83 | return false; | |
84 | } | |
85 | ||
86 | #ifdef SMALL_RAND | |
87 | static uint8_t random_byte = 0xB4; | |
88 | ||
89 | /* generate a random number */ | |
90 | uint8_t dorand(void) | |
91 | { | |
92 | asm("BCF STATUS,0"); | |
93 | asm("RRF _random_byte,W"); | |
94 | asm("BTFSC STATUS,0"); | |
95 | asm("XORLW 0xB4"); | |
96 | asm("MOVWF _random_byte"); | |
97 | ||
98 | return random_byte; | |
99 | } | |
100 | #else | |
101 | uint16_t dorand(void) | |
102 | { | |
103 | static uint16_t lfsr = 0xACE1u; | |
104 | lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xB400u); | |
105 | ||
106 | return lfsr; | |
107 | } | |
108 | ||
109 | #endif | |
110 | ||
111 | #define CHECK_MSEC 5 // sample key every 5 mS | |
112 | #define PRESS_MSEC 10 // stable before presses | |
113 | #define RELEASE_MSEC 100 // stable before released | |
114 | ||
115 | ||
116 | /* test to see if the button has been pressed */ | |
117 | bool test_button(void) | |
118 | { | |
119 | static bool down = false; | |
120 | static uint8_t count = PRESS_MSEC / CHECK_MSEC; | |
121 | ||
122 | // button was up | |
123 | if (!down) { | |
124 | if (PORTAbits.RA1 == 0) | |
125 | count--; | |
126 | else | |
127 | count = PRESS_MSEC / CHECK_MSEC; | |
128 | // has been down for the count, flip state | |
129 | if (count <= 0) { | |
130 | down = true; | |
131 | count = RELEASE_MSEC / CHECK_MSEC; | |
132 | } | |
133 | } else { | |
134 | if (PORTAbits.RA1 == 1) | |
135 | count--; | |
136 | else | |
137 | count = RELEASE_MSEC / CHECK_MSEC; | |
138 | // has been back up for the count | |
139 | // flip state, and say we saw one cycle | |
140 | if (count <= 0) { | |
141 | down = false; | |
142 | count = PRESS_MSEC / CHECK_MSEC; | |
143 | return true; | |
144 | } | |
145 | } | |
146 | return false; | |
147 | } | |
148 | ||
149 | ||
150 | void clear_all(void) | |
151 | { | |
152 | unsigned char i; | |
153 | /* blank everything */ | |
154 | for (i=0; i<RGB_COUNT; i++) rgbdata[i]=0; | |
155 | } | |
156 | ||
157 | /* how much rainbow to show 0-384 */ | |
158 | static const uint32_t quant = 384; //200; | |
159 | ||
160 | bool rainbow_draw(void) | |
161 | { | |
162 | unsigned char r,g,b; | |
163 | unsigned char *p; | |
164 | unsigned char i; | |
165 | ||
166 | /* draw a rainbow */ | |
167 | p = rgbdata; | |
168 | for (i=0; i<16; i++) { | |
169 | RGBWheel((i * quant / 16) % 384, &r, &g, &b); | |
170 | *(p++) = g/4; | |
171 | *(p++) = r/4; | |
172 | *(p++) = b/4; | |
173 | } | |
174 | for (i=16; i>0; i--) { | |
175 | RGBWheel(((i-1) * quant / 16) % 384, &r, &g, &b); | |
176 | *(p++) = g/4; | |
177 | *(p++) = r/4; | |
178 | *(p++) = b/4; | |
179 | } | |
180 | return true; | |
181 | } | |
182 | ||
183 | #define RAIN_UNIT 5 | |
184 | unsigned char rain_delay = RAIN_UNIT; | |
185 | ||
186 | bool rainbow_rotate(void) | |
187 | { | |
188 | static unsigned char delay = 0; | |
189 | ||
190 | unsigned char r,g,b; | |
191 | unsigned char *p; | |
192 | unsigned char i; | |
193 | unsigned char t; | |
194 | ||
195 | if ((delay--) > 0) return false; | |
196 | ||
197 | // rotate the colours | |
198 | p = rgbdata; | |
199 | ||
200 | g = p[0]; | |
201 | r = p[1]; | |
202 | b = p[2]; | |
203 | ||
204 | for (i=0; i<15*3; i++) { | |
205 | *p = *(p+3); | |
206 | p++; | |
207 | } | |
208 | *(p++) = g; | |
209 | *(p++) = r; | |
210 | *(p++) = b; | |
211 | ||
212 | // left eye, other direction | |
213 | ||
214 | p += 15 * 3; | |
215 | g = p[0]; | |
216 | r = p[1]; | |
217 | b = p[2]; | |
218 | p += 2; | |
219 | for (i=0; i<15*3; i++) { | |
220 | *p = *(p-3); | |
221 | p--; | |
222 | } | |
223 | *(p--) = b; | |
224 | *(p--) = r; | |
225 | *(p--) = g; | |
226 | ||
227 | delay = rain_delay; | |
228 | return true; | |
229 | } | |
230 | ||
231 | ||
232 | /****** | |
233 | colour bouncing blobs mode | |
234 | */ | |
235 | ||
236 | int8_t rspeed, gspeed, bspeed; | |
237 | #define MAX_SPEED 30; | |
238 | #define MAX_LEN 8 | |
239 | #define MAX_BRIGHT 32 | |
240 | ||
241 | bool bouncer_draw(void) | |
242 | { | |
243 | clear_all(); | |
244 | ||
245 | rspeed = dorand() % MAX_SPEED; | |
246 | gspeed = dorand() % MAX_SPEED; | |
247 | bspeed = dorand() % MAX_SPEED; | |
248 | ||
249 | unsigned char i; | |
250 | // draw rand len red | |
251 | for (i=(dorand() % MAX_LEN)+1; i>0; i--) { | |
252 | rgbdata[(3*i)+1] = MAX_BRIGHT; | |
253 | } | |
254 | // draw rand len green | |
255 | for (i=(dorand() % MAX_LEN)+1; i>0; i--) { | |
256 | rgbdata[(3*i)] = MAX_BRIGHT; | |
257 | } | |
258 | // draw rand len blue | |
259 | for (i=(dorand() % MAX_LEN)+1; i>0; i--) { | |
260 | rgbdata[(3*i)+2] = MAX_BRIGHT; | |
261 | } | |
262 | ||
263 | return true; | |
264 | } | |
265 | ||
266 | bool bouncer_rotate(uint8_t loop) | |
267 | { | |
268 | unsigned char * p; | |
269 | unsigned char i; | |
270 | ||
271 | if (loop % rspeed == 0) { | |
272 | if (rspeed > 0 ) { | |
273 | // moving up | |
274 | if (rgbdata[ (LED_COUNT-1)*3+1 ] == 0) { | |
275 | // still space to move up | |
276 | p = rgbdata + RGB_COUNT - 5; | |
277 | for (i=LED_COUNT-1; i > 0; i--) { | |
278 | p[3] = *p; | |
279 | p-=3; | |
280 | } | |
281 | rgbdata[1] = 0; | |
282 | } else { | |
283 | rspeed = -rspeed; | |
284 | } | |
285 | } else { | |
286 | // moving down | |
287 | if (rgbdata[1] == 0 || (rgbdata[1]!=0 && rgbdata[4]!=0)) { | |
288 | // still space to move down | |
289 | p = rgbdata + 1; | |
290 | for (i=LED_COUNT-1; i>0; i++) { | |
291 | *p = p[3]; | |
292 | p+=3; | |
293 | } | |
294 | rgbdata[RGB_COUNT-2] = 0; | |
295 | } else { | |
296 | rspeed = dorand() % MAX_SPEED; | |
297 | for (i=(dorand() % MAX_LEN)+1; i>0; i--) { | |
298 | rgbdata[(3*i)+1] = MAX_BRIGHT; | |
299 | } | |
300 | } | |
301 | } | |
302 | } | |
303 | if (loop % gspeed == 0) { | |
304 | if (gspeed > 0 ) { | |
305 | // moving up | |
306 | if (rgbdata[ (LED_COUNT-1)*3 ] == 0) { | |
307 | // still space to move up | |
308 | p = rgbdata + RGB_COUNT - 6; | |
309 | for (i=LED_COUNT-1; i > 0; i--) { | |
310 | p[3] = *p; | |
311 | p-=3; | |
312 | } | |
313 | rgbdata[0] = 0; | |
314 | } else { | |
315 | gspeed = -gspeed; | |
316 | } | |
317 | } else { | |
318 | // moving down | |
319 | if (rgbdata[0] == 0 || (rgbdata[0]!=0 && rgbdata[3]!=0)) { | |
320 | // still space to move down | |
321 | p = rgbdata; | |
322 | for (i=LED_COUNT-1; i>0; i++) { | |
323 | *p = p[3]; | |
324 | p+=3; | |
325 | } | |
326 | rgbdata[RGB_COUNT-3] = 0; | |
327 | } else { | |
328 | gspeed = dorand() % MAX_SPEED; | |
329 | // draw rand len green | |
330 | for (i=(dorand() % MAX_LEN)+1; i>0; i--) { | |
331 | rgbdata[(3*i)] = MAX_BRIGHT; | |
332 | } | |
333 | } | |
334 | } | |
335 | } | |
336 | if (loop % bspeed == 0) { | |
337 | if (bspeed > 0 ) { | |
338 | // moving up | |
339 | if (rgbdata[ (LED_COUNT-1)*3+2 ] == 0) { | |
340 | // still space to move up | |
341 | p = rgbdata + RGB_COUNT - 4; | |
342 | for (i=LED_COUNT-1; i > 0; i--) { | |
343 | p[3] = *p; | |
344 | p-=3; | |
345 | } | |
346 | rgbdata[2] = 0; | |
347 | } else { | |
348 | bspeed = -bspeed; | |
349 | } | |
350 | } else { | |
351 | // moving down | |
352 | if (rgbdata[2] == 0 || (rgbdata[2]!=0 && rgbdata[5]!=0)) { | |
353 | // still space to move down | |
354 | p = rgbdata + 2; | |
355 | for (i=LED_COUNT-1; i>0; i++) { | |
356 | *p = p[3]; | |
357 | p+=3; | |
358 | } | |
359 | rgbdata[RGB_COUNT-1] = 0; | |
360 | } else { | |
361 | bspeed = dorand() % MAX_SPEED; | |
362 | // draw rand len blue | |
363 | for (i=(dorand() % MAX_LEN)+1; i>0; i--) { | |
364 | rgbdata[(3*i)+2] = MAX_BRIGHT; | |
365 | } | |
366 | } | |
367 | } | |
368 | } | |
369 | ||
370 | return true; | |
371 | } | |
372 | ||
373 | /* fade in and out mode */ | |
374 | bool glow_draw(uint8_t r, uint8_t g, uint8_t b) | |
375 | { | |
376 | rspeed = r / 8; | |
377 | gspeed = g / 8; | |
378 | bspeed = b / 8; | |
379 | ||
380 | return false; | |
381 | } | |
382 | ||
383 | bool glow_rotate(uint8_t loop) | |
384 | { | |
385 | unsigned char *p; | |
386 | unsigned char i; | |
387 | ||
388 | uint8_t val = loop & 0x7F; | |
389 | ||
390 | if (loop >= 128) | |
391 | val = 128 - val; | |
392 | ||
393 | val /= 2; /* range is now 0-63 */ | |
394 | ||
395 | uint8_t r,g,b; | |
396 | g = (uint16_t)gspeed * val / 64; | |
397 | r = (uint16_t)rspeed * val / 64; | |
398 | b = (uint16_t)bspeed * val / 64; | |
399 | ||
400 | /* draw a rainbow */ | |
401 | p = rgbdata; | |
402 | for (i=0; i<LED_COUNT; i++) { | |
403 | *(p++) = g; | |
404 | *(p++) = r; | |
405 | *(p++) = b; | |
406 | } | |
407 | ||
408 | return true; | |
409 | } | |
410 | ||
411 | bool dot_draw(uint8_t r, uint8_t g, uint8_t b) | |
412 | { | |
413 | uint8_t i; | |
414 | uint8_t *p = rgbdata; | |
415 | for (i=0; i<RGB_COUNT;i++) *(p++)=0; | |
416 | ||
417 | rgbdata[0] = g / 8; | |
418 | rgbdata[1] = r / 8; | |
419 | rgbdata[2] = b / 8; | |
420 | rgbdata[48+0] = g / 8; | |
421 | rgbdata[48+1] = r / 8; | |
422 | rgbdata[48+2] = b / 8; | |
423 | ||
424 | return false; | |
425 | } | |
426 | ||
427 | /******************************************************************************/ | |
428 | /* Main Program */ | |
429 | /******************************************************************************/ | |
430 | ||
431 | #define MAX_MODE 8 | |
432 | ||
433 | ||
434 | ||
435 | ||
436 | void main(void) | |
437 | { | |
438 | unsigned char mode = 2; | |
439 | bool changed = false; | |
440 | uint8_t loop = 0; | |
441 | ||
442 | /* Initialize I/O and Peripherals for application */ | |
443 | InitApp(); | |
444 | ws2811_init(); | |
445 | ||
446 | /* initial pattern */ | |
447 | rainbow_draw(); | |
448 | ws2811_transmit(); | |
449 | ||
450 | glow_draw(0,255,0); | |
451 | ||
452 | /* main loop */ | |
453 | while(1) | |
454 | { | |
455 | CLRWDT(); // tell watchdog we are still awake | |
456 | ||
457 | /* test if the battery is dead, stop if it is */ | |
458 | if (test_battery()) { | |
459 | SLEEP(); | |
460 | } | |
461 | ||
462 | /* if button is pressed, change mode */ | |
463 | if (test_button()) { | |
464 | mode = (mode + 1) % MAX_MODE; | |
465 | loop = 0; | |
466 | ||
467 | // initialise the new mode | |
468 | switch (mode) { | |
469 | case 0: | |
470 | rain_delay = RAIN_UNIT; | |
471 | changed = rainbow_draw(); | |
472 | break; | |
473 | case 1: | |
474 | changed = bouncer_draw(); | |
475 | break; | |
476 | case 2: | |
477 | changed = glow_draw(0,255,0); | |
478 | break; | |
479 | case 3: | |
480 | changed = glow_draw(255,0,0); | |
481 | break; | |
482 | case 4: | |
483 | changed = glow_draw(0,0,255); | |
484 | break; | |
485 | case 5: | |
486 | changed = glow_draw(255,0,255); | |
487 | break; | |
488 | case 6: | |
489 | changed = dot_draw(255,255,255); | |
490 | break; | |
491 | case 7: | |
492 | changed = dot_draw(0,8,0); | |
493 | break; | |
494 | } | |
495 | } else { | |
496 | /* otherwise run mode maintenance */ | |
497 | switch (mode) { | |
498 | case 0: | |
499 | changed = rainbow_rotate(); | |
500 | break; | |
501 | case 1: | |
502 | changed = bouncer_rotate(loop); | |
503 | break; | |
504 | case 2: | |
505 | case 3: | |
506 | case 4: | |
507 | changed = glow_rotate(loop); | |
508 | break; | |
509 | case 5: | |
510 | if (loop == 0) { | |
511 | uint16_t n = dorand() % quant; | |
512 | uint8_t r,g,b; | |
513 | RGBWheel(n, &r, &g, &b); | |
514 | changed = glow_draw(r, g, b); | |
515 | } | |
516 | changed = glow_rotate(loop); | |
517 | break; | |
518 | case 6: | |
519 | changed = rainbow_rotate(); | |
520 | break; | |
521 | case 7: | |
522 | if (loop==0) changed = rainbow_rotate(); | |
523 | break; | |
524 | } | |
525 | } | |
526 | ||
527 | /* transmit the latest data set */ | |
528 | if (changed) ws2811_transmit(); | |
529 | ||
530 | /* wait a bit */ | |
531 | __delay_ms(5); | |
532 | ||
533 | loop++; | |
534 | } | |
535 | } | |
536 |