]> git.zerfleddert.de Git - fnordlicht-mini/blame - firmware/fnordlicht-firmware/remote.c
more tx disabling.
[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
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;
ec1bef8e 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
76struct remote_state_t remote;
77struct global_remote_t global_remote;
78
79#if CONFIG_SERIAL && CONFIG_REMOTE
80
81/* static parsing routines */
82static void parse_fade_rgb(struct remote_msg_fade_rgb_t *msg);
83static void parse_fade_hsv(struct remote_msg_fade_hsv_t *msg);
84static void parse_save_rgb(struct remote_msg_save_rgb_t *msg);
85static void parse_save_hsv(struct remote_msg_save_hsv_t *msg);
86static void parse_save_current(struct remote_msg_save_current_t *msg);
87static void parse_config_offsets(struct remote_msg_config_offsets_t *msg);
88static void parse_start_program(struct remote_msg_start_program_t *msg);
89static void parse_stop(struct remote_msg_stop_t *msg);
90static void parse_modify_current(struct remote_msg_modify_current_t *msg);
91static void parse_pull_int(struct remote_msg_pull_int_t *msg);
92static void parse_config_startup(struct remote_msg_config_startup_t *msg);
93static void parse_bootloader(struct remote_msg_bootloader_t *msg);
94static void parse_powerdown(void);
95
96void 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;
ec1bef8e 115#endif
116}
117
118static 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
169static 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
183static __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
191static 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
ec1bef8e 198void 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);
ec1bef8e 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 }
ec1bef8e 239}
240
241void 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
251void 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 */
263uint8_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
282static 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
289void 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
298void parse_fade_rgb(struct remote_msg_fade_rgb_t *msg)
299{
300 pwm_fade_rgb(&msg->color, msg->step, msg->delay);
301}
302
303void parse_fade_hsv(struct remote_msg_fade_hsv_t *msg)
304{
305 pwm_fade_hsv(&msg->color, msg->step, msg->delay);
306}
307
308void 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
328void 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
345void 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
365void 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
374void 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
381void parse_stop(struct remote_msg_stop_t *msg)
382{
383 script_stop();
384 if (msg->fade)
385 pwm_stop_fading();
386}
387
388void 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
404void 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
425void 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
a73d0f32 433#if UART_TX_ENABLED
ec1bef8e 434static void wait_for_uart(void)
435{
436 while (fifo_fill((fifo_t *)&global_uart.tx) != 0 || !uart_send_complete());
437}
a73d0f32 438#else
439/* no need to wait for me */
440#define wait_for_uart()
441#endif
ec1bef8e 442
443void 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
458void 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 */
503EMPTY_INTERRUPT(INT0_vect);
504
505#endif
Impressum, Datenschutz