9baf6c2920802512822ed85214fff9b60038178b
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 <config/hardware.h>
39 .low
= FUSE_CKDIV8
& FUSE_SUT0
&\
40 FUSE_CKSEL3
& FUSE_CKSEL2
& FUSE_CKSEL1
,
41 .high
= FUSE_SPIEN
& FUSE_BODLEVEL1
& FUSE_BODLEVEL0
,
42 .extended
= EFUSE_DEFAULT
,
45 OM_MOMENTARY
, /* on as long as "key on" pressed */
46 OM_DOUBLE
, /* On off with "on" and "off" keys */
47 OM_TOGGLE
, /* toggle on "key on" pressed */
48 OM_ERASED
= 0xff /* EEPROM erased, need setup */
53 LM_LEARN_ON_KEY
, /* Learn primary key */
54 LM_LEARN_OFF_KEY
, /* Learn secondary key, relevant for OM_DOUBLE */
55 LM_LEARN_INITIAL
, /* Learn initial pulse length, 10ms steps */
56 LM_LEARN_DUTY
, /* Learn duty cycle 0-10 */
57 LM_LEARN_OP_MODE
, /* Learn operation mode */
58 LM_EASY_WAIT_PRESS
, /* Wait for key press to enter easy mode */
59 LM_EASY_WAIT_UP
, /* Wait for end of key press */
60 LM_EASY_WAIT_TURN
, /* Wait for loco 80 turn end */
61 LM_EASY_MODE
, /* Easy config mode for PWM and initial kick */
62 LM_END
, /* Only a label */
66 uint8_t magic
; /* Magic value */
72 uint8_t initial_pulse
; /* Lenghth of initial pulse in 10ms steps */
73 uint8_t on_duty_cycle
; /* Duty cycle for on. 0-10 */
74 volatile enum learn_mode learn_mode
;
77 static struct EEMEM config ee_config
;
78 static volatile struct config config
;
80 static volatile uint8_t easy_mode
= 0;
81 static volatile uint8_t easy_mode_possible
= 0;
83 #ifndef USE_REGISTER_VARS
84 static volatile uint8_t drive_on
= 0;
87 /******************************************************************************
89 * Some nice sounds to play on your coil
94 #define sekunde(g)(quinte((quinte(g)))*2)
95 #define terz(g) ((g) * 4 / 5)
96 #define kleine_terz(g) ((g) * 5 / 6)
97 #define quarte(g) ((g) * 3 / 4)
98 #define quinte(g) ((g) * 2 / 3)
99 #define tt(g) ((g) * 32 / 45)
100 #define septime(g) ((g) * 15 / 8)
102 #if defined(WITH_SOUND) && defined(WITH_PWM)
103 void play_tone(uint8_t divisor
, uint8_t duration
, uint8_t pause
)
108 c
= (divisor
* 2) / 3;
110 for (c
= 0; c
< duration
- pause
; c
++)
115 for (c
= 0; c
< pause
; c
++)
122 static void tone_enter(void)
124 play_tone((G
), 70, 20);
125 play_tone(terz(G
), 70, 20);
126 play_tone(quinte(G
), 70, 20);
127 play_tone(G
/2, 100, 0);
130 static void tone_good(void)
132 play_tone(G
, 150, 120);
133 play_tone(terz(G
), 100, 70);
134 play_tone(tt(G
), 100, 50);
135 play_tone(quarte(terz((G
))), 50, 0);
136 play_tone(quinte(G
), 150, 0);
137 play_tone(terz(G
), 100, 50);
140 static void snd_on(void)
147 static void snd_off(void)
155 #define play_tone(...)
156 #define tone_enter(...)
157 #define tone_good(...)
162 static void load_config(void)
164 eeprom_read_block((uint8_t *)&config
, &ee_config
, sizeof(config
));
167 static void save_config(void)
169 #ifdef WITH_EEPROM_UPDATE
170 eeprom_update_block((uint8_t *)&config
, &ee_config
, sizeof(config
));
172 eeprom_write_block((uint8_t *)&config
, &ee_config
, sizeof(config
));
176 void mm_pinchange_callback(void)
178 static uint8_t btn_last
= 0;
180 if (BTN_PRESSED
&& !btn_last
) {
182 config
.learn_mode
%= LM_END
;
184 btn_last
= BTN_PRESSED
;
187 static uint8_t get_speed(uint8_t command
)
189 uint8_t b1
, b3
, b5
, b7
;
191 b1
= ((command
& 0x80) != 0);
192 b3
= ((command
& 0x20) != 0);
193 b5
= ((command
& 0x8) != 0);
194 b7
= ((command
& 0x2) != 0);
196 //if ((b1 != b2) || (b2 != b3) || (b3 != b4) || (b5 != b6) || (b7 != b8))
198 return (b1
+ b3
* 2 + b5
* 4 +b7
* 8);
202 void mm_drive_cb(uint8_t loco
, uint8_t function
, uint8_t command
)
205 speed
= get_speed(command
);
206 static uint8_t alert_last
= 0;
210 switch (config
.learn_mode
) {
213 config
.learn_mode
= LM_EASY_WAIT_PRESS
;
216 if ((speed
== 1) && (alert_last
== 0)) {
217 config
.learn_mode
= LM_OFF
;
223 case LM_EASY_WAIT_PRESS
:
225 config
.learn_mode
= LM_OFF
;
231 alert_last
= (speed
== 1);
238 if (config
.learn_mode
== LM_EASY_MODE
) {
239 config
.initial_pulse
= speed
;
246 if (config
.learn_mode
== LM_EASY_MODE
)
247 config
.on_duty_cycle
= speed
;
252 void mm_key_cb(uint8_t decoder
, uint8_t function
, uint8_t command
)
254 static uint8_t toggle_lock
= 0;
256 /* We don't listen to F1-F4 secret function codes */
260 switch(config
.learn_mode
) {
263 if ((decoder
== config
.decoder_on
) &&
264 (command
== config
.key_on
)) { /* Primary key pressed */
265 switch(config
.op_mode
) {
272 drive_on
= ~drive_on
;
280 if ((decoder
== config
.decoder_on
) &&
281 (command
== 0)) { /* Primary key released */
282 switch(config
.op_mode
) {
295 case LM_EASY_WAIT_PRESS
:
296 if ((decoder
== config
.decoder_on
) &&
297 (command
== config
.key_on
))
298 config
.learn_mode
= LM_EASY_WAIT_UP
;
301 case LM_EASY_WAIT_UP
:
302 if ((decoder
== config
.decoder_on
) &&
304 config
.learn_mode
= LM_EASY_MODE
;
309 #ifdef HANDLE_OFF_KEY
311 if ((decoder
== config
.decoder_off
) &&
312 (command
== config
.key_off
)) { /* Secondary "off" key pressed */
313 switch(config
.op_mode
) {
326 case LM_LEARN_ON_KEY
:
328 config
.decoder_on
= decoder
;
329 config
.key_on
= command
;
330 if (config
.op_mode
== OM_DOUBLE
)
331 config
.learn_mode
= LM_LEARN_OFF_KEY
;
333 config
.learn_mode
= LM_OFF
;
337 #ifdef LEARN_ADVANCED
338 case LM_LEARN_OFF_KEY
:
340 config
.decoder_off
= decoder
;
341 config
.key_off
= command
;
342 config
.learn_mode
= LM_OFF
;
347 case LM_LEARN_INITIAL
:
355 if (config
.initial_pulse
>= 10)
356 config
.initial_pulse
-= 10;
358 config
.initial_pulse
= 0;
363 if (config
.initial_pulse
<= 245)
364 config
.initial_pulse
+= 10;
366 config
.initial_pulse
= 255;
383 if (config
.on_duty_cycle
> 0)
384 config
.on_duty_cycle
-= 1;
389 if (config
.on_duty_cycle
< 10)
390 config
.on_duty_cycle
+= 1;
400 case LM_LEARN_OP_MODE
:
403 config
.op_mode
= OM_MOMENTARY
;
405 config
.learn_mode
= LM_OFF
;
408 config
.op_mode
= OM_DOUBLE
;
410 config
.learn_mode
= LM_OFF
;
413 config
.op_mode
= OM_TOGGLE
;
415 config
.learn_mode
= LM_OFF
;
426 /******************************************************************************
428 * main() - The main routine
431 void shift(uint8_t mu
);
433 #ifdef DEBUG_DRIVE_LED
434 #define MON_LED(val) setpin(PIN_LED, val)
440 #define DRIVE_OFF {OCR1B = PWM_CYCLE; MON_LED(0);}
441 #define DRIVE_ON {OCR1B = PWM_CYCLE - config.on_duty_cycle; MON_LED(1);}
442 #define DRIVE_FULL {OCR1B = 0; MON_LED(1);}
444 #define DRIVE_OFF {setpin(PIN_DRIVE, 0); MON_LED(0);}
445 #define DRIVE_ON {setpin(PIN_DRIVE, 1); MON_LED(1);}
446 #define DRIVE_FULL {setpin(PIN_DRIVE, 1); MON_LED(1);}
452 #ifdef WITH_INITIAL_PULSE
453 uint8_t drive_last
= 0;
454 uint8_t drive_slope
= 0;
457 #ifdef USE_REGISTER_VARS
471 if (config
.magic
!= EE_MAGIC
) {
472 config
.magic
= EE_MAGIC
;
473 config
.op_mode
= OM_MOMENTARY
;
474 config
.decoder_on
= 1;
476 config
.decoder_off
= 1;
478 config
.initial_pulse
= 10;
479 config
.on_duty_cycle
= 5;
480 config
.learn_mode
= LM_LEARN_ON_KEY
;
486 #ifdef WITH_INITIAL_PULSE
488 if (drive_on
&& !drive_last
)
492 drive_last
= drive_on
;
497 #ifdef WITH_INITIAL_PULSE
500 for (i
= 0; i
< config
.initial_pulse
; i
++) {
510 if (!config
.learn_mode
||
511 config
.learn_mode
> LM_LEARN_OP_MODE
)
514 for (i
= 0; i
< config
.learn_mode
; i
++) {
520 if (drive_on
) goto drive_start
;
522 if (drive_on
) goto drive_start
;
524 for (i
= 0; i
< 15 - config
.learn_mode
; i
++) {
525 if (drive_on
) goto drive_start
;
533 /******************************************************************************