trennfix/sw: Fixed a bit beauty.
[eisenbahn.git] / trennfix / sw / src / 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
38enum op_mode {
39 OM_MOMENTARY, /* on as long as "key on" pressed */
40 OM_DOUBLE, /* On off with "on" and "off" keys */
41 OM_TOGGLE, /* toggle on "key on" pressed */
42 OM_ERASED = 0xff /* EEPROM erased, need setup */
43};
44
45enum learn_mode {
46 LM_OFF = 0,
dc41eb22
PH
47 LM_LEARN_ON_KEY, /* Learn primary key */
48 LM_LEARN_OFF_KEY, /* Learn secondary key, relevant for OM_DOUBLE */
49 LM_LEARN_INITIAL, /* Learn initial pulse length, 10ms steps */
50 LM_LEARN_DUTY, /* Learn duty cycle 0-10 */
51 LM_LEARN_OP_MODE, /* Learn operation mode */
52 LM_EASY_WAIT_PRESS, /* Wait for key press to enter easy mode */
53 LM_EASY_WAIT_UP, /* Wait for end of key press */
54 LM_EASY_WAIT_TURN, /* Wait for loco 80 turn end */
55 LM_EASY_MODE, /* Easy config mode for PWM and initial kick */
56 LM_END, /* Only a label */
70095677
PH
57};
58
59struct config {
60 uint8_t magic; /* Magic value */
61 enum op_mode op_mode;
62 uint8_t decoder_on;
63 uint8_t key_on;
64 uint8_t decoder_off;
65 uint8_t key_off;
93cb14d4
PH
66 uint8_t initial_pulse; /* Lenghth of initial pulse in 10ms steps */
67 uint8_t on_duty_cycle; /* Duty cycle for on. 0-10 */
68 volatile enum learn_mode learn_mode;
70095677
PH
69};
70
71static struct EEMEM config ee_config;
93cb14d4 72static volatile struct config config;
dc41eb22
PH
73
74static volatile uint8_t easy_mode = 0;
75static volatile uint8_t easy_mode_possible = 0;
76
77#ifndef USE_REGISTER_VARS
93cb14d4 78static volatile uint8_t drive_on = 0;
dc41eb22
PH
79#endif
80
81/******************************************************************************
82 *
83 * Some nice sounds to play on your coil
84 *
85 */
86
87#define G 180
88#define sekunde(g)(quinte((quinte(g)))*2)
89#define terz(g) ((g) * 4 / 5)
90#define kleine_terz(g) ((g) * 5 / 6)
91#define quarte(g) ((g) * 3 / 4)
92#define quinte(g) ((g) * 2 / 3)
93#define tt(g) ((g) * 32 / 45)
94#define septime(g) ((g) * 15 / 8)
95
96#if defined(WITH_SOUND) && defined(WITH_PWM)
97void play_tone(uint8_t divisor, uint8_t duration, uint8_t pause)
98{
99 uint16_t c;
100 TCCR1 = 0x8;
101 OCR1C = divisor;
d0047978
PH
102 c = (divisor * 2) / 3;
103 OCR1B = c;
dc41eb22
PH
104 for (c = 0; c < duration - pause; c++)
105 _delay_ms(2);
d0047978
PH
106 TCCR1 = 0x6;
107 OCR1C = PWM_CYCLE;
108 OCR1B = 12;
dc41eb22
PH
109 for (c = 0; c < pause; c++)
110 _delay_ms(2);
111 TCCR1 = 0x6;
d0047978
PH
112 OCR1C = PWM_CYCLE;
113 OCR1B = PWM_CYCLE;
dc41eb22
PH
114}
115
116static void tone_enter(void)
117{
118 play_tone((G), 70, 20);
119 play_tone(terz(G), 70, 20);
120 play_tone(quinte(G), 70, 20);
121 play_tone(G/2, 100, 0);
122}
123
124static void tone_good(void)
125{
126 play_tone(G, 150, 120);
127 play_tone(terz(G), 100, 70);
128 play_tone(tt(G), 100, 50);
129 play_tone(quarte(terz((G))), 50, 0);
130 play_tone(quinte(G), 150, 0);
131 play_tone(terz(G), 100, 50);
132}
133
134static void snd_on(void)
135{
136 TCCR1 = 0x8;
137 OCR1C = 120;
138 OCR1B = 60;
139}
140
141static void snd_off(void)
142{
143 TCCR1 = 0x6;
d0047978
PH
144 OCR1C = PWM_CYCLE;
145 OCR1B = PWM_CYCLE;
dc41eb22
PH
146}
147
148#else
149#define play_tone(...)
150#define tone_enter(...)
151#define tone_good(...)
152#define snd_on(...)
153#define snd_off(...)
154#endif
70095677
PH
155
156static void load_config(void)
54de37bf 157{
93cb14d4 158 eeprom_read_block((uint8_t *)&config, &ee_config, sizeof(config));
54de37bf
PH
159}
160
70095677 161static void save_config(void)
54de37bf 162{
dc41eb22 163#ifdef WITH_EEPROM_UPDATE
93cb14d4
PH
164 eeprom_update_block((uint8_t *)&config, &ee_config, sizeof(config));
165#else
166 eeprom_write_block((uint8_t *)&config, &ee_config, sizeof(config));
167#endif
54de37bf
PH
168}
169
d0047978
PH
170void mm_switch_pinchange_callback(void)
171{
70095677 172 static uint8_t btn_last = 0;
56b25f8b 173
70095677 174 if (BTN_PRESSED && !btn_last) {
93cb14d4
PH
175 config.learn_mode++;
176 config.learn_mode %= LM_END;
70095677
PH
177 }
178 btn_last = BTN_PRESSED;
54de37bf
PH
179}
180
56b25f8b
PH
181static uint8_t get_speed(uint8_t command)
182{
d0047978 183 uint8_t b1, b3, b5, b7;
93cb14d4 184
56b25f8b 185 b1 = ((command & 0x80) != 0);
56b25f8b 186 b3 = ((command & 0x20) != 0);
56b25f8b 187 b5 = ((command & 0x8) != 0);
56b25f8b 188 b7 = ((command & 0x2) != 0);
56b25f8b
PH
189
190 //if ((b1 != b2) || (b2 != b3) || (b3 != b4) || (b5 != b6) || (b7 != b8))
191 // return 0xff;
d0047978 192 return (b1 + b3 * 2 + b5 * 4 +b7 * 8);
56b25f8b
PH
193}
194
93cb14d4 195
dc41eb22 196void mm_switch_drive(uint8_t loco, uint8_t function, uint8_t command)
56b25f8b 197{
56b25f8b 198 uint8_t speed;
56b25f8b 199 speed = get_speed(command);
dc41eb22
PH
200 static uint8_t alert_last = 0;
201
202 switch(loco) {
203 case 80:
204 switch (config.learn_mode) {
205 case LM_OFF:
206 if (speed == 1)
207 config.learn_mode = LM_EASY_WAIT_PRESS;
208 break;
209 case LM_EASY_MODE:
210 if ((speed == 1) && (alert_last == 0)) {
211 config.learn_mode = LM_OFF;
212 save_config();
213 tone_good();
93cb14d4 214 }
dc41eb22 215 break;
56b25f8b 216
dc41eb22
PH
217 case LM_EASY_WAIT_PRESS:
218 if (speed != 1)
219 config.learn_mode = LM_OFF;
220 break;
93cb14d4 221
dc41eb22
PH
222 default:
223 break;
224 }
225 alert_last = (speed == 1);
226 break;
d0047978 227
dc41eb22
PH
228 case 50:
229 if (speed)
d0047978
PH
230 speed = speed - 1;
231
dc41eb22 232 if (config.learn_mode == LM_EASY_MODE) {
93cb14d4 233 config.initial_pulse = speed;
56b25f8b 234 }
dc41eb22
PH
235 break;
236 case 51:
d0047978
PH
237 if (speed)
238 speed = speed - 1;
239
dc41eb22
PH
240 if (config.learn_mode == LM_EASY_MODE)
241 config.on_duty_cycle = speed;
242 break;
56b25f8b 243 }
dc41eb22 244
56b25f8b
PH
245}
246
70095677 247void mm_switch_command(uint8_t decoder, uint8_t command)
54de37bf 248{
70095677 249 static uint8_t toggle_lock = 0;
dc41eb22 250
93cb14d4 251 switch(config.learn_mode) {
70095677
PH
252
253 case LM_OFF:
254 default:
255 if ((decoder == config.decoder_on) &&
256 (command == config.key_on)) { /* Primary key pressed */
257 switch(config.op_mode) {
258 case OM_MOMENTARY:
259 case OM_DOUBLE:
dc41eb22 260 drive_on = 1;
70095677
PH
261 break;
262 case OM_TOGGLE:
263 if (!toggle_lock) {
dc41eb22 264 drive_on = ~drive_on;
70095677
PH
265 toggle_lock = 1;
266 }
267 break;
268 default:
269 break;
270 }
271 }
70095677 272 if ((decoder == config.decoder_on) &&
7c08d02a 273 (command == 0)) { /* Primary key released */
70095677
PH
274 switch(config.op_mode) {
275 case OM_MOMENTARY:
dc41eb22 276 drive_on = 0;
70095677
PH
277 break;
278 case OM_TOGGLE:
279 toggle_lock = 0;
280 break;
281 default:
282 break;
283 }
284 }
dc41eb22
PH
285 break;
286
287 case LM_EASY_WAIT_PRESS:
288 if ((decoder == config.decoder_on) &&
289 (command == config.key_on))
290 config.learn_mode = LM_EASY_WAIT_UP;
291 return;
292
293 case LM_EASY_WAIT_UP:
294 if ((decoder == config.decoder_on) &&
295 (command == 0)) {
296 config.learn_mode = LM_EASY_MODE;
297 tone_enter();
298 }
299 return;
300
93cb14d4 301#ifdef HANDLE_OFF_KEY
54de37bf 302
70095677
PH
303 if ((decoder == config.decoder_off) &&
304 (command == config.key_off)) { /* Secondary "off" key pressed */
305 switch(config.op_mode) {
306 case OM_DOUBLE:
307 drive_on = 0;
308 break;
309 case OM_TOGGLE:
310 case OM_MOMENTARY:
311 default:
312 break;
70095677
PH
313 }
314 }
93cb14d4 315#endif
70095677
PH
316 break;
317
318 case LM_LEARN_ON_KEY:
319 if (command) {
320 config.decoder_on = decoder;
321 config.key_on = command;
70095677 322 if (config.op_mode == OM_DOUBLE)
93cb14d4 323 config.learn_mode = LM_LEARN_OFF_KEY;
70095677 324 else
93cb14d4
PH
325 config.learn_mode = LM_OFF;
326 save_config();
70095677
PH
327 }
328 break;
93cb14d4 329#ifdef LEARN_ADVANCED
70095677
PH
330 case LM_LEARN_OFF_KEY:
331 if (command) {
332 config.decoder_off = decoder;
333 config.key_off = command;
93cb14d4 334 config.learn_mode = LM_OFF;
70095677 335 save_config();
70095677
PH
336 }
337 break;
338
339 case LM_LEARN_INITIAL:
340 if (drive_on) {
341 if (command == 0)
342 drive_on = 0;
343
344 } else {
345 switch(command) {
346 case 1:
347 if (config.initial_pulse >= 10)
348 config.initial_pulse -= 10;
349 else
350 config.initial_pulse = 0;
351 save_config();
352 drive_on = 1;
353 break;
354 case 2:
355 if (config.initial_pulse <= 245)
356 config.initial_pulse += 10;
357 else
358 config.initial_pulse = 255;
359 save_config();
360 drive_on = 1;
361 break;
362 default:
363 break;
364 }
365 }
366 break;
54de37bf 367
70095677
PH
368 case LM_LEARN_DUTY:
369 if (drive_on) {
370 if (command == 0)
371 drive_on = 0;
372 } else {
373 switch(command) {
374 case 1:
375 if (config.on_duty_cycle > 0)
376 config.on_duty_cycle -= 1;
377 save_config();
378 drive_on = 1;
379 break;
380 case 2:
381 if (config.on_duty_cycle < 10)
382 config.on_duty_cycle += 1;
383 save_config();
384 drive_on = 1;
385 break;
386 default:
387 break;
388 }
389 }
390 break;
391
392 case LM_LEARN_OP_MODE:
393 switch(command) {
394 case 1:
395 config.op_mode = OM_MOMENTARY;
396 save_config();
93cb14d4 397 config.learn_mode = LM_OFF;
70095677
PH
398 break;
399 case 3:
400 config.op_mode = OM_DOUBLE;
401 save_config();
93cb14d4 402 config.learn_mode = LM_OFF;
70095677
PH
403 break;
404 case 5:
405 config.op_mode = OM_TOGGLE;
406 save_config();
93cb14d4 407 config.learn_mode = LM_OFF;
70095677
PH
408 break;
409 default:
410 break;
411 }
412 break;
93cb14d4 413#endif
54de37bf
PH
414 }
415}
416
417
54de37bf
PH
418/******************************************************************************
419 *
420 * main() - The main routine
421 *
422 */
7c08d02a 423void shift(uint8_t mu);
54de37bf 424
dc41eb22 425#ifdef WITH_PWM
d0047978
PH
426#define DRIVE_OFF {OCR1B = PWM_CYCLE;}
427#define DRIVE_ON {OCR1B = PWM_CYCLE - config.on_duty_cycle;}
dc41eb22
PH
428#define DRIVE_FULL {OCR1B = 0;}
429#else
27b551dd 430#define DRIVE_OFF {setpin(PIN_DRIVE, 0); setpin(PIN_LED, 0);}
dc41eb22 431#define DRIVE_ON {setpin(PIN_DRIVE, 1); setpin(PIN_LED, 1);}
27b551dd 432#define DRIVE_FULL {setpin(PIN_DRIVE, 1); setpin(PIN_LED, 1);}
dc41eb22
PH
433#endif
434
54de37bf 435int main(void) {
dc41eb22
PH
436 uint16_t i;
437
438#ifdef WITH_INITIAL_PULSE
70095677
PH
439 uint8_t drive_last = 0;
440 uint8_t drive_slope = 0;
dc41eb22
PH
441#endif
442
dc41eb22
PH
443#ifdef USE_REGISTER_VARS
444 drive_on = 0;
445#endif
7c08d02a 446 mm_init();
70095677 447 load_config();
56b25f8b 448 setup_hw();
dc41eb22
PH
449
450 if (config.magic != EE_MAGIC) {
70095677
PH
451 config.magic = EE_MAGIC;
452 config.op_mode = OM_MOMENTARY;
56b25f8b
PH
453 config.decoder_on = 1;
454 config.key_on = 1;
70095677
PH
455 config.decoder_off = 1;
456 config.key_off = 2;
457 config.initial_pulse = 10;
458 config.on_duty_cycle = 5;
93cb14d4 459 config.learn_mode = LM_LEARN_ON_KEY;
70095677 460 }
54de37bf 461 sei();
54de37bf 462 while (1) {
93cb14d4
PH
463 drive_start:
464
dc41eb22 465#ifdef WITH_INITIAL_PULSE
70095677 466 cli();
dc41eb22 467 if (drive_on && !drive_last)
70095677
PH
468 drive_slope = 1;
469 else
470 drive_slope = 0;
dc41eb22 471 drive_last = drive_on;
70095677 472 sei();
dc41eb22
PH
473#endif
474 if (drive_on) {
56b25f8b 475
dc41eb22 476#ifdef WITH_INITIAL_PULSE
70095677 477 if (drive_slope) {
dc41eb22 478 DRIVE_FULL;
70095677 479 for (i = 0; i < config.initial_pulse; i++) {
dc41eb22 480 _delay_ms(5);
70095677 481 }
af5fd980 482 }
dc41eb22
PH
483#endif
484 DRIVE_ON;
485
54de37bf 486 } else {
dc41eb22
PH
487 DRIVE_OFF;
488
489 if (!config.learn_mode ||
490 config.learn_mode > LM_LEARN_OP_MODE)
70095677 491 continue;
54de37bf 492
93cb14d4 493 for (i = 0; i < config.learn_mode; i++) {
70095677 494 setpin(PIN_LED, 1);
dc41eb22 495 snd_on();
93cb14d4 496 _delay_ms(10);
70095677 497 setpin(PIN_LED, 0);
dc41eb22
PH
498 snd_off();
499 if (drive_on) goto drive_start;
93cb14d4 500 _delay_ms(135);
dc41eb22 501 if (drive_on) goto drive_start;
70095677 502 }
93cb14d4 503 for (i = 0; i < 15 - config.learn_mode; i++) {
dc41eb22 504 if (drive_on) goto drive_start;
93cb14d4 505 _delay_ms(70);
70095677
PH
506 }
507 }
54de37bf
PH
508 }
509 return 0;
510}
511
512/******************************************************************************
513 * The end :-)
514 */