1 /* vim:ts=4 sts=4 et tw=80
5 * for additional information please
6 * see http://lochraster.org/fnordlichtmini
8 * (c) by Alexander Neumann <alexander@bumpern.de>
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.
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
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/>.
26 #include <avr/pgmspace.h>
27 #include <avr/sleep.h>
28 #include <avr/interrupt.h>
29 #include <util/delay.h>
35 #include "../common/pt/pt.h"
37 #include "remote-proto.h"
38 #include "../common/common.h"
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
50 /* serial communication */
52 struct remote_msg_t msg
;
53 uint8_t buf
[REMOTE_MSG_LEN
];
70 struct remote_state_t remote
;
71 struct global_remote_t global_remote
;
73 #if CONFIG_SERIAL && CONFIG_REMOTE
75 /* static parsing routines */
76 static void parse_fade_rgb(struct remote_msg_fade_rgb_t
*msg
);
77 static void parse_fade_hsv(struct remote_msg_fade_hsv_t
*msg
);
78 static void parse_save_rgb(struct remote_msg_save_rgb_t
*msg
);
79 static void parse_save_hsv(struct remote_msg_save_hsv_t
*msg
);
80 static void parse_save_current(struct remote_msg_save_current_t
*msg
);
81 static void parse_config_offsets(struct remote_msg_config_offsets_t
*msg
);
82 static void parse_start_program(struct remote_msg_start_program_t
*msg
);
83 static void parse_stop(struct remote_msg_stop_t
*msg
);
84 static void parse_modify_current(struct remote_msg_modify_current_t
*msg
);
85 static void parse_pull_int(struct remote_msg_pull_int_t
*msg
);
86 static void parse_config_startup(struct remote_msg_config_startup_t
*msg
);
87 static void parse_bootloader(struct remote_msg_bootloader_t
*msg
);
88 static void parse_powerdown(void);
90 void remote_init(void)
92 /* initialize offsets */
93 global_remote
.offsets
.saturation
= 255;
94 global_remote
.offsets
.value
= 255;
96 /* initialize int pin, tri-state */
97 R_DDR
&= ~_BV(INTPIN
);
98 R_PORT
&= ~_BV(INTPIN
);
100 remote
.int_state
= INT_IDLE
;
104 static void remote_parse_msg(struct remote_msg_t
*msg
)
107 if (msg
->address
!= global_remote
.address
&& msg
->address
!= REMOTE_ADDR_BROADCAST
)
112 case REMOTE_CMD_FADE_RGB
:
113 parse_fade_rgb((struct remote_msg_fade_rgb_t
*)msg
);
115 case REMOTE_CMD_FADE_HSV
:
116 parse_fade_hsv((struct remote_msg_fade_hsv_t
*)msg
);
118 case REMOTE_CMD_SAVE_RGB
:
119 parse_save_rgb((struct remote_msg_save_rgb_t
*)msg
);
121 case REMOTE_CMD_SAVE_HSV
:
122 parse_save_hsv((struct remote_msg_save_hsv_t
*)msg
);
124 case REMOTE_CMD_SAVE_CURRENT
:
125 parse_save_current((struct remote_msg_save_current_t
*)msg
);
127 case REMOTE_CMD_CONFIG_OFFSETS
:
128 parse_config_offsets((struct remote_msg_config_offsets_t
*)msg
);
130 case REMOTE_CMD_START_PROGRAM
:
131 parse_start_program((struct remote_msg_start_program_t
*)msg
);
133 case REMOTE_CMD_STOP
:
134 parse_stop((struct remote_msg_stop_t
*)msg
);
136 case REMOTE_CMD_MODIFY_CURRENT
:
137 parse_modify_current((struct remote_msg_modify_current_t
*)msg
);
139 case REMOTE_CMD_PULL_INT
:
140 parse_pull_int((struct remote_msg_pull_int_t
*)msg
);
142 case REMOTE_CMD_CONFIG_STARTUP
:
143 parse_config_startup((struct remote_msg_config_startup_t
*)msg
);
145 case REMOTE_CMD_BOOTLOADER
:
146 parse_bootloader((struct remote_msg_bootloader_t
*)msg
);
148 case REMOTE_CMD_POWERDOWN
:
155 static PT_THREAD(remote_thread(struct pt
*thread
))
160 PT_WAIT_UNTIL(thread
, remote
.buflen
== REMOTE_MSG_LEN
);
162 remote_parse_msg(&remote
.msg
);
169 static __noinline
void send_msg(struct remote_msg_t
*msg
)
171 uint8_t *ptr
= (uint8_t *)msg
;
172 for (uint8_t i
= 0; i
< REMOTE_MSG_LEN
; i
++)
177 static void send_resync(uint8_t addr
)
179 for (uint8_t i
= 0; i
< REMOTE_SYNC_LEN
; i
++)
180 uart_putc(REMOTE_CMD_RESYNC
);
184 void remote_poll(void)
186 if (fifo_fill((fifo_t
*)&global_uart
.rx
) > 0) {
187 uint8_t data
= fifo_dequeue((fifo_t
*)&global_uart
.rx
);
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
;
195 /* reset remote buffer */
198 /* enable remote command thread */
200 PT_INIT(&remote
.thread
);
202 /* just pass through data */
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
;
211 /* remember the number of sync bytes received so far */
212 if (data
== REMOTE_CMD_RESYNC
)
219 PT_SCHEDULE(remote_thread(&remote
.thread
));
221 /* check int timer */
222 if (remote
.int_state
== INT_PULLED_TIMER
&& timer_expired(&remote
.int_timer
)) {
223 remote_release_int();
227 void remote_pull_int(void)
229 if (remote
.int_state
!= INT_IDLE
)
232 /* pull int pin to ground */
233 R_DDR
|= _BV(INTPIN
);
234 R_PORT
&= ~_BV(INTPIN
);
237 void remote_release_int(void)
239 if (remote
.int_state
== INT_IDLE
)
242 /* reset pin to tri-state */
243 R_DDR
&= ~_BV(INTPIN
);
244 R_PORT
&= ~_BV(INTPIN
);
245 remote
.int_state
= INT_IDLE
;
248 /* offset helper functions */
249 uint8_t remote_apply_offset(uint8_t value
, int8_t offset
)
256 } else if (offset
> 0) {
257 uint16_t temp
= value
;
268 static uint8_t apply_scale(uint8_t value
, uint8_t scale
)
270 uint16_t temp
= value
* scale
;
275 void remote_apply_hsv_offset(struct hsv_color_t
*color
)
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
);
282 /* parser functions */
284 void parse_fade_rgb(struct remote_msg_fade_rgb_t
*msg
)
286 pwm_fade_rgb(&msg
->color
, msg
->step
, msg
->delay
);
289 void parse_fade_hsv(struct remote_msg_fade_hsv_t
*msg
)
291 pwm_fade_hsv(&msg
->color
, msg
->step
, msg
->delay
);
294 void parse_save_rgb(struct remote_msg_save_rgb_t
*msg
)
296 if (msg
->slot
>= CONFIG_EEPROM_COLORS
)
299 struct storage_color_t c
;
301 c
.delay
= msg
->delay
;
302 c
.pause
= msg
->pause
;
304 /* mark color as rgb */
305 c
.color
.rgb_marker
= 255;
307 c
.color
.red
= msg
->color
.red
;
308 c
.color
.green
= msg
->color
.green
;
309 c
.color
.blue
= msg
->color
.blue
;
311 storage_save_color(msg
->slot
, &c
);
314 void parse_save_hsv(struct remote_msg_save_hsv_t
*msg
)
316 if (msg
->slot
>= CONFIG_EEPROM_COLORS
)
319 struct storage_color_t c
;
321 c
.delay
= msg
->delay
;
322 c
.pause
= msg
->pause
;
324 c
.color
.hue
= msg
->color
.hue
;
325 c
.color
.saturation
= msg
->color
.saturation
;
326 c
.color
.value
= msg
->color
.value
;
328 storage_save_color(msg
->slot
, &c
);
331 void parse_save_current(struct remote_msg_save_current_t
*msg
)
333 if (msg
->slot
>= CONFIG_EEPROM_COLORS
)
336 struct storage_color_t c
;
338 c
.delay
= msg
->delay
;
339 c
.pause
= msg
->pause
;
341 /* mark color as rgb */
342 c
.color
.rgb_marker
= 255;
344 c
.color
.red
= global_pwm
.current
.red
;
345 c
.color
.green
= global_pwm
.current
.green
;
346 c
.color
.blue
= global_pwm
.current
.blue
;
348 storage_save_color(msg
->slot
, &c
);
351 void parse_config_offsets(struct remote_msg_config_offsets_t
*msg
)
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
;
360 void parse_start_program(struct remote_msg_start_program_t
*msg
)
364 script_start(0, msg
->script
, &msg
->params
);
367 void parse_stop(struct remote_msg_stop_t
*msg
)
374 void parse_modify_current(struct remote_msg_modify_current_t
*msg
)
376 /* return if a color change is in progress */
378 if (!global_script
.disable
|| !pwm_target_reached())
381 if (!pwm_target_reached())
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
);
390 void parse_pull_int(struct remote_msg_pull_int_t
*msg
)
392 /* pull pin to ground */
395 uint8_t delay
= msg
->delay
;
397 /* start timer, delay between 50ms and 2550ms */
400 else if (delay
<= 51)
405 timer_set(&remote
.int_timer
, delay
);
408 remote
.int_state
= INT_PULLED_TIMER
;
411 void parse_config_startup(struct remote_msg_config_startup_t
*msg
)
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();
420 static void wait_for_uart(void)
422 while (fifo_fill((fifo_t
*)&global_uart
.tx
) != 0 || !uart_send_complete());
425 /* no need to wait for me */
426 #define wait_for_uart()
429 void parse_bootloader(struct remote_msg_bootloader_t
*msg
)
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
)
438 /* wait until the tx fifo is empty, then start watchdog, but never kick it
439 * (bootloader and firmware both disable the watchdog) */
441 wdt_enable(WDTO_120MS
);
444 void parse_powerdown(void)
446 /* configure sleep mode */
447 set_sleep_mode(SLEEP_MODE_PWR_DOWN
);
449 /* wait until the tx fifo is empty before sleep */
452 /* save all output registers */
453 uint8_t portb
= PORTB
;
454 uint8_t portc
= PORTC
;
455 uint8_t portd
= PORTD
;
466 /* configure int pin as input (already done by setting the
467 * DDR register to zero) with pullup */
468 R_PORT
= _BV(INTPIN
);
470 /* enable int0 low level interrupt */
471 _IFR_INT0
= _BV(INTF0
);
472 _ICR_INT0
|= _BV(INT0
);
473 /* enter sleep mode */
476 /* wakeup, disable int0 */
477 _ICR_INT0
&= ~_BV(INT0
);
479 /* restore output registers */
488 /* do nothing, just for wakeup after sleep */
489 EMPTY_INTERRUPT(INT0_vect
);