trennfix/sw: Added smokefix, further modularized the mm decoder
[eisenbahn.git] / trennfix / sw / src / smokefix_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 33#include <mm/mm_switch.h>
f12652df 34#include <mm/mm_decode.h>
93cb14d4 35#include <config/hardware.h>
54de37bf 36
f12652df
PH
37uint8_t eeFooByte EEMEM = 123;
38
70095677
PH
39#define EE_MAGIC 0xab
40
f12652df
PH
41FUSES = {
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,
46};
47
48void mm_key_cb(uint8_t decoder, uint8_t function, uint8_t key);
49void mm_drive_cb(uint8_t decoder, uint8_t function, uint8_t command);
50
70095677
PH
51enum op_mode {
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 */
56};
57
58enum learn_mode {
59 LM_OFF = 0,
dc41eb22
PH
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 */
f12652df 70};
70095677
PH
71
72struct config {
73 uint8_t magic; /* Magic value */
74 enum op_mode op_mode;
75 uint8_t decoder_on;
76 uint8_t key_on;
77 uint8_t decoder_off;
78 uint8_t key_off;
93cb14d4 79 uint8_t initial_pulse; /* Lenghth of initial pulse in 10ms steps */
f12652df 80 uint8_t on_duty_cycle[15]; /* Duty cycle for on. 0-10 */
93cb14d4 81 volatile enum learn_mode learn_mode;
70095677
PH
82};
83
f12652df 84static uint8_t main_speed = 0;
70095677 85static struct EEMEM config ee_config;
93cb14d4 86static volatile struct config config;
dc41eb22
PH
87
88static volatile uint8_t easy_mode = 0;
89static volatile uint8_t easy_mode_possible = 0;
90
91#ifndef USE_REGISTER_VARS
93cb14d4 92static volatile uint8_t drive_on = 0;
dc41eb22
PH
93#endif
94
95/******************************************************************************
96 *
97 * Some nice sounds to play on your coil
98 *
99 */
100
101#define G 180
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)
109
110#if defined(WITH_SOUND) && defined(WITH_PWM)
111void play_tone(uint8_t divisor, uint8_t duration, uint8_t pause)
112{
113 uint16_t c;
114 TCCR1 = 0x8;
115 OCR1C = divisor;
d0047978
PH
116 c = (divisor * 2) / 3;
117 OCR1B = c;
dc41eb22
PH
118 for (c = 0; c < duration - pause; c++)
119 _delay_ms(2);
d0047978
PH
120 TCCR1 = 0x6;
121 OCR1C = PWM_CYCLE;
122 OCR1B = 12;
dc41eb22
PH
123 for (c = 0; c < pause; c++)
124 _delay_ms(2);
125 TCCR1 = 0x6;
d0047978
PH
126 OCR1C = PWM_CYCLE;
127 OCR1B = PWM_CYCLE;
dc41eb22
PH
128}
129
130static void tone_enter(void)
131{
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);
136}
137
138static void tone_good(void)
139{
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);
f12652df 145 play_tone(terz(G), 100, 50);
dc41eb22
PH
146}
147
148static void snd_on(void)
149{
150 TCCR1 = 0x8;
151 OCR1C = 120;
152 OCR1B = 60;
153}
154
155static void snd_off(void)
156{
157 TCCR1 = 0x6;
d0047978
PH
158 OCR1C = PWM_CYCLE;
159 OCR1B = PWM_CYCLE;
dc41eb22
PH
160}
161
162#else
163#define play_tone(...)
f12652df 164#define tone_enter(...) {setpin(PIN_LED, 1); _delay_ms(500); setpin(PIN_LED, 0);}
dc41eb22
PH
165#define tone_good(...)
166#define snd_on(...)
167#define snd_off(...)
168#endif
70095677
PH
169
170static void load_config(void)
54de37bf 171{
93cb14d4 172 eeprom_read_block((uint8_t *)&config, &ee_config, sizeof(config));
54de37bf
PH
173}
174
70095677 175static void save_config(void)
54de37bf 176{
dc41eb22 177#ifdef WITH_EEPROM_UPDATE
93cb14d4
PH
178 eeprom_update_block((uint8_t *)&config, &ee_config, sizeof(config));
179#else
180 eeprom_write_block((uint8_t *)&config, &ee_config, sizeof(config));
181#endif
54de37bf
PH
182}
183
f12652df 184void mm_pinchange_callback(void)
d0047978 185{
70095677 186 static uint8_t btn_last = 0;
56b25f8b 187
70095677 188 if (BTN_PRESSED && !btn_last) {
93cb14d4
PH
189 config.learn_mode++;
190 config.learn_mode %= LM_END;
70095677
PH
191 }
192 btn_last = BTN_PRESSED;
54de37bf
PH
193}
194
56b25f8b
PH
195static uint8_t get_speed(uint8_t command)
196{
f12652df
PH
197 uint8_t b0, b2, b4, b6;
198
199 b0 = ((command & 0x80) != 0);
200 b2 = ((command & 0x20) != 0);
201 b4 = ((command & 0x8) != 0);
202 b6 = ((command & 0x2) != 0);
203
204 //if ((b9!= b2) || (b2 != b3) || (b3 != b4) || (b5 != b6) || (b7 != b8))
56b25f8b 205 // return 0xff;
f12652df
PH
206 return (b0+ b2 * 2 + b4 * 4 +b6 * 8);
207}
208
209enum mm2_command {
210 CF_MM1 ,
211 CF_REVERSE,
212 CF_FORWARD,
213 CF_F1_ON,
214 CF_F1_OFF,
215 CF_F2_ON,
216 CF_F2_OFF,
217 CF_F3_ON,
218 CF_F3_OFF,
219 CF_F4_ON,
220 CF_F4_OFF,
221};
222
223#ifdef MM_USE_QUEUE
224static void dequeue_commands(void)
225{
226
227 return;
228
229 struct mm_command *cmd = mm_get();
230 if (!cmd)
231 return;
232 if (cmd->recmode != MM_FAST)
233 return;
234 if ((cmd->function & 0x3) != 0x3)
235 return;
236 // uint8_t loco = mm_lookup_decoder(cmd->address);
237
56b25f8b
PH
238}
239
f12652df 240#endif
93cb14d4 241
f12652df 242void mm_drive_cb(uint8_t loco, uint8_t func_enc, uint8_t cmd_enc)
56b25f8b 243{
f12652df
PH
244 uint8_t b1, b3, b5, b7;
245 uint8_t fcode;
246
247 /* Those three are decoded from fun and command */
56b25f8b 248 uint8_t speed;
f12652df
PH
249 uint8_t function;
250 enum mm2_command mm2_command = CF_MM1;
dc41eb22 251 static uint8_t alert_last = 0;
f12652df
PH
252 speed = get_speed(cmd_enc);
253
254#ifdef MUELL
255 goto mm2_done; // FIXME
256
257 b1 = ((cmd_enc & 0x4) != 0);
258 b3 = ((cmd_enc & 0x1) != 0);
259 b5 = ((cmd_enc & 0x4) != 0);
260 b7 = ((cmd_enc & 0x1) != 0);
261
262 fcode = b1 * 8 + b3 * 4 + b5 *2 + b7;
263
264 /* The ugly speed section */
265 if (speed < 8) {
266 if (fcode == 0xb) {
267 mm2_command = CF_REVERSE;
268 goto mm2_done;
269 }
270 if (fcode == 0x5) {
271 mm2_command = CF_FORWARD;
272 goto mm2_done;
273 }
274 } else {
275 if (fcode == 0xa) {
276 mm2_command = CF_REVERSE;
277 goto mm2_done;
278 }
279 if (fcode == 0x4) {
280 mm2_command = CF_FORWARD;
281 goto mm2_done;
282 }
283 }
284
285 /* Special cases for f1-f4 commands */
286 if (fcode == 0xa) {
287 switch(speed) {
288 case 3:
289 mm2_command = CF_F1_ON;
290 goto mm2_done;
291 case 4:
292 mm2_command = CF_F2_ON;
293 goto mm2_done;
294 case 6:
295 mm2_command = CF_F3_ON;
296 goto mm2_done;
297 case 7:
298 mm2_command = CF_F4_ON;
299 goto mm2_done;
300 default:
301 goto mm2_done;
302 }
303 } else if (fcode == 0x5) {
304 switch(speed) {
305 case 11:
306 mm2_command = CF_F1_OFF;
307 goto mm2_done;
308 case 12:
309 mm2_command = CF_F2_OFF;
310 goto mm2_done;
311 case 14:
312 mm2_command = CF_F3_OFF;
313 goto mm2_done;
314 case 15:
315 mm2_command = CF_F4_OFF;
316 goto mm2_done;
317 }
318 }
319
320 switch(fcode) {
321 case 0xc:
322 mm2_command = CF_F1_OFF;
323 goto mm2_done;
324 case 0xd:
325 mm2_command = CF_F1_ON;
326 goto mm2_done;
327
328 case 0x2:
329 mm2_command = CF_F2_OFF;
330 goto mm2_done;
331 case 0x3:
332 mm2_command = CF_F2_ON;
333 goto mm2_done;
334
335 case 0x6:
336 mm2_command = CF_F3_OFF;
337 goto mm2_done;
338
339 case 0x7:
340 mm2_command = CF_F3_ON;
341 goto mm2_done;
342
343 case 0xe:
344 mm2_command = CF_F4_OFF;
345 goto mm2_done;
346
347 case 0xf:
348 mm2_command = CF_F4_ON;
349 goto mm2_done;
350 default:
351 break;
352 }
353 mm2_done:
354 if (loco == 34) {
355 switch(mm2_command) {
356 case CF_F3_ON:
357 drive_on = 1;
358 break;
359 case CF_F3_OFF:
360 drive_on = 0;
361 break;
362 default:
363 break;
364 }
365 }
366#endif
367 if (loco == config.decoder_on) {
368 if (speed)
369 speed = speed - 1;
370 main_speed = speed;
371 return;
372 }
dc41eb22
PH
373
374 switch(loco) {
375 case 80:
376 switch (config.learn_mode) {
377 case LM_OFF:
378 if (speed == 1)
379 config.learn_mode = LM_EASY_WAIT_PRESS;
380 break;
381 case LM_EASY_MODE:
382 if ((speed == 1) && (alert_last == 0)) {
383 config.learn_mode = LM_OFF;
384 save_config();
385 tone_good();
93cb14d4 386 }
dc41eb22 387 break;
56b25f8b 388
dc41eb22
PH
389 case LM_EASY_WAIT_PRESS:
390 if (speed != 1)
391 config.learn_mode = LM_OFF;
392 break;
93cb14d4 393
dc41eb22
PH
394 default:
395 break;
396 }
397 alert_last = (speed == 1);
398 break;
d0047978 399
dc41eb22
PH
400 case 50:
401 if (speed)
d0047978
PH
402 speed = speed - 1;
403
dc41eb22 404 if (config.learn_mode == LM_EASY_MODE) {
93cb14d4 405 config.initial_pulse = speed;
56b25f8b 406 }
dc41eb22
PH
407 break;
408 case 51:
d0047978
PH
409 if (speed)
410 speed = speed - 1;
411
dc41eb22 412 if (config.learn_mode == LM_EASY_MODE)
f12652df 413 config.on_duty_cycle[main_speed] = speed;
dc41eb22 414 break;
56b25f8b
PH
415 }
416}
417
f12652df 418void mm_key_cb(uint8_t decoder, uint8_t function, uint8_t raw_command)
54de37bf 419{
70095677 420 static uint8_t toggle_lock = 0;
f12652df
PH
421 uint8_t command;
422
423 static uint8_t last_raw = 0;
424 static uint8_t last_fkey = 0;
425
426 if ((function & 3) == 3) { /* F key hack! */
427 uint8_t diff;
428 diff = last_raw ^ raw_command;
429 if (diff) {
430 if (diff & 0x80)
431 command = 4;
432 else if (diff & 0x20)
433 command = 3;
434 else if (diff & 0x8)
435 command = 2;
436 else
437 command = 1;
438 command |= 0x80;
439 if ((command == config.key_on)
440 && (last_raw & ~raw_command))
441 command = 0;
442 last_raw = raw_command;
443 } else {
444 return;
445 }
446 } else {
447 command = mm_lookup_key(raw_command);
448 }
dc41eb22 449
93cb14d4 450 switch(config.learn_mode) {
70095677
PH
451 case LM_OFF:
452 default:
453 if ((decoder == config.decoder_on) &&
454 (command == config.key_on)) { /* Primary key pressed */
455 switch(config.op_mode) {
456 case OM_MOMENTARY:
457 case OM_DOUBLE:
dc41eb22 458 drive_on = 1;
70095677
PH
459 break;
460 case OM_TOGGLE:
461 if (!toggle_lock) {
dc41eb22 462 drive_on = ~drive_on;
70095677
PH
463 toggle_lock = 1;
464 }
465 break;
466 default:
467 break;
468 }
469 }
70095677 470 if ((decoder == config.decoder_on) &&
7c08d02a 471 (command == 0)) { /* Primary key released */
70095677
PH
472 switch(config.op_mode) {
473 case OM_MOMENTARY:
dc41eb22 474 drive_on = 0;
70095677
PH
475 break;
476 case OM_TOGGLE:
477 toggle_lock = 0;
478 break;
479 default:
480 break;
481 }
482 }
dc41eb22
PH
483 break;
484
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;
489 return;
490
491 case LM_EASY_WAIT_UP:
492 if ((decoder == config.decoder_on) &&
493 (command == 0)) {
494 config.learn_mode = LM_EASY_MODE;
495 tone_enter();
496 }
497 return;
f12652df 498
93cb14d4 499#ifdef HANDLE_OFF_KEY
54de37bf 500
70095677
PH
501 if ((decoder == config.decoder_off) &&
502 (command == config.key_off)) { /* Secondary "off" key pressed */
503 switch(config.op_mode) {
504 case OM_DOUBLE:
505 drive_on = 0;
506 break;
507 case OM_TOGGLE:
508 case OM_MOMENTARY:
509 default:
510 break;
70095677
PH
511 }
512 }
93cb14d4 513#endif
70095677 514 break;
f12652df 515
70095677
PH
516 case LM_LEARN_ON_KEY:
517 if (command) {
518 config.decoder_on = decoder;
519 config.key_on = command;
70095677 520 if (config.op_mode == OM_DOUBLE)
93cb14d4 521 config.learn_mode = LM_LEARN_OFF_KEY;
70095677 522 else
93cb14d4
PH
523 config.learn_mode = LM_OFF;
524 save_config();
70095677
PH
525 }
526 break;
93cb14d4 527#ifdef LEARN_ADVANCED
70095677
PH
528 case LM_LEARN_OFF_KEY:
529 if (command) {
530 config.decoder_off = decoder;
531 config.key_off = command;
93cb14d4 532 config.learn_mode = LM_OFF;
70095677 533 save_config();
70095677
PH
534 }
535 break;
f12652df 536
70095677
PH
537 case LM_LEARN_INITIAL:
538 if (drive_on) {
539 if (command == 0)
540 drive_on = 0;
f12652df 541
70095677
PH
542 } else {
543 switch(command) {
544 case 1:
545 if (config.initial_pulse >= 10)
546 config.initial_pulse -= 10;
f12652df 547 else
70095677
PH
548 config.initial_pulse = 0;
549 save_config();
550 drive_on = 1;
551 break;
552 case 2:
553 if (config.initial_pulse <= 245)
554 config.initial_pulse += 10;
555 else
556 config.initial_pulse = 255;
557 save_config();
558 drive_on = 1;
559 break;
560 default:
561 break;
562 }
563 }
564 break;
54de37bf 565
70095677
PH
566 case LM_LEARN_DUTY:
567 if (drive_on) {
568 if (command == 0)
569 drive_on = 0;
570 } else {
571 switch(command) {
572 case 1:
573 if (config.on_duty_cycle > 0)
574 config.on_duty_cycle -= 1;
575 save_config();
576 drive_on = 1;
577 break;
578 case 2:
579 if (config.on_duty_cycle < 10)
580 config.on_duty_cycle += 1;
581 save_config();
582 drive_on = 1;
583 break;
584 default:
585 break;
586 }
587 }
588 break;
f12652df 589
70095677
PH
590 case LM_LEARN_OP_MODE:
591 switch(command) {
592 case 1:
593 config.op_mode = OM_MOMENTARY;
594 save_config();
93cb14d4 595 config.learn_mode = LM_OFF;
70095677
PH
596 break;
597 case 3:
598 config.op_mode = OM_DOUBLE;
599 save_config();
93cb14d4 600 config.learn_mode = LM_OFF;
70095677
PH
601 break;
602 case 5:
603 config.op_mode = OM_TOGGLE;
604 save_config();
93cb14d4 605 config.learn_mode = LM_OFF;
70095677
PH
606 break;
607 default:
608 break;
609 }
610 break;
93cb14d4 611#endif
54de37bf
PH
612 }
613}
614
615
54de37bf
PH
616/******************************************************************************
617 *
618 * main() - The main routine
619 *
620 */
7c08d02a 621void shift(uint8_t mu);
54de37bf 622
f12652df
PH
623#ifdef DEBUG_DRIVE_LED
624#define MON_LED(val) setpin(PIN_LED, val)
625#else
626#define MON_LED(val)
627#endif
628
dc41eb22 629#ifdef WITH_PWM
f12652df
PH
630#define DRIVE_OFF {OCR1B = PWM_CYCLE; MON_LED(0);}
631#define DRIVE_ON {OCR1B = PWM_CYCLE - config.on_duty_cycle[main_speed]; \
632 MON_LED(1);}
633#define DRIVE_FULL {OCR1B = 0; MON_LED(1);}
dc41eb22 634#else
f12652df
PH
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);}
dc41eb22
PH
638#endif
639
54de37bf 640int main(void) {
dc41eb22
PH
641 uint16_t i;
642
643#ifdef WITH_INITIAL_PULSE
70095677
PH
644 uint8_t drive_last = 0;
645 uint8_t drive_slope = 0;
dc41eb22
PH
646#endif
647
dc41eb22
PH
648#ifdef USE_REGISTER_VARS
649 drive_on = 0;
650#endif
7c08d02a 651 mm_init();
70095677 652 load_config();
56b25f8b 653 setup_hw();
dc41eb22 654
f12652df
PH
655 while(0) {
656 setpin(PIN_LED, 1);
657 _delay_ms(2);
658 _delay_ms(2);
659 trigger();
660 sei();
661 }
dc41eb22 662 if (config.magic != EE_MAGIC) {
70095677
PH
663 config.magic = EE_MAGIC;
664 config.op_mode = OM_MOMENTARY;
56b25f8b
PH
665 config.decoder_on = 1;
666 config.key_on = 1;
70095677
PH
667 config.decoder_off = 1;
668 config.key_off = 2;
669 config.initial_pulse = 10;
f12652df 670 config.on_duty_cycle[0] = 5;
93cb14d4 671 config.learn_mode = LM_LEARN_ON_KEY;
70095677 672 }
54de37bf 673 sei();
54de37bf 674 while (1) {
f12652df
PH
675#ifdef MM_USE_QUEUE
676 dequeue_commands();
677#endif
93cb14d4
PH
678 drive_start:
679
dc41eb22 680#ifdef WITH_INITIAL_PULSE
70095677 681 cli();
dc41eb22 682 if (drive_on && !drive_last)
70095677
PH
683 drive_slope = 1;
684 else
685 drive_slope = 0;
dc41eb22 686 drive_last = drive_on;
70095677 687 sei();
dc41eb22
PH
688#endif
689 if (drive_on) {
56b25f8b 690
dc41eb22 691#ifdef WITH_INITIAL_PULSE
70095677 692 if (drive_slope) {
dc41eb22 693 DRIVE_FULL;
70095677 694 for (i = 0; i < config.initial_pulse; i++) {
dc41eb22 695 _delay_ms(5);
70095677 696 }
af5fd980 697 }
dc41eb22
PH
698#endif
699 DRIVE_ON;
f12652df 700
54de37bf 701 } else {
dc41eb22
PH
702 DRIVE_OFF;
703
704 if (!config.learn_mode ||
705 config.learn_mode > LM_LEARN_OP_MODE)
70095677 706 continue;
54de37bf 707
93cb14d4 708 for (i = 0; i < config.learn_mode; i++) {
70095677 709 setpin(PIN_LED, 1);
dc41eb22 710 snd_on();
93cb14d4 711 _delay_ms(10);
70095677 712 setpin(PIN_LED, 0);
dc41eb22
PH
713 snd_off();
714 if (drive_on) goto drive_start;
93cb14d4 715 _delay_ms(135);
dc41eb22 716 if (drive_on) goto drive_start;
70095677 717 }
93cb14d4 718 for (i = 0; i < 15 - config.learn_mode; i++) {
dc41eb22 719 if (drive_on) goto drive_start;
93cb14d4 720 _delay_ms(70);
70095677
PH
721 }
722 }
f12652df
PH
723 // MCUCR |= _BV(SE);
724 // sleep();
54de37bf
PH
725 }
726 return 0;
727}
728
729/******************************************************************************
730 * The end :-)
731 */