Commit | Line | Data |
---|---|---|
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 | ||
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, | |
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 | ||
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; | |
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 | ||
71 | static struct EEMEM config ee_config; | |
93cb14d4 | 72 | static volatile struct config config; |
dc41eb22 PH |
73 | |
74 | static volatile uint8_t easy_mode = 0; | |
75 | static volatile uint8_t easy_mode_possible = 0; | |
76 | ||
77 | #ifndef USE_REGISTER_VARS | |
93cb14d4 | 78 | static 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) | |
97 | void 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 | ||
116 | static 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 | ||
124 | static 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 | ||
134 | static void snd_on(void) | |
135 | { | |
136 | TCCR1 = 0x8; | |
137 | OCR1C = 120; | |
138 | OCR1B = 60; | |
139 | } | |
140 | ||
141 | static 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 | |
156 | static void load_config(void) | |
54de37bf | 157 | { |
93cb14d4 | 158 | eeprom_read_block((uint8_t *)&config, &ee_config, sizeof(config)); |
54de37bf PH |
159 | } |
160 | ||
70095677 | 161 | static 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 |
170 | void 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 |
181 | static 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 | 196 | void 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 | 247 | void 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 | 423 | void 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 | |
430 | #define DRIVE_OFF {setpin(PIN_DRIVE, 0);} | |
431 | #define DRIVE_ON {setpin(PIN_DRIVE, 1); setpin(PIN_LED, 1);} | |
432 | #define DRIVE_FULL {setpin(PIN_DRIVE, 1); setpin(PIN_LED, 0);} | |
433 | #endif | |
434 | ||
54de37bf | 435 | int 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 | */ |