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