trennfix/sw: Added smokefix, further modularized the mm decoder
[eisenbahn.git] / trennfix / sw / src / trennfix_main.c
CommitLineData
54de37bf
PH
1/******************************************************************************
2 *
3 * Trennfix firmware - main.c
4 *
5 * Copyright (C) 2017 Philipp Hachtmann
6 *
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.
11 *
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.
16 *
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/>.
19 *
20 *****************************************************************************/
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include <avr/io.h>
27#include <avr/eeprom.h>
28#include <avr/interrupt.h>
29#include <avr/pgmspace.h>
30
31#include <util/delay.h>
32#include <stdint.h>
93cb14d4
PH
33#include <mm/mm_switch.h>
34#include <config/hardware.h>
54de37bf 35
70095677
PH
36#define EE_MAGIC 0xab
37
f12652df
PH
38FUSES = {
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,
43};
70095677
PH
44enum op_mode {
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 */
49};
50
51enum learn_mode {
52 LM_OFF = 0,
dc41eb22
PH
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 */
f12652df 63};
70095677
PH
64
65struct config {
66 uint8_t magic; /* Magic value */
67 enum op_mode op_mode;
68 uint8_t decoder_on;
69 uint8_t key_on;
70 uint8_t decoder_off;
71 uint8_t key_off;
93cb14d4
PH
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;
70095677
PH
75};
76
77static struct EEMEM config ee_config;
93cb14d4 78static volatile struct config config;
dc41eb22
PH
79
80static volatile uint8_t easy_mode = 0;
81static volatile uint8_t easy_mode_possible = 0;
82
83#ifndef USE_REGISTER_VARS
93cb14d4 84static volatile uint8_t drive_on = 0;
dc41eb22
PH
85#endif
86
87/******************************************************************************
88 *
89 * Some nice sounds to play on your coil
90 *
91 */
92
93#define G 180
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)
101
102#if defined(WITH_SOUND) && defined(WITH_PWM)
103void play_tone(uint8_t divisor, uint8_t duration, uint8_t pause)
104{
105 uint16_t c;
106 TCCR1 = 0x8;
107 OCR1C = divisor;
d0047978
PH
108 c = (divisor * 2) / 3;
109 OCR1B = c;
dc41eb22
PH
110 for (c = 0; c < duration - pause; c++)
111 _delay_ms(2);
d0047978
PH
112 TCCR1 = 0x6;
113 OCR1C = PWM_CYCLE;
114 OCR1B = 12;
dc41eb22
PH
115 for (c = 0; c < pause; c++)
116 _delay_ms(2);
117 TCCR1 = 0x6;
d0047978
PH
118 OCR1C = PWM_CYCLE;
119 OCR1B = PWM_CYCLE;
dc41eb22
PH
120}
121
122static void tone_enter(void)
123{
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);
128}
129
130static void tone_good(void)
131{
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);
f12652df 137 play_tone(terz(G), 100, 50);
dc41eb22
PH
138}
139
140static void snd_on(void)
141{
142 TCCR1 = 0x8;
143 OCR1C = 120;
144 OCR1B = 60;
145}
146
147static void snd_off(void)
148{
149 TCCR1 = 0x6;
d0047978
PH
150 OCR1C = PWM_CYCLE;
151 OCR1B = PWM_CYCLE;
dc41eb22
PH
152}
153
154#else
155#define play_tone(...)
156#define tone_enter(...)
157#define tone_good(...)
158#define snd_on(...)
159#define snd_off(...)
160#endif
70095677
PH
161
162static void load_config(void)
54de37bf 163{
93cb14d4 164 eeprom_read_block((uint8_t *)&config, &ee_config, sizeof(config));
54de37bf
PH
165}
166
70095677 167static void save_config(void)
54de37bf 168{
dc41eb22 169#ifdef WITH_EEPROM_UPDATE
93cb14d4
PH
170 eeprom_update_block((uint8_t *)&config, &ee_config, sizeof(config));
171#else
172 eeprom_write_block((uint8_t *)&config, &ee_config, sizeof(config));
173#endif
54de37bf
PH
174}
175
f12652df 176void mm_pinchange_callback(void)
d0047978 177{
70095677 178 static uint8_t btn_last = 0;
56b25f8b 179
70095677 180 if (BTN_PRESSED && !btn_last) {
93cb14d4
PH
181 config.learn_mode++;
182 config.learn_mode %= LM_END;
70095677
PH
183 }
184 btn_last = BTN_PRESSED;
54de37bf
PH
185}
186
56b25f8b
PH
187static uint8_t get_speed(uint8_t command)
188{
d0047978 189 uint8_t b1, b3, b5, b7;
f12652df 190
56b25f8b 191 b1 = ((command & 0x80) != 0);
56b25f8b 192 b3 = ((command & 0x20) != 0);
56b25f8b 193 b5 = ((command & 0x8) != 0);
56b25f8b 194 b7 = ((command & 0x2) != 0);
f12652df 195
56b25f8b
PH
196 //if ((b1 != b2) || (b2 != b3) || (b3 != b4) || (b5 != b6) || (b7 != b8))
197 // return 0xff;
d0047978 198 return (b1 + b3 * 2 + b5 * 4 +b7 * 8);
56b25f8b
PH
199}
200
93cb14d4 201
f12652df 202void mm_drive_cb(uint8_t loco, uint8_t function, uint8_t command)
56b25f8b 203{
56b25f8b 204 uint8_t speed;
56b25f8b 205 speed = get_speed(command);
dc41eb22
PH
206 static uint8_t alert_last = 0;
207
208 switch(loco) {
f12652df 209 case 10:
dc41eb22
PH
210 switch (config.learn_mode) {
211 case LM_OFF:
212 if (speed == 1)
213 config.learn_mode = LM_EASY_WAIT_PRESS;
214 break;
215 case LM_EASY_MODE:
216 if ((speed == 1) && (alert_last == 0)) {
217 config.learn_mode = LM_OFF;
218 save_config();
219 tone_good();
93cb14d4 220 }
dc41eb22 221 break;
56b25f8b 222
dc41eb22
PH
223 case LM_EASY_WAIT_PRESS:
224 if (speed != 1)
225 config.learn_mode = LM_OFF;
226 break;
93cb14d4 227
dc41eb22
PH
228 default:
229 break;
230 }
231 alert_last = (speed == 1);
232 break;
d0047978 233
dc41eb22
PH
234 case 50:
235 if (speed)
d0047978
PH
236 speed = speed - 1;
237
dc41eb22 238 if (config.learn_mode == LM_EASY_MODE) {
93cb14d4 239 config.initial_pulse = speed;
56b25f8b 240 }
dc41eb22
PH
241 break;
242 case 51:
d0047978
PH
243 if (speed)
244 speed = speed - 1;
245
dc41eb22
PH
246 if (config.learn_mode == LM_EASY_MODE)
247 config.on_duty_cycle = speed;
248 break;
56b25f8b
PH
249 }
250}
251
f12652df 252void mm_key_cb(uint8_t decoder, uint8_t function, uint8_t command)
54de37bf 253{
70095677 254 static uint8_t toggle_lock = 0;
f12652df
PH
255
256 /* We don't listen to F1-F4 secret function codes */
257 if (function & 0x3)
258 return;
259
93cb14d4 260 switch(config.learn_mode) {
70095677
PH
261 case LM_OFF:
262 default:
263 if ((decoder == config.decoder_on) &&
264 (command == config.key_on)) { /* Primary key pressed */
265 switch(config.op_mode) {
266 case OM_MOMENTARY:
267 case OM_DOUBLE:
dc41eb22 268 drive_on = 1;
70095677
PH
269 break;
270 case OM_TOGGLE:
271 if (!toggle_lock) {
dc41eb22 272 drive_on = ~drive_on;
70095677
PH
273 toggle_lock = 1;
274 }
275 break;
276 default:
277 break;
278 }
279 }
70095677 280 if ((decoder == config.decoder_on) &&
7c08d02a 281 (command == 0)) { /* Primary key released */
70095677
PH
282 switch(config.op_mode) {
283 case OM_MOMENTARY:
dc41eb22 284 drive_on = 0;
70095677
PH
285 break;
286 case OM_TOGGLE:
287 toggle_lock = 0;
288 break;
289 default:
290 break;
291 }
292 }
dc41eb22
PH
293 break;
294
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;
299 return;
300
301 case LM_EASY_WAIT_UP:
302 if ((decoder == config.decoder_on) &&
303 (command == 0)) {
304 config.learn_mode = LM_EASY_MODE;
305 tone_enter();
306 }
307 return;
f12652df 308
93cb14d4 309#ifdef HANDLE_OFF_KEY
54de37bf 310
70095677
PH
311 if ((decoder == config.decoder_off) &&
312 (command == config.key_off)) { /* Secondary "off" key pressed */
313 switch(config.op_mode) {
314 case OM_DOUBLE:
315 drive_on = 0;
316 break;
317 case OM_TOGGLE:
318 case OM_MOMENTARY:
319 default:
320 break;
70095677
PH
321 }
322 }
93cb14d4 323#endif
70095677 324 break;
f12652df 325
70095677
PH
326 case LM_LEARN_ON_KEY:
327 if (command) {
328 config.decoder_on = decoder;
329 config.key_on = command;
70095677 330 if (config.op_mode == OM_DOUBLE)
93cb14d4 331 config.learn_mode = LM_LEARN_OFF_KEY;
70095677 332 else
93cb14d4
PH
333 config.learn_mode = LM_OFF;
334 save_config();
70095677
PH
335 }
336 break;
93cb14d4 337#ifdef LEARN_ADVANCED
70095677
PH
338 case LM_LEARN_OFF_KEY:
339 if (command) {
340 config.decoder_off = decoder;
341 config.key_off = command;
93cb14d4 342 config.learn_mode = LM_OFF;
70095677 343 save_config();
70095677
PH
344 }
345 break;
f12652df 346
70095677
PH
347 case LM_LEARN_INITIAL:
348 if (drive_on) {
349 if (command == 0)
350 drive_on = 0;
f12652df 351
70095677
PH
352 } else {
353 switch(command) {
354 case 1:
355 if (config.initial_pulse >= 10)
356 config.initial_pulse -= 10;
f12652df 357 else
70095677
PH
358 config.initial_pulse = 0;
359 save_config();
360 drive_on = 1;
361 break;
362 case 2:
363 if (config.initial_pulse <= 245)
364 config.initial_pulse += 10;
365 else
366 config.initial_pulse = 255;
367 save_config();
368 drive_on = 1;
369 break;
370 default:
371 break;
372 }
373 }
374 break;
54de37bf 375
70095677
PH
376 case LM_LEARN_DUTY:
377 if (drive_on) {
378 if (command == 0)
379 drive_on = 0;
380 } else {
381 switch(command) {
382 case 1:
383 if (config.on_duty_cycle > 0)
384 config.on_duty_cycle -= 1;
385 save_config();
386 drive_on = 1;
387 break;
388 case 2:
389 if (config.on_duty_cycle < 10)
390 config.on_duty_cycle += 1;
391 save_config();
392 drive_on = 1;
393 break;
394 default:
395 break;
396 }
397 }
398 break;
f12652df 399
70095677
PH
400 case LM_LEARN_OP_MODE:
401 switch(command) {
402 case 1:
403 config.op_mode = OM_MOMENTARY;
404 save_config();
93cb14d4 405 config.learn_mode = LM_OFF;
70095677
PH
406 break;
407 case 3:
408 config.op_mode = OM_DOUBLE;
409 save_config();
93cb14d4 410 config.learn_mode = LM_OFF;
70095677
PH
411 break;
412 case 5:
413 config.op_mode = OM_TOGGLE;
414 save_config();
93cb14d4 415 config.learn_mode = LM_OFF;
70095677
PH
416 break;
417 default:
418 break;
419 }
420 break;
93cb14d4 421#endif
54de37bf
PH
422 }
423}
424
425
54de37bf
PH
426/******************************************************************************
427 *
428 * main() - The main routine
429 *
430 */
7c08d02a 431void shift(uint8_t mu);
54de37bf 432
f12652df
PH
433#ifdef DEBUG_DRIVE_LED
434#define MON_LED(val) setpin(PIN_LED, val)
435#else
436#define MON_LED(val)
437#endif
438
dc41eb22 439#ifdef WITH_PWM
f12652df
PH
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);}
dc41eb22 443#else
f12652df
PH
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);}
dc41eb22
PH
447#endif
448
54de37bf 449int main(void) {
dc41eb22
PH
450 uint16_t i;
451
452#ifdef WITH_INITIAL_PULSE
70095677
PH
453 uint8_t drive_last = 0;
454 uint8_t drive_slope = 0;
dc41eb22
PH
455#endif
456
dc41eb22
PH
457#ifdef USE_REGISTER_VARS
458 drive_on = 0;
459#endif
7c08d02a 460 mm_init();
70095677 461 load_config();
56b25f8b 462 setup_hw();
dc41eb22 463
f12652df
PH
464 while(0) {
465 setpin(PIN_LED, 1);
466 _delay_ms(2);
467 _delay_ms(2);
468 trigger();
469 sei();
470 }
dc41eb22 471 if (config.magic != EE_MAGIC) {
70095677
PH
472 config.magic = EE_MAGIC;
473 config.op_mode = OM_MOMENTARY;
56b25f8b
PH
474 config.decoder_on = 1;
475 config.key_on = 1;
70095677
PH
476 config.decoder_off = 1;
477 config.key_off = 2;
478 config.initial_pulse = 10;
479 config.on_duty_cycle = 5;
93cb14d4 480 config.learn_mode = LM_LEARN_ON_KEY;
70095677 481 }
54de37bf 482 sei();
54de37bf 483 while (1) {
93cb14d4
PH
484 drive_start:
485
dc41eb22 486#ifdef WITH_INITIAL_PULSE
70095677 487 cli();
dc41eb22 488 if (drive_on && !drive_last)
70095677
PH
489 drive_slope = 1;
490 else
491 drive_slope = 0;
dc41eb22 492 drive_last = drive_on;
70095677 493 sei();
dc41eb22
PH
494#endif
495 if (drive_on) {
56b25f8b 496
dc41eb22 497#ifdef WITH_INITIAL_PULSE
70095677 498 if (drive_slope) {
dc41eb22 499 DRIVE_FULL;
70095677 500 for (i = 0; i < config.initial_pulse; i++) {
dc41eb22 501 _delay_ms(5);
70095677 502 }
af5fd980 503 }
dc41eb22
PH
504#endif
505 DRIVE_ON;
f12652df 506
54de37bf 507 } else {
dc41eb22
PH
508 DRIVE_OFF;
509
510 if (!config.learn_mode ||
511 config.learn_mode > LM_LEARN_OP_MODE)
70095677 512 continue;
54de37bf 513
93cb14d4 514 for (i = 0; i < config.learn_mode; i++) {
70095677 515 setpin(PIN_LED, 1);
dc41eb22 516 snd_on();
93cb14d4 517 _delay_ms(10);
70095677 518 setpin(PIN_LED, 0);
dc41eb22
PH
519 snd_off();
520 if (drive_on) goto drive_start;
93cb14d4 521 _delay_ms(135);
dc41eb22 522 if (drive_on) goto drive_start;
70095677 523 }
93cb14d4 524 for (i = 0; i < 15 - config.learn_mode; i++) {
dc41eb22 525 if (drive_on) goto drive_start;
93cb14d4 526 _delay_ms(70);
70095677
PH
527 }
528 }
54de37bf
PH
529 }
530 return 0;
531}
532
533/******************************************************************************
534 * The end :-)
535 */