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 | ||
f12652df PH |
38 | FUSES = { |
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 |
44 | enum 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 | ||
51 | enum 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 | |
65 | struct 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 | ||
77 | static struct EEMEM config ee_config; | |
93cb14d4 | 78 | static volatile struct config config; |
dc41eb22 PH |
79 | |
80 | static volatile uint8_t easy_mode = 0; | |
81 | static volatile uint8_t easy_mode_possible = 0; | |
82 | ||
83 | #ifndef USE_REGISTER_VARS | |
93cb14d4 | 84 | static 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) | |
103 | void 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 | ||
122 | static 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 | ||
130 | static 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 | ||
140 | static void snd_on(void) | |
141 | { | |
142 | TCCR1 = 0x8; | |
143 | OCR1C = 120; | |
144 | OCR1B = 60; | |
145 | } | |
146 | ||
147 | static 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 | |
162 | static void load_config(void) | |
54de37bf | 163 | { |
93cb14d4 | 164 | eeprom_read_block((uint8_t *)&config, &ee_config, sizeof(config)); |
54de37bf PH |
165 | } |
166 | ||
70095677 | 167 | static 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 | 176 | void 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 |
187 | static 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 | 202 | void 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 | 252 | void 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 | 431 | void 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 | 449 | int 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 | */ |