1 /******************************************************************************
3 * Trennfix firmware - main.c
5 * Copyright (C) 2017 Philipp Hachtmann
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *****************************************************************************/
27 #include <avr/eeprom.h>
28 #include <avr/interrupt.h>
29 #include <avr/pgmspace.h>
31 #include <util/delay.h>
33 #include <mm/mm_switch.h>
34 #include <mm/mm_decode.h>
35 #include <config/hardware.h>
37 uint8_t eeFooByte EEMEM
= 123;
42 .low
= FUSE_CKDIV8
& FUSE_SUT0
&\
43 FUSE_CKSEL3
& FUSE_CKSEL2
& FUSE_CKSEL1
,
44 .high
= FUSE_SPIEN
& FUSE_BODLEVEL1
& FUSE_BODLEVEL0
& FUSE_EESAVE
,
45 .extended
= EFUSE_DEFAULT
,
48 void mm_key_cb(uint8_t decoder
, uint8_t function
, uint8_t key
);
49 void mm_drive_cb(uint8_t decoder
, uint8_t function
, uint8_t command
);
52 OM_MOMENTARY
, /* on as long as "key on" pressed */
53 OM_DOUBLE
, /* On off with "on" and "off" keys */
54 OM_TOGGLE
, /* toggle on "key on" pressed */
55 OM_ERASED
= 0xff /* EEPROM erased, need setup */
60 LM_LEARN_ON_KEY
, /* Learn primary key */
61 LM_LEARN_OFF_KEY
, /* Learn secondary key, relevant for OM_DOUBLE */
62 LM_LEARN_INITIAL
, /* Learn initial pulse length, 10ms steps */
63 LM_LEARN_DUTY
, /* Learn duty cycle 0-10 */
64 LM_LEARN_OP_MODE
, /* Learn operation mode */
65 LM_EASY_WAIT_PRESS
, /* Wait for key press to enter easy mode */
66 LM_EASY_WAIT_UP
, /* Wait for end of key press */
67 LM_EASY_WAIT_TURN
, /* Wait for loco 80 turn end */
68 LM_EASY_MODE
, /* Easy config mode for PWM and initial kick */
69 LM_END
, /* Only a label */
73 uint8_t magic
; /* Magic value */
79 uint8_t initial_pulse
; /* Lenghth of initial pulse in 10ms steps */
80 uint8_t on_duty_cycle
[15]; /* Duty cycle for on. 0-10 */
81 volatile enum learn_mode learn_mode
;
84 static uint8_t main_speed
= 0;
85 static struct EEMEM config ee_config
;
86 static volatile struct config config
;
88 static volatile uint8_t easy_mode
= 0;
89 static volatile uint8_t easy_mode_possible
= 0;
91 #ifndef USE_REGISTER_VARS
92 static volatile uint8_t drive_on
= 0;
95 /******************************************************************************
97 * Some nice sounds to play on your coil
102 #define sekunde(g)(quinte((quinte(g)))*2)
103 #define terz(g) ((g) * 4 / 5)
104 #define kleine_terz(g) ((g) * 5 / 6)
105 #define quarte(g) ((g) * 3 / 4)
106 #define quinte(g) ((g) * 2 / 3)
107 #define tt(g) ((g) * 32 / 45)
108 #define septime(g) ((g) * 15 / 8)
110 #if defined(WITH_SOUND) && defined(WITH_PWM)
111 void play_tone(uint8_t divisor
, uint8_t duration
, uint8_t pause
)
116 c
= (divisor
* 2) / 3;
118 for (c
= 0; c
< duration
- pause
; c
++)
123 for (c
= 0; c
< pause
; c
++)
130 static void tone_enter(void)
132 play_tone((G
), 70, 20);
133 play_tone(terz(G
), 70, 20);
134 play_tone(quinte(G
), 70, 20);
135 play_tone(G
/2, 100, 0);
138 static void tone_good(void)
140 play_tone(G
, 150, 120);
141 play_tone(terz(G
), 100, 70);
142 play_tone(tt(G
), 100, 50);
143 play_tone(quarte(terz((G
))), 50, 0);
144 play_tone(quinte(G
), 150, 0);
145 play_tone(terz(G
), 100, 50);
148 static void snd_on(void)
155 static void snd_off(void)
163 #define play_tone(...)
164 #define tone_enter(...) {setpin(PIN_LED, 1); _delay_ms(500); setpin(PIN_LED, 0);}
165 #define tone_good(...)
170 static void load_config(void)
172 eeprom_read_block((uint8_t *)&config
, &ee_config
, sizeof(config
));
175 static void save_config(void)
177 #ifdef WITH_EEPROM_UPDATE
178 eeprom_update_block((uint8_t *)&config
, &ee_config
, sizeof(config
));
180 eeprom_write_block((uint8_t *)&config
, &ee_config
, sizeof(config
));
184 void mm_pinchange_callback(void)
186 static uint8_t btn_last
= 0;
188 if (BTN_PRESSED
&& !btn_last
) {
190 config
.learn_mode
%= LM_END
;
192 btn_last
= BTN_PRESSED
;
195 static uint8_t get_speed(uint8_t command
)
197 uint8_t b0
, b2
, b4
, b6
;
199 b0
= ((command
& 0x80) != 0);
200 b2
= ((command
& 0x20) != 0);
201 b4
= ((command
& 0x8) != 0);
202 b6
= ((command
& 0x2) != 0);
204 //if ((b9!= b2) || (b2 != b3) || (b3 != b4) || (b5 != b6) || (b7 != b8))
206 return (b0
+ b2
* 2 + b4
* 4 +b6
* 8);
224 static void dequeue_commands(void)
229 struct mm_command
*cmd
= mm_get();
232 if (cmd
->recmode
!= MM_FAST
)
234 if ((cmd
->function
& 0x3) != 0x3)
236 // uint8_t loco = mm_lookup_decoder(cmd->address);
242 void mm_drive_cb(uint8_t loco
, uint8_t func_enc
, uint8_t cmd_enc
)
244 uint8_t b1
, b3
, b5
, b7
;
247 /* Those three are decoded from fun and command */
250 enum mm2_command mm2_command
= CF_MM1
;
251 static uint8_t alert_last
= 0;
252 speed
= get_speed(cmd_enc
);
255 goto mm2_done
; // FIXME
257 b1
= ((cmd_enc
& 0x4) != 0);
258 b3
= ((cmd_enc
& 0x1) != 0);
259 b5
= ((cmd_enc
& 0x4) != 0);
260 b7
= ((cmd_enc
& 0x1) != 0);
262 fcode
= b1
* 8 + b3
* 4 + b5
*2 + b7
;
264 /* The ugly speed section */
267 mm2_command
= CF_REVERSE
;
271 mm2_command
= CF_FORWARD
;
276 mm2_command
= CF_REVERSE
;
280 mm2_command
= CF_FORWARD
;
285 /* Special cases for f1-f4 commands */
289 mm2_command
= CF_F1_ON
;
292 mm2_command
= CF_F2_ON
;
295 mm2_command
= CF_F3_ON
;
298 mm2_command
= CF_F4_ON
;
303 } else if (fcode
== 0x5) {
306 mm2_command
= CF_F1_OFF
;
309 mm2_command
= CF_F2_OFF
;
312 mm2_command
= CF_F3_OFF
;
315 mm2_command
= CF_F4_OFF
;
322 mm2_command
= CF_F1_OFF
;
325 mm2_command
= CF_F1_ON
;
329 mm2_command
= CF_F2_OFF
;
332 mm2_command
= CF_F2_ON
;
336 mm2_command
= CF_F3_OFF
;
340 mm2_command
= CF_F3_ON
;
344 mm2_command
= CF_F4_OFF
;
348 mm2_command
= CF_F4_ON
;
355 switch(mm2_command
) {
367 if (loco
== config
.decoder_on
) {
376 switch (config
.learn_mode
) {
379 config
.learn_mode
= LM_EASY_WAIT_PRESS
;
382 if ((speed
== 1) && (alert_last
== 0)) {
383 config
.learn_mode
= LM_OFF
;
389 case LM_EASY_WAIT_PRESS
:
391 config
.learn_mode
= LM_OFF
;
397 alert_last
= (speed
== 1);
404 if (config
.learn_mode
== LM_EASY_MODE
) {
405 config
.initial_pulse
= speed
;
412 if (config
.learn_mode
== LM_EASY_MODE
)
413 config
.on_duty_cycle
[main_speed
] = speed
;
418 void mm_key_cb(uint8_t decoder
, uint8_t function
, uint8_t raw_command
)
420 static uint8_t toggle_lock
= 0;
423 static uint8_t last_raw
= 0;
424 static uint8_t last_fkey
= 0;
426 if ((function
& 3) == 3) { /* F key hack! */
428 diff
= last_raw
^ raw_command
;
432 else if (diff
& 0x20)
439 if ((command
== config
.key_on
)
440 && (last_raw
& ~raw_command
))
442 last_raw
= raw_command
;
447 command
= mm_lookup_key(raw_command
);
450 switch(config
.learn_mode
) {
453 if ((decoder
== config
.decoder_on
) &&
454 (command
== config
.key_on
)) { /* Primary key pressed */
455 switch(config
.op_mode
) {
462 drive_on
= ~drive_on
;
470 if ((decoder
== config
.decoder_on
) &&
471 (command
== 0)) { /* Primary key released */
472 switch(config
.op_mode
) {
485 case LM_EASY_WAIT_PRESS
:
486 if ((decoder
== config
.decoder_on
) &&
487 (command
== config
.key_on
))
488 config
.learn_mode
= LM_EASY_WAIT_UP
;
491 case LM_EASY_WAIT_UP
:
492 if ((decoder
== config
.decoder_on
) &&
494 config
.learn_mode
= LM_EASY_MODE
;
499 #ifdef HANDLE_OFF_KEY
501 if ((decoder
== config
.decoder_off
) &&
502 (command
== config
.key_off
)) { /* Secondary "off" key pressed */
503 switch(config
.op_mode
) {
516 case LM_LEARN_ON_KEY
:
518 config
.decoder_on
= decoder
;
519 config
.key_on
= command
;
520 if (config
.op_mode
== OM_DOUBLE
)
521 config
.learn_mode
= LM_LEARN_OFF_KEY
;
523 config
.learn_mode
= LM_OFF
;
527 #ifdef LEARN_ADVANCED
528 case LM_LEARN_OFF_KEY
:
530 config
.decoder_off
= decoder
;
531 config
.key_off
= command
;
532 config
.learn_mode
= LM_OFF
;
537 case LM_LEARN_INITIAL
:
545 if (config
.initial_pulse
>= 10)
546 config
.initial_pulse
-= 10;
548 config
.initial_pulse
= 0;
553 if (config
.initial_pulse
<= 245)
554 config
.initial_pulse
+= 10;
556 config
.initial_pulse
= 255;
573 if (config
.on_duty_cycle
> 0)
574 config
.on_duty_cycle
-= 1;
579 if (config
.on_duty_cycle
< 10)
580 config
.on_duty_cycle
+= 1;
590 case LM_LEARN_OP_MODE
:
593 config
.op_mode
= OM_MOMENTARY
;
595 config
.learn_mode
= LM_OFF
;
598 config
.op_mode
= OM_DOUBLE
;
600 config
.learn_mode
= LM_OFF
;
603 config
.op_mode
= OM_TOGGLE
;
605 config
.learn_mode
= LM_OFF
;
616 /******************************************************************************
618 * main() - The main routine
621 void shift(uint8_t mu
);
623 #ifdef DEBUG_DRIVE_LED
624 #define MON_LED(val) setpin(PIN_LED, val)
630 #define DRIVE_OFF {OCR1B = PWM_CYCLE; MON_LED(0);}
631 #define DRIVE_ON {OCR1B = PWM_CYCLE - config.on_duty_cycle[main_speed]; \
633 #define DRIVE_FULL {OCR1B = 0; MON_LED(1);}
635 #define DRIVE_OFF {setpin(PIN_DRIVE, 0); MON_LED(0);}
636 #define DRIVE_ON {setpin(PIN_DRIVE, 1); MON_LED(1);}
637 #define DRIVE_FULL {setpin(PIN_DRIVE, 1); MON_LED(1);}
643 #ifdef WITH_INITIAL_PULSE
644 uint8_t drive_last
= 0;
645 uint8_t drive_slope
= 0;
648 #ifdef USE_REGISTER_VARS
662 if (config
.magic
!= EE_MAGIC
) {
663 config
.magic
= EE_MAGIC
;
664 config
.op_mode
= OM_MOMENTARY
;
665 config
.decoder_on
= 1;
667 config
.decoder_off
= 1;
669 config
.initial_pulse
= 10;
670 config
.on_duty_cycle
[0] = 5;
671 config
.learn_mode
= LM_LEARN_ON_KEY
;
680 #ifdef WITH_INITIAL_PULSE
682 if (drive_on
&& !drive_last
)
686 drive_last
= drive_on
;
691 #ifdef WITH_INITIAL_PULSE
694 for (i
= 0; i
< config
.initial_pulse
; i
++) {
704 if (!config
.learn_mode
||
705 config
.learn_mode
> LM_LEARN_OP_MODE
)
708 for (i
= 0; i
< config
.learn_mode
; i
++) {
714 if (drive_on
) goto drive_start
;
716 if (drive_on
) goto drive_start
;
718 for (i
= 0; i
< 15 - config
.learn_mode
; i
++) {
719 if (drive_on
) goto drive_start
;
729 /******************************************************************************