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