]> git.zerfleddert.de Git - fnordlicht-mini/blame_incremental - firmware/fnordlicht-firmware/remote.c
reference firmware
[fnordlicht-mini] / firmware / fnordlicht-firmware / remote.c
... / ...
CommitLineData
1/* vim:ts=4 sts=4 et tw=80
2 *
3 * fnordlicht firmware
4 *
5 * for additional information please
6 * see http://lochraster.org/fnordlichtmini
7 *
8 * (c) by Alexander Neumann <alexander@bumpern.de>
9 *
10 * This program is free software: you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License version 3 as published by
12 * the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#include <stdlib.h>
24#include <string.h>
25#include <avr/wdt.h>
26#include <avr/pgmspace.h>
27#include <avr/sleep.h>
28#include <avr/interrupt.h>
29#include <util/delay.h>
30#include "globals.h"
31#include "remote.h"
32#include "fifo.h"
33#include "uart.h"
34#include "pwm.h"
35#include "../common/pt/pt.h"
36#include "script.h"
37#include "remote-proto.h"
38#include "../common/common.h"
39#include "storage.h"
40#include "timer.h"
41
42/* abbreviations for port, ddr and pin */
43#define R_PORT _OUTPORT(REMOTE_INT_PORT)
44#define R_DDR _DDRPORT(REMOTE_INT_PORT)
45#define R_PIN _INPORT(REMOTE_INT_PORT)
46#define INTPIN REMOTE_INT_PIN
47
48#define M_PORT _OUTPORT(REMOTE_MASTER_PORT)
49#define M_DDR _DDRPORT(REMOTE_MASTER_PORT)
50#define M_PIN _INPORT(REMOTE_MASTER_PORT)
51#define M_PIN1 REMOTE_MASTER_PIN1
52#define M_PIN2 REMOTE_MASTER_PIN2
53
54struct remote_state_t
55{
56 /* serial communication */
57 union {
58 struct remote_msg_t msg;
59 uint8_t buf[REMOTE_MSG_LEN];
60 };
61 uint8_t buflen;
62
63 uint8_t sync;
64 uint8_t synced;
65 struct pt thread;
66#if CONFIG_MASTER_MODE
67 struct pt master_thread;
68#endif
69
70 /* int line */
71 timer_t int_timer;
72 enum {
73 INT_IDLE = 0,
74 INT_PULLED = 1,
75 INT_PULLED_TIMER = 2,
76 } int_state;
77};
78
79struct remote_state_t remote;
80struct global_remote_t global_remote;
81
82#if CONFIG_SERIAL && CONFIG_REMOTE
83
84/* static parsing routines */
85static void parse_fade_rgb(struct remote_msg_fade_rgb_t *msg);
86static void parse_fade_hsv(struct remote_msg_fade_hsv_t *msg);
87static void parse_save_rgb(struct remote_msg_save_rgb_t *msg);
88static void parse_save_hsv(struct remote_msg_save_hsv_t *msg);
89static void parse_save_current(struct remote_msg_save_current_t *msg);
90static void parse_config_offsets(struct remote_msg_config_offsets_t *msg);
91static void parse_start_program(struct remote_msg_start_program_t *msg);
92static void parse_stop(struct remote_msg_stop_t *msg);
93static void parse_modify_current(struct remote_msg_modify_current_t *msg);
94static void parse_pull_int(struct remote_msg_pull_int_t *msg);
95static void parse_config_startup(struct remote_msg_config_startup_t *msg);
96static void parse_bootloader(struct remote_msg_bootloader_t *msg);
97static void parse_powerdown(void);
98
99void remote_init(void)
100{
101 /* master mode check: check if PIN2 pulls down PIN1 */
102 /* configure M_PIN1 as input with pullup */
103 M_DDR &= ~_BV(M_PIN1);
104 M_PORT |= _BV(M_PIN1);
105 /* configure M_PIN2 as output, set low */
106 M_DDR |= _BV(M_PIN2);
107 M_PORT &= ~_BV(M_PIN2);
108
109 /* initialize offsets */
110 global_remote.offsets.saturation = 255;
111 global_remote.offsets.value = 255;
112
113 /* initialize int pin, tri-state */
114 R_DDR &= ~_BV(INTPIN);
115 R_PORT &= ~_BV(INTPIN);
116#ifdef INIT_ZERO
117 remote.int_state = INT_IDLE;
118
119 PT_INIT(&remote.master_thread);
120#endif
121
122#if CONFIG_MASTER_MODE == 1
123 /* check master state: read value of M_PIN1 */
124 if (!(M_PIN & _BV(M_PIN1)))
125 global_remote.master = true;
126#elif CONFIG_MASTER_MODE == 2
127 /* statically configure master mode */
128 global_remote.master = true;
129#endif
130}
131
132static void remote_parse_msg(struct remote_msg_t *msg)
133{
134 /* verify address */
135 if (msg->address != global_remote.address && msg->address != REMOTE_ADDR_BROADCAST)
136 return;
137
138 /* parse command */
139 switch (msg->cmd) {
140 case REMOTE_CMD_FADE_RGB:
141 parse_fade_rgb((struct remote_msg_fade_rgb_t *)msg);
142 break;
143 case REMOTE_CMD_FADE_HSV:
144 parse_fade_hsv((struct remote_msg_fade_hsv_t *)msg);
145 break;
146 case REMOTE_CMD_SAVE_RGB:
147 parse_save_rgb((struct remote_msg_save_rgb_t *)msg);
148 break;
149 case REMOTE_CMD_SAVE_HSV:
150 parse_save_hsv((struct remote_msg_save_hsv_t *)msg);
151 break;
152 case REMOTE_CMD_SAVE_CURRENT:
153 parse_save_current((struct remote_msg_save_current_t *)msg);
154 break;
155 case REMOTE_CMD_CONFIG_OFFSETS:
156 parse_config_offsets((struct remote_msg_config_offsets_t *)msg);
157 break;
158 case REMOTE_CMD_START_PROGRAM:
159 parse_start_program((struct remote_msg_start_program_t *)msg);
160 break;
161 case REMOTE_CMD_STOP:
162 parse_stop((struct remote_msg_stop_t *)msg);
163 break;
164 case REMOTE_CMD_MODIFY_CURRENT:
165 parse_modify_current((struct remote_msg_modify_current_t *)msg);
166 break;
167 case REMOTE_CMD_PULL_INT:
168 parse_pull_int((struct remote_msg_pull_int_t *)msg);
169 break;
170 case REMOTE_CMD_CONFIG_STARTUP:
171 parse_config_startup((struct remote_msg_config_startup_t *)msg);
172 break;
173 case REMOTE_CMD_BOOTLOADER:
174 parse_bootloader((struct remote_msg_bootloader_t *)msg);
175 break;
176 case REMOTE_CMD_POWERDOWN:
177 parse_powerdown();
178 break;
179
180 }
181}
182
183static PT_THREAD(remote_thread(struct pt *thread))
184{
185 PT_BEGIN(thread);
186
187 while (1) {
188 PT_WAIT_UNTIL(thread, remote.buflen == REMOTE_MSG_LEN);
189
190 remote_parse_msg(&remote.msg);
191 remote.buflen = 0;
192 }
193
194 PT_END(thread);
195}
196
197static __noinline void send_msg(struct remote_msg_t *msg)
198{
199 uint8_t *ptr = (uint8_t *)msg;
200 for (uint8_t i = 0; i < REMOTE_MSG_LEN; i++)
201 uart_putc(*ptr++);
202}
203
204
205static void send_resync(uint8_t addr)
206{
207 for (uint8_t i = 0; i < REMOTE_SYNC_LEN; i++)
208 uart_putc(REMOTE_CMD_RESYNC);
209 uart_putc(addr);
210}
211
212#if CONFIG_MASTER_MODE
213/* parameters for master mode script commands */
214#define MASTER_PROGRAMS 2
215static PROGMEM uint8_t master_parameters[] = {
216 /* first: colorwheel forward */
217 0, /* program index */
218 1, /* fade step */
219 2, /* fade delay */
220 0, /* fade sleep */
221 0, 0, /* hue start (little endian) */
222 20, 0, /* hue step (little endian) */
223 -1, /* addr add */
224 255, /* saturation */
225 255, /* value */
226
227 /* first: colorwheel backward */
228 0, /* program index */
229 1, /* fade step */
230 2, /* fade delay */
231 0, /* fade sleep */
232 0, 0, /* hue start (little endian) */
233 20, 0, /* hue step (little endian) */
234 1, /* addr add */
235 255, /* saturation */
236 255, /* value */
237};
238
239static PT_THREAD(remote_master_thread(struct pt *thread))
240{
241 static struct remote_msg_start_program_t msg;
242 static timer_t timer;
243 static uint16_t sleep;
244 static uint8_t *ptr;
245 static uint8_t idx;
246
247 PT_BEGIN(thread);
248
249 /* wait */
250 timer_set(&timer, MASTER_WAIT_BEFORE_SYNC);
251 while(!timer_expired(&timer))
252 PT_YIELD(thread);
253
254 /* start program on all devices */
255 msg.address = 0xff;
256 msg.cmd = REMOTE_CMD_START_PROGRAM;
257
258 while (1) {
259 ptr = &master_parameters[0];
260
261 for (idx = 0; idx < MASTER_PROGRAMS; idx++) {
262 /* stop current program and fading */
263 script_stop();
264 pwm_stop_fading();
265
266 /* start program colorwheel on all nodes */
267 msg.script = pgm_read_byte(ptr++);
268 /* load parameters */
269 for (uint8_t i = 0; i < sizeof(msg.params); i++)
270 msg.params.raw[i] = pgm_read_byte(ptr++);
271
272 /* send */
273 send_resync(MASTER_MODE_FIRST_ADDRESS);
274 PT_YIELD(thread);
275 send_msg((struct remote_msg_t *)&msg);
276
277 /* start program locally */
278 script_start(0, msg.script, &msg.params);
279
280 /* sleep */
281 sleep = MASTER_MODE_SLEEP;
282 while (sleep--) {
283 /* sleep 1s */
284 timer_set(&timer, 100);
285
286 while (!timer_expired(&timer))
287 PT_YIELD(thread);
288 }
289 }
290 }
291
292 PT_END(thread);
293}
294#endif
295
296void remote_poll(void)
297{
298 if (fifo_fill((fifo_t *)&global_uart.rx) > 0) {
299 uint8_t data = fifo_dequeue((fifo_t *)&global_uart.rx);
300
301 /* check if sync sequence has been received before */
302 if (remote.sync == REMOTE_SYNC_LEN) {
303 /* synced, safe address and send next address to following device */
304 global_remote.address = data;
305 uart_putc(data+1);
306
307 /* reset remote buffer */
308 remote.buflen = 0;
309
310 /* enable remote command thread */
311 remote.synced = 1;
312 PT_INIT(&remote.thread);
313#if CONFIG_MASTER_MODE
314 global_remote.master = false;
315#endif
316 } else {
317 /* just pass through data */
318 uart_putc(data);
319
320 /* put data into remote buffer
321 * (just processed if remote.synced == 1) */
322 if (remote.buflen < sizeof(remote.buf))
323 remote.buf[remote.buflen++] = data;
324 }
325
326 /* remember the number of sync bytes received so far */
327 if (data == REMOTE_CMD_RESYNC)
328 remote.sync++;
329 else
330 remote.sync = 0;
331 }
332
333 if (remote.synced)
334 PT_SCHEDULE(remote_thread(&remote.thread));
335
336 /* check int timer */
337 if (remote.int_state == INT_PULLED_TIMER && timer_expired(&remote.int_timer)) {
338 remote_release_int();
339 }
340
341#if CONFIG_MASTER_MODE
342 if (global_remote.master)
343 PT_SCHEDULE(remote_master_thread(&remote.master_thread));
344#endif
345}
346
347void remote_pull_int(void)
348{
349 if (remote.int_state != INT_IDLE)
350 return;
351
352 /* pull int pin to ground */
353 R_DDR |= _BV(INTPIN);
354 R_PORT &= ~_BV(INTPIN);
355}
356
357void remote_release_int(void)
358{
359 if (remote.int_state == INT_IDLE)
360 return;
361
362 /* reset pin to tri-state */
363 R_DDR &= ~_BV(INTPIN);
364 R_PORT &= ~_BV(INTPIN);
365 remote.int_state = INT_IDLE;
366}
367
368/* offset helper functions */
369uint8_t remote_apply_offset(uint8_t value, int8_t offset)
370{
371 if (offset < 0) {
372 if (value > -offset)
373 value += offset;
374 else
375 value = 1;
376 } else if (offset > 0) {
377 uint16_t temp = value;
378 temp += offset;
379 if (temp > 255)
380 value = 255;
381 else
382 value = LO8(temp);
383 }
384
385 return value;
386}
387
388static uint8_t apply_scale(uint8_t value, uint8_t scale)
389{
390 uint16_t temp = value * scale;
391 temp /= 255;
392 return LO8(temp);
393}
394
395void remote_apply_hsv_offset(struct hsv_color_t *color)
396{
397 color->hue += global_remote.offsets.hue;
398 color->saturation = apply_scale(color->saturation, global_remote.offsets.saturation);
399 color->value = apply_scale(color->value, global_remote.offsets.value);
400}
401
402/* parser functions */
403
404void parse_fade_rgb(struct remote_msg_fade_rgb_t *msg)
405{
406 pwm_fade_rgb(&msg->color, msg->step, msg->delay);
407}
408
409void parse_fade_hsv(struct remote_msg_fade_hsv_t *msg)
410{
411 pwm_fade_hsv(&msg->color, msg->step, msg->delay);
412}
413
414void parse_save_rgb(struct remote_msg_save_rgb_t *msg)
415{
416 if (msg->slot >= CONFIG_EEPROM_COLORS)
417 return;
418
419 struct storage_color_t c;
420 c.step = msg->step;
421 c.delay = msg->delay;
422 c.pause = msg->pause;
423
424 /* mark color as rgb */
425 c.color.rgb_marker = 255;
426
427 c.color.red = msg->color.red;
428 c.color.green = msg->color.green;
429 c.color.blue = msg->color.blue;
430
431 storage_save_color(msg->slot, &c);
432}
433
434void parse_save_hsv(struct remote_msg_save_hsv_t *msg)
435{
436 if (msg->slot >= CONFIG_EEPROM_COLORS)
437 return;
438
439 struct storage_color_t c;
440 c.step = msg->step;
441 c.delay = msg->delay;
442 c.pause = msg->pause;
443
444 c.color.hue = msg->color.hue;
445 c.color.saturation = msg->color.saturation;
446 c.color.value = msg->color.value;
447
448 storage_save_color(msg->slot, &c);
449}
450
451void parse_save_current(struct remote_msg_save_current_t *msg)
452{
453 if (msg->slot >= CONFIG_EEPROM_COLORS)
454 return;
455
456 struct storage_color_t c;
457 c.step = msg->step;
458 c.delay = msg->delay;
459 c.pause = msg->pause;
460
461 /* mark color as rgb */
462 c.color.rgb_marker = 255;
463
464 c.color.red = global_pwm.current.red;
465 c.color.green = global_pwm.current.green;
466 c.color.blue = global_pwm.current.blue;
467
468 storage_save_color(msg->slot, &c);
469}
470
471void parse_config_offsets(struct remote_msg_config_offsets_t *msg)
472{
473 global_remote.offsets.step = msg->step;
474 global_remote.offsets.delay = msg->delay;
475 global_remote.offsets.hue = msg->hue;
476 global_remote.offsets.saturation = msg->saturation;
477 global_remote.offsets.value = msg->value;
478}
479
480void parse_start_program(struct remote_msg_start_program_t *msg)
481{
482 script_stop();
483 pwm_stop_fading();
484 script_start(0, msg->script, &msg->params);
485}
486
487void parse_stop(struct remote_msg_stop_t *msg)
488{
489 script_stop();
490 if (msg->fade)
491 pwm_stop_fading();
492}
493
494void parse_modify_current(struct remote_msg_modify_current_t *msg)
495{
496 /* return if a color change is in progress */
497#if CONFIG_SCRIPT
498 if (!global_script.disable || !pwm_target_reached())
499 return;
500#else
501 if (!pwm_target_reached())
502 return;
503#endif
504
505 /* apply rgb and hsv offsets */
506 pwm_modify_rgb(&msg->rgb, msg->step, msg->delay);
507 pwm_modify_hsv(&msg->hsv, msg->step, msg->delay);
508}
509
510void parse_pull_int(struct remote_msg_pull_int_t *msg)
511{
512 /* pull pin to ground */
513 remote_pull_int();
514
515 uint8_t delay = msg->delay;
516
517 /* start timer, delay between 50ms and 2550ms */
518 if (delay == 0)
519 delay = 5;
520 else if (delay <= 51)
521 delay *= 5;
522 else
523 delay = 255;
524
525 timer_set(&remote.int_timer, delay);
526
527 /* remember state */
528 remote.int_state = INT_PULLED_TIMER;
529}
530
531void parse_config_startup(struct remote_msg_config_startup_t *msg)
532{
533 /* set mode and copy data */
534 memcpy(&startup_config.params, &msg->params, sizeof(struct startup_parameters_t));
535 /* save config to eeprom */
536 storage_save_config();
537}
538
539static void wait_for_uart(void)
540{
541 while (fifo_fill((fifo_t *)&global_uart.tx) != 0 || !uart_send_complete());
542}
543
544void parse_bootloader(struct remote_msg_bootloader_t *msg)
545{
546 /* check magic bytes */
547 if (msg->magic[0] != BOOTLOADER_MAGIC_BYTE1 ||
548 msg->magic[1] != BOOTLOADER_MAGIC_BYTE2 ||
549 msg->magic[2] != BOOTLOADER_MAGIC_BYTE3 ||
550 msg->magic[3] != BOOTLOADER_MAGIC_BYTE4)
551 return;
552
553 /* wait until the tx fifo is empty, then start watchdog, but never kick it
554 * (bootloader and firmware both disable the watchdog) */
555 wait_for_uart();
556 wdt_enable(WDTO_120MS);
557}
558
559void parse_powerdown(void)
560{
561 /* configure sleep mode */
562 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
563
564 /* wait until the tx fifo is empty before sleep */
565 wait_for_uart();
566
567 /* save all output registers */
568 uint8_t portb = PORTB;
569 uint8_t portc = PORTC;
570 uint8_t portd = PORTD;
571 uint8_t ddrb = DDRB;
572 uint8_t ddrc = DDRC;
573 uint8_t ddrd = DDRD;
574 PORTB = 0;
575 PORTC = 0;
576 PORTD = 0;
577 DDRB = 0;
578 DDRC = 0;
579 DDRD = 0;
580
581 /* configure int pin as input (already done by setting the
582 * DDR register to zero) with pullup */
583 R_PORT = _BV(INTPIN);
584
585 /* enable int0 low level interrupt */
586 _IFR_INT0 = _BV(INTF0);
587 _ICR_INT0 |= _BV(INT0);
588 /* enter sleep mode */
589 sleep_mode();
590
591 /* wakeup, disable int0 */
592 _ICR_INT0 &= ~_BV(INT0);
593
594 /* restore output registers */
595 PORTB = portb;
596 PORTC = portc;
597 PORTD = portd;
598 DDRB = ddrb;
599 DDRC = ddrc;
600 DDRD = ddrd;
601}
602
603/* do nothing, just for wakeup after sleep */
604EMPTY_INTERRUPT(INT0_vect);
605
606#endif
Impressum, Datenschutz