]> git.zerfleddert.de Git - fnordlicht-mini/blob - firmware/fnordlicht-firmware/remote.c
dcef098c7336e3960435e21bd41c3e36b0157948
[fnordlicht-mini] / firmware / fnordlicht-firmware / remote.c
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
54 struct 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
67 /* int line */
68 timer_t int_timer;
69 enum {
70 INT_IDLE = 0,
71 INT_PULLED = 1,
72 INT_PULLED_TIMER = 2,
73 } int_state;
74 };
75
76 struct remote_state_t remote;
77 struct global_remote_t global_remote;
78
79 #if CONFIG_SERIAL && CONFIG_REMOTE
80
81 /* static parsing routines */
82 static void parse_fade_rgb(struct remote_msg_fade_rgb_t *msg);
83 static void parse_fade_hsv(struct remote_msg_fade_hsv_t *msg);
84 static void parse_save_rgb(struct remote_msg_save_rgb_t *msg);
85 static void parse_save_hsv(struct remote_msg_save_hsv_t *msg);
86 static void parse_save_current(struct remote_msg_save_current_t *msg);
87 static void parse_config_offsets(struct remote_msg_config_offsets_t *msg);
88 static void parse_start_program(struct remote_msg_start_program_t *msg);
89 static void parse_stop(struct remote_msg_stop_t *msg);
90 static void parse_modify_current(struct remote_msg_modify_current_t *msg);
91 static void parse_pull_int(struct remote_msg_pull_int_t *msg);
92 static void parse_config_startup(struct remote_msg_config_startup_t *msg);
93 static void parse_bootloader(struct remote_msg_bootloader_t *msg);
94 static void parse_powerdown(void);
95
96 void remote_init(void)
97 {
98 /* master mode check: check if PIN2 pulls down PIN1 */
99 /* configure M_PIN1 as input with pullup */
100 M_DDR &= ~_BV(M_PIN1);
101 M_PORT |= _BV(M_PIN1);
102 /* configure M_PIN2 as output, set low */
103 M_DDR |= _BV(M_PIN2);
104 M_PORT &= ~_BV(M_PIN2);
105
106 /* initialize offsets */
107 global_remote.offsets.saturation = 255;
108 global_remote.offsets.value = 255;
109
110 /* initialize int pin, tri-state */
111 R_DDR &= ~_BV(INTPIN);
112 R_PORT &= ~_BV(INTPIN);
113 #ifdef INIT_ZERO
114 remote.int_state = INT_IDLE;
115 #endif
116 }
117
118 static void remote_parse_msg(struct remote_msg_t *msg)
119 {
120 /* verify address */
121 if (msg->address != global_remote.address && msg->address != REMOTE_ADDR_BROADCAST)
122 return;
123
124 /* parse command */
125 switch (msg->cmd) {
126 case REMOTE_CMD_FADE_RGB:
127 parse_fade_rgb((struct remote_msg_fade_rgb_t *)msg);
128 break;
129 case REMOTE_CMD_FADE_HSV:
130 parse_fade_hsv((struct remote_msg_fade_hsv_t *)msg);
131 break;
132 case REMOTE_CMD_SAVE_RGB:
133 parse_save_rgb((struct remote_msg_save_rgb_t *)msg);
134 break;
135 case REMOTE_CMD_SAVE_HSV:
136 parse_save_hsv((struct remote_msg_save_hsv_t *)msg);
137 break;
138 case REMOTE_CMD_SAVE_CURRENT:
139 parse_save_current((struct remote_msg_save_current_t *)msg);
140 break;
141 case REMOTE_CMD_CONFIG_OFFSETS:
142 parse_config_offsets((struct remote_msg_config_offsets_t *)msg);
143 break;
144 case REMOTE_CMD_START_PROGRAM:
145 parse_start_program((struct remote_msg_start_program_t *)msg);
146 break;
147 case REMOTE_CMD_STOP:
148 parse_stop((struct remote_msg_stop_t *)msg);
149 break;
150 case REMOTE_CMD_MODIFY_CURRENT:
151 parse_modify_current((struct remote_msg_modify_current_t *)msg);
152 break;
153 case REMOTE_CMD_PULL_INT:
154 parse_pull_int((struct remote_msg_pull_int_t *)msg);
155 break;
156 case REMOTE_CMD_CONFIG_STARTUP:
157 parse_config_startup((struct remote_msg_config_startup_t *)msg);
158 break;
159 case REMOTE_CMD_BOOTLOADER:
160 parse_bootloader((struct remote_msg_bootloader_t *)msg);
161 break;
162 case REMOTE_CMD_POWERDOWN:
163 parse_powerdown();
164 break;
165
166 }
167 }
168
169 static PT_THREAD(remote_thread(struct pt *thread))
170 {
171 PT_BEGIN(thread);
172
173 while (1) {
174 PT_WAIT_UNTIL(thread, remote.buflen == REMOTE_MSG_LEN);
175
176 remote_parse_msg(&remote.msg);
177 remote.buflen = 0;
178 }
179
180 PT_END(thread);
181 }
182
183 static __noinline void send_msg(struct remote_msg_t *msg)
184 {
185 uint8_t *ptr = (uint8_t *)msg;
186 for (uint8_t i = 0; i < REMOTE_MSG_LEN; i++)
187 uart_putc(*ptr++);
188 }
189
190
191 static void send_resync(uint8_t addr)
192 {
193 for (uint8_t i = 0; i < REMOTE_SYNC_LEN; i++)
194 uart_putc(REMOTE_CMD_RESYNC);
195 uart_putc(addr);
196 }
197
198 void remote_poll(void)
199 {
200 if (fifo_fill((fifo_t *)&global_uart.rx) > 0) {
201 uint8_t data = fifo_dequeue((fifo_t *)&global_uart.rx);
202
203 /* check if sync sequence has been received before */
204 if (remote.sync == REMOTE_SYNC_LEN) {
205 /* synced, safe address and send next address to following device */
206 global_remote.address = data;
207 uart_putc(data+1);
208
209 /* reset remote buffer */
210 remote.buflen = 0;
211
212 /* enable remote command thread */
213 remote.synced = 1;
214 PT_INIT(&remote.thread);
215 } else {
216 /* just pass through data */
217 uart_putc(data);
218
219 /* put data into remote buffer
220 * (just processed if remote.synced == 1) */
221 if (remote.buflen < sizeof(remote.buf))
222 remote.buf[remote.buflen++] = data;
223 }
224
225 /* remember the number of sync bytes received so far */
226 if (data == REMOTE_CMD_RESYNC)
227 remote.sync++;
228 else
229 remote.sync = 0;
230 }
231
232 if (remote.synced)
233 PT_SCHEDULE(remote_thread(&remote.thread));
234
235 /* check int timer */
236 if (remote.int_state == INT_PULLED_TIMER && timer_expired(&remote.int_timer)) {
237 remote_release_int();
238 }
239 }
240
241 void remote_pull_int(void)
242 {
243 if (remote.int_state != INT_IDLE)
244 return;
245
246 /* pull int pin to ground */
247 R_DDR |= _BV(INTPIN);
248 R_PORT &= ~_BV(INTPIN);
249 }
250
251 void remote_release_int(void)
252 {
253 if (remote.int_state == INT_IDLE)
254 return;
255
256 /* reset pin to tri-state */
257 R_DDR &= ~_BV(INTPIN);
258 R_PORT &= ~_BV(INTPIN);
259 remote.int_state = INT_IDLE;
260 }
261
262 /* offset helper functions */
263 uint8_t remote_apply_offset(uint8_t value, int8_t offset)
264 {
265 if (offset < 0) {
266 if (value > -offset)
267 value += offset;
268 else
269 value = 1;
270 } else if (offset > 0) {
271 uint16_t temp = value;
272 temp += offset;
273 if (temp > 255)
274 value = 255;
275 else
276 value = LO8(temp);
277 }
278
279 return value;
280 }
281
282 static uint8_t apply_scale(uint8_t value, uint8_t scale)
283 {
284 uint16_t temp = value * scale;
285 temp /= 255;
286 return LO8(temp);
287 }
288
289 void remote_apply_hsv_offset(struct hsv_color_t *color)
290 {
291 color->hue += global_remote.offsets.hue;
292 color->saturation = apply_scale(color->saturation, global_remote.offsets.saturation);
293 color->value = apply_scale(color->value, global_remote.offsets.value);
294 }
295
296 /* parser functions */
297
298 void parse_fade_rgb(struct remote_msg_fade_rgb_t *msg)
299 {
300 pwm_fade_rgb(&msg->color, msg->step, msg->delay);
301 }
302
303 void parse_fade_hsv(struct remote_msg_fade_hsv_t *msg)
304 {
305 pwm_fade_hsv(&msg->color, msg->step, msg->delay);
306 }
307
308 void parse_save_rgb(struct remote_msg_save_rgb_t *msg)
309 {
310 if (msg->slot >= CONFIG_EEPROM_COLORS)
311 return;
312
313 struct storage_color_t c;
314 c.step = msg->step;
315 c.delay = msg->delay;
316 c.pause = msg->pause;
317
318 /* mark color as rgb */
319 c.color.rgb_marker = 255;
320
321 c.color.red = msg->color.red;
322 c.color.green = msg->color.green;
323 c.color.blue = msg->color.blue;
324
325 storage_save_color(msg->slot, &c);
326 }
327
328 void parse_save_hsv(struct remote_msg_save_hsv_t *msg)
329 {
330 if (msg->slot >= CONFIG_EEPROM_COLORS)
331 return;
332
333 struct storage_color_t c;
334 c.step = msg->step;
335 c.delay = msg->delay;
336 c.pause = msg->pause;
337
338 c.color.hue = msg->color.hue;
339 c.color.saturation = msg->color.saturation;
340 c.color.value = msg->color.value;
341
342 storage_save_color(msg->slot, &c);
343 }
344
345 void parse_save_current(struct remote_msg_save_current_t *msg)
346 {
347 if (msg->slot >= CONFIG_EEPROM_COLORS)
348 return;
349
350 struct storage_color_t c;
351 c.step = msg->step;
352 c.delay = msg->delay;
353 c.pause = msg->pause;
354
355 /* mark color as rgb */
356 c.color.rgb_marker = 255;
357
358 c.color.red = global_pwm.current.red;
359 c.color.green = global_pwm.current.green;
360 c.color.blue = global_pwm.current.blue;
361
362 storage_save_color(msg->slot, &c);
363 }
364
365 void parse_config_offsets(struct remote_msg_config_offsets_t *msg)
366 {
367 global_remote.offsets.step = msg->step;
368 global_remote.offsets.delay = msg->delay;
369 global_remote.offsets.hue = msg->hue;
370 global_remote.offsets.saturation = msg->saturation;
371 global_remote.offsets.value = msg->value;
372 }
373
374 void parse_start_program(struct remote_msg_start_program_t *msg)
375 {
376 script_stop();
377 pwm_stop_fading();
378 script_start(0, msg->script, &msg->params);
379 }
380
381 void parse_stop(struct remote_msg_stop_t *msg)
382 {
383 script_stop();
384 if (msg->fade)
385 pwm_stop_fading();
386 }
387
388 void parse_modify_current(struct remote_msg_modify_current_t *msg)
389 {
390 /* return if a color change is in progress */
391 #if CONFIG_SCRIPT
392 if (!global_script.disable || !pwm_target_reached())
393 return;
394 #else
395 if (!pwm_target_reached())
396 return;
397 #endif
398
399 /* apply rgb and hsv offsets */
400 pwm_modify_rgb(&msg->rgb, msg->step, msg->delay);
401 pwm_modify_hsv(&msg->hsv, msg->step, msg->delay);
402 }
403
404 void parse_pull_int(struct remote_msg_pull_int_t *msg)
405 {
406 /* pull pin to ground */
407 remote_pull_int();
408
409 uint8_t delay = msg->delay;
410
411 /* start timer, delay between 50ms and 2550ms */
412 if (delay == 0)
413 delay = 5;
414 else if (delay <= 51)
415 delay *= 5;
416 else
417 delay = 255;
418
419 timer_set(&remote.int_timer, delay);
420
421 /* remember state */
422 remote.int_state = INT_PULLED_TIMER;
423 }
424
425 void parse_config_startup(struct remote_msg_config_startup_t *msg)
426 {
427 /* set mode and copy data */
428 memcpy(&startup_config.params, &msg->params, sizeof(struct startup_parameters_t));
429 /* save config to eeprom */
430 storage_save_config();
431 }
432
433 #if UART_TX_ENABLED
434 static void wait_for_uart(void)
435 {
436 while (fifo_fill((fifo_t *)&global_uart.tx) != 0 || !uart_send_complete());
437 }
438 #else
439 /* no need to wait for me */
440 #define wait_for_uart()
441 #endif
442
443 void parse_bootloader(struct remote_msg_bootloader_t *msg)
444 {
445 /* check magic bytes */
446 if (msg->magic[0] != BOOTLOADER_MAGIC_BYTE1 ||
447 msg->magic[1] != BOOTLOADER_MAGIC_BYTE2 ||
448 msg->magic[2] != BOOTLOADER_MAGIC_BYTE3 ||
449 msg->magic[3] != BOOTLOADER_MAGIC_BYTE4)
450 return;
451
452 /* wait until the tx fifo is empty, then start watchdog, but never kick it
453 * (bootloader and firmware both disable the watchdog) */
454 wait_for_uart();
455 wdt_enable(WDTO_120MS);
456 }
457
458 void parse_powerdown(void)
459 {
460 /* configure sleep mode */
461 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
462
463 /* wait until the tx fifo is empty before sleep */
464 wait_for_uart();
465
466 /* save all output registers */
467 uint8_t portb = PORTB;
468 uint8_t portc = PORTC;
469 uint8_t portd = PORTD;
470 uint8_t ddrb = DDRB;
471 uint8_t ddrc = DDRC;
472 uint8_t ddrd = DDRD;
473 PORTB = 0;
474 PORTC = 0;
475 PORTD = 0;
476 DDRB = 0;
477 DDRC = 0;
478 DDRD = 0;
479
480 /* configure int pin as input (already done by setting the
481 * DDR register to zero) with pullup */
482 R_PORT = _BV(INTPIN);
483
484 /* enable int0 low level interrupt */
485 _IFR_INT0 = _BV(INTF0);
486 _ICR_INT0 |= _BV(INT0);
487 /* enter sleep mode */
488 sleep_mode();
489
490 /* wakeup, disable int0 */
491 _ICR_INT0 &= ~_BV(INT0);
492
493 /* restore output registers */
494 PORTB = portb;
495 PORTC = portc;
496 PORTD = portd;
497 DDRB = ddrb;
498 DDRC = ddrc;
499 DDRD = ddrd;
500 }
501
502 /* do nothing, just for wakeup after sleep */
503 EMPTY_INTERRUPT(INT0_vect);
504
505 #endif
Impressum, Datenschutz