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
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
56 /* serial communication */
58 struct remote_msg_t msg
;
59 uint8_t buf
[REMOTE_MSG_LEN
];
76 struct remote_state_t remote
;
77 struct global_remote_t global_remote
;
79 #if CONFIG_SERIAL && CONFIG_REMOTE
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);
96 void remote_init(void)
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
);
106 /* initialize offsets */
107 global_remote
.offsets
.saturation
= 255;
108 global_remote
.offsets
.value
= 255;
110 /* initialize int pin, tri-state */
111 R_DDR
&= ~_BV(INTPIN
);
112 R_PORT
&= ~_BV(INTPIN
);
114 remote
.int_state
= INT_IDLE
;
118 static void remote_parse_msg(struct remote_msg_t
*msg
)
121 if (msg
->address
!= global_remote
.address
&& msg
->address
!= REMOTE_ADDR_BROADCAST
)
126 case REMOTE_CMD_FADE_RGB
:
127 parse_fade_rgb((struct remote_msg_fade_rgb_t
*)msg
);
129 case REMOTE_CMD_FADE_HSV
:
130 parse_fade_hsv((struct remote_msg_fade_hsv_t
*)msg
);
132 case REMOTE_CMD_SAVE_RGB
:
133 parse_save_rgb((struct remote_msg_save_rgb_t
*)msg
);
135 case REMOTE_CMD_SAVE_HSV
:
136 parse_save_hsv((struct remote_msg_save_hsv_t
*)msg
);
138 case REMOTE_CMD_SAVE_CURRENT
:
139 parse_save_current((struct remote_msg_save_current_t
*)msg
);
141 case REMOTE_CMD_CONFIG_OFFSETS
:
142 parse_config_offsets((struct remote_msg_config_offsets_t
*)msg
);
144 case REMOTE_CMD_START_PROGRAM
:
145 parse_start_program((struct remote_msg_start_program_t
*)msg
);
147 case REMOTE_CMD_STOP
:
148 parse_stop((struct remote_msg_stop_t
*)msg
);
150 case REMOTE_CMD_MODIFY_CURRENT
:
151 parse_modify_current((struct remote_msg_modify_current_t
*)msg
);
153 case REMOTE_CMD_PULL_INT
:
154 parse_pull_int((struct remote_msg_pull_int_t
*)msg
);
156 case REMOTE_CMD_CONFIG_STARTUP
:
157 parse_config_startup((struct remote_msg_config_startup_t
*)msg
);
159 case REMOTE_CMD_BOOTLOADER
:
160 parse_bootloader((struct remote_msg_bootloader_t
*)msg
);
162 case REMOTE_CMD_POWERDOWN
:
169 static PT_THREAD(remote_thread(struct pt
*thread
))
174 PT_WAIT_UNTIL(thread
, remote
.buflen
== REMOTE_MSG_LEN
);
176 remote_parse_msg(&remote
.msg
);
183 static __noinline
void send_msg(struct remote_msg_t
*msg
)
185 uint8_t *ptr
= (uint8_t *)msg
;
186 for (uint8_t i
= 0; i
< REMOTE_MSG_LEN
; i
++)
191 static void send_resync(uint8_t addr
)
193 for (uint8_t i
= 0; i
< REMOTE_SYNC_LEN
; i
++)
194 uart_putc(REMOTE_CMD_RESYNC
);
198 void remote_poll(void)
200 if (fifo_fill((fifo_t
*)&global_uart
.rx
) > 0) {
201 uint8_t data
= fifo_dequeue((fifo_t
*)&global_uart
.rx
);
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
;
209 /* reset remote buffer */
212 /* enable remote command thread */
214 PT_INIT(&remote
.thread
);
216 /* just pass through data */
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
;
225 /* remember the number of sync bytes received so far */
226 if (data
== REMOTE_CMD_RESYNC
)
233 PT_SCHEDULE(remote_thread(&remote
.thread
));
235 /* check int timer */
236 if (remote
.int_state
== INT_PULLED_TIMER
&& timer_expired(&remote
.int_timer
)) {
237 remote_release_int();
241 void remote_pull_int(void)
243 if (remote
.int_state
!= INT_IDLE
)
246 /* pull int pin to ground */
247 R_DDR
|= _BV(INTPIN
);
248 R_PORT
&= ~_BV(INTPIN
);
251 void remote_release_int(void)
253 if (remote
.int_state
== INT_IDLE
)
256 /* reset pin to tri-state */
257 R_DDR
&= ~_BV(INTPIN
);
258 R_PORT
&= ~_BV(INTPIN
);
259 remote
.int_state
= INT_IDLE
;
262 /* offset helper functions */
263 uint8_t remote_apply_offset(uint8_t value
, int8_t offset
)
270 } else if (offset
> 0) {
271 uint16_t temp
= value
;
282 static uint8_t apply_scale(uint8_t value
, uint8_t scale
)
284 uint16_t temp
= value
* scale
;
289 void remote_apply_hsv_offset(struct hsv_color_t
*color
)
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
);
296 /* parser functions */
298 void parse_fade_rgb(struct remote_msg_fade_rgb_t
*msg
)
300 pwm_fade_rgb(&msg
->color
, msg
->step
, msg
->delay
);
303 void parse_fade_hsv(struct remote_msg_fade_hsv_t
*msg
)
305 pwm_fade_hsv(&msg
->color
, msg
->step
, msg
->delay
);
308 void parse_save_rgb(struct remote_msg_save_rgb_t
*msg
)
310 if (msg
->slot
>= CONFIG_EEPROM_COLORS
)
313 struct storage_color_t c
;
315 c
.delay
= msg
->delay
;
316 c
.pause
= msg
->pause
;
318 /* mark color as rgb */
319 c
.color
.rgb_marker
= 255;
321 c
.color
.red
= msg
->color
.red
;
322 c
.color
.green
= msg
->color
.green
;
323 c
.color
.blue
= msg
->color
.blue
;
325 storage_save_color(msg
->slot
, &c
);
328 void parse_save_hsv(struct remote_msg_save_hsv_t
*msg
)
330 if (msg
->slot
>= CONFIG_EEPROM_COLORS
)
333 struct storage_color_t c
;
335 c
.delay
= msg
->delay
;
336 c
.pause
= msg
->pause
;
338 c
.color
.hue
= msg
->color
.hue
;
339 c
.color
.saturation
= msg
->color
.saturation
;
340 c
.color
.value
= msg
->color
.value
;
342 storage_save_color(msg
->slot
, &c
);
345 void parse_save_current(struct remote_msg_save_current_t
*msg
)
347 if (msg
->slot
>= CONFIG_EEPROM_COLORS
)
350 struct storage_color_t c
;
352 c
.delay
= msg
->delay
;
353 c
.pause
= msg
->pause
;
355 /* mark color as rgb */
356 c
.color
.rgb_marker
= 255;
358 c
.color
.red
= global_pwm
.current
.red
;
359 c
.color
.green
= global_pwm
.current
.green
;
360 c
.color
.blue
= global_pwm
.current
.blue
;
362 storage_save_color(msg
->slot
, &c
);
365 void parse_config_offsets(struct remote_msg_config_offsets_t
*msg
)
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
;
374 void parse_start_program(struct remote_msg_start_program_t
*msg
)
378 script_start(0, msg
->script
, &msg
->params
);
381 void parse_stop(struct remote_msg_stop_t
*msg
)
388 void parse_modify_current(struct remote_msg_modify_current_t
*msg
)
390 /* return if a color change is in progress */
392 if (!global_script
.disable
|| !pwm_target_reached())
395 if (!pwm_target_reached())
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
);
404 void parse_pull_int(struct remote_msg_pull_int_t
*msg
)
406 /* pull pin to ground */
409 uint8_t delay
= msg
->delay
;
411 /* start timer, delay between 50ms and 2550ms */
414 else if (delay
<= 51)
419 timer_set(&remote
.int_timer
, delay
);
422 remote
.int_state
= INT_PULLED_TIMER
;
425 void parse_config_startup(struct remote_msg_config_startup_t
*msg
)
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();
434 static void wait_for_uart(void)
436 while (fifo_fill((fifo_t
*)&global_uart
.tx
) != 0 || !uart_send_complete());
439 /* no need to wait for me */
440 #define wait_for_uart()
443 void parse_bootloader(struct remote_msg_bootloader_t
*msg
)
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
)
452 /* wait until the tx fifo is empty, then start watchdog, but never kick it
453 * (bootloader and firmware both disable the watchdog) */
455 wdt_enable(WDTO_120MS
);
458 void parse_powerdown(void)
460 /* configure sleep mode */
461 set_sleep_mode(SLEEP_MODE_PWR_DOWN
);
463 /* wait until the tx fifo is empty before sleep */
466 /* save all output registers */
467 uint8_t portb
= PORTB
;
468 uint8_t portc
= PORTC
;
469 uint8_t portd
= PORTD
;
480 /* configure int pin as input (already done by setting the
481 * DDR register to zero) with pullup */
482 R_PORT
= _BV(INTPIN
);
484 /* enable int0 low level interrupt */
485 _IFR_INT0
= _BV(INTF0
);
486 _ICR_INT0
|= _BV(INT0
);
487 /* enter sleep mode */
490 /* wakeup, disable int0 */
491 _ICR_INT0
&= ~_BV(INT0
);
493 /* restore output registers */
502 /* do nothing, just for wakeup after sleep */
503 EMPTY_INTERRUPT(INT0_vect
);