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; | |
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 | |
70095677 PH |
153 | |
154 | static void load_config(void) | |
54de37bf | 155 | { |
93cb14d4 | 156 | eeprom_read_block((uint8_t *)&config, &ee_config, sizeof(config)); |
54de37bf PH |
157 | } |
158 | ||
70095677 | 159 | static void save_config(void) |
54de37bf | 160 | { |
dc41eb22 | 161 | #ifdef WITH_EEPROM_UPDATE |
93cb14d4 PH |
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 | |
54de37bf PH |
166 | } |
167 | ||
70095677 PH |
168 | ISR(PCINT0_vect){ |
169 | static uint8_t btn_last = 0; | |
56b25f8b | 170 | |
70095677 | 171 | mm_pinchange_handler(); |
54de37bf | 172 | |
70095677 | 173 | if (BTN_PRESSED && !btn_last) { |
93cb14d4 PH |
174 | config.learn_mode++; |
175 | config.learn_mode %= LM_END; | |
70095677 PH |
176 | } |
177 | btn_last = BTN_PRESSED; | |
54de37bf PH |
178 | } |
179 | ||
56b25f8b PH |
180 | static uint8_t get_speed(uint8_t command) |
181 | { | |
93cb14d4 PH |
182 | uint8_t b1, b3, b5, b7; |
183 | ||
56b25f8b | 184 | b1 = ((command & 0x80) != 0); |
56b25f8b | 185 | b3 = ((command & 0x20) != 0); |
56b25f8b | 186 | b5 = ((command & 0x8) != 0); |
56b25f8b | 187 | b7 = ((command & 0x2) != 0); |
56b25f8b PH |
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 | ||
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; | |
227 | case 50: | |
228 | if (speed) | |
229 | speed -= 1; | |
230 | ||
231 | if (config.learn_mode == LM_EASY_MODE) { | |
93cb14d4 | 232 | config.initial_pulse = speed; |
56b25f8b | 233 | } |
dc41eb22 PH |
234 | break; |
235 | case 51: | |
236 | if (config.learn_mode == LM_EASY_MODE) | |
237 | config.on_duty_cycle = speed; | |
238 | break; | |
56b25f8b | 239 | } |
dc41eb22 | 240 | |
56b25f8b PH |
241 | } |
242 | ||
70095677 | 243 | void mm_switch_command(uint8_t decoder, uint8_t command) |
54de37bf | 244 | { |
70095677 | 245 | static uint8_t toggle_lock = 0; |
dc41eb22 | 246 | |
93cb14d4 | 247 | switch(config.learn_mode) { |
70095677 PH |
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: | |
dc41eb22 | 256 | drive_on = 1; |
70095677 PH |
257 | break; |
258 | case OM_TOGGLE: | |
259 | if (!toggle_lock) { | |
dc41eb22 | 260 | drive_on = ~drive_on; |
70095677 PH |
261 | toggle_lock = 1; |
262 | } | |
263 | break; | |
264 | default: | |
265 | break; | |
266 | } | |
267 | } | |
70095677 | 268 | if ((decoder == config.decoder_on) && |
7c08d02a | 269 | (command == 0)) { /* Primary key released */ |
70095677 PH |
270 | switch(config.op_mode) { |
271 | case OM_MOMENTARY: | |
dc41eb22 | 272 | drive_on = 0; |
70095677 PH |
273 | break; |
274 | case OM_TOGGLE: | |
275 | toggle_lock = 0; | |
276 | break; | |
277 | default: | |
278 | break; | |
279 | } | |
280 | } | |
dc41eb22 PH |
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 | ||
93cb14d4 | 297 | #ifdef HANDLE_OFF_KEY |
54de37bf | 298 | |
70095677 PH |
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; | |
70095677 PH |
309 | } |
310 | } | |
93cb14d4 | 311 | #endif |
70095677 PH |
312 | break; |
313 | ||
314 | case LM_LEARN_ON_KEY: | |
315 | if (command) { | |
316 | config.decoder_on = decoder; | |
317 | config.key_on = command; | |
70095677 | 318 | if (config.op_mode == OM_DOUBLE) |
93cb14d4 | 319 | config.learn_mode = LM_LEARN_OFF_KEY; |
70095677 | 320 | else |
93cb14d4 PH |
321 | config.learn_mode = LM_OFF; |
322 | save_config(); | |
70095677 PH |
323 | } |
324 | break; | |
93cb14d4 | 325 | #ifdef LEARN_ADVANCED |
70095677 PH |
326 | case LM_LEARN_OFF_KEY: |
327 | if (command) { | |
328 | config.decoder_off = decoder; | |
329 | config.key_off = command; | |
93cb14d4 | 330 | config.learn_mode = LM_OFF; |
70095677 | 331 | save_config(); |
70095677 PH |
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; | |
54de37bf | 363 | |
70095677 PH |
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(); | |
93cb14d4 | 393 | config.learn_mode = LM_OFF; |
70095677 PH |
394 | break; |
395 | case 3: | |
396 | config.op_mode = OM_DOUBLE; | |
397 | save_config(); | |
93cb14d4 | 398 | config.learn_mode = LM_OFF; |
70095677 PH |
399 | break; |
400 | case 5: | |
401 | config.op_mode = OM_TOGGLE; | |
402 | save_config(); | |
93cb14d4 | 403 | config.learn_mode = LM_OFF; |
70095677 PH |
404 | break; |
405 | default: | |
406 | break; | |
407 | } | |
408 | break; | |
93cb14d4 | 409 | #endif |
54de37bf PH |
410 | } |
411 | } | |
412 | ||
413 | ||
54de37bf PH |
414 | /****************************************************************************** |
415 | * | |
416 | * main() - The main routine | |
417 | * | |
418 | */ | |
7c08d02a | 419 | void shift(uint8_t mu); |
54de37bf | 420 | |
dc41eb22 PH |
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 | ||
54de37bf | 431 | int main(void) { |
dc41eb22 PH |
432 | uint16_t i; |
433 | ||
434 | #ifdef WITH_INITIAL_PULSE | |
70095677 PH |
435 | uint8_t drive_last = 0; |
436 | uint8_t drive_slope = 0; | |
dc41eb22 PH |
437 | #endif |
438 | ||
70095677 | 439 | |
dc41eb22 PH |
440 | #ifdef USE_REGISTER_VARS |
441 | drive_on = 0; | |
442 | #endif | |
7c08d02a | 443 | mm_init(); |
70095677 | 444 | load_config(); |
56b25f8b | 445 | setup_hw(); |
dc41eb22 PH |
446 | |
447 | if (config.magic != EE_MAGIC) { | |
70095677 PH |
448 | config.magic = EE_MAGIC; |
449 | config.op_mode = OM_MOMENTARY; | |
56b25f8b PH |
450 | config.decoder_on = 1; |
451 | config.key_on = 1; | |
70095677 PH |
452 | config.decoder_off = 1; |
453 | config.key_off = 2; | |
454 | config.initial_pulse = 10; | |
455 | config.on_duty_cycle = 5; | |
93cb14d4 | 456 | config.learn_mode = LM_LEARN_ON_KEY; |
70095677 | 457 | } |
54de37bf | 458 | sei(); |
7c08d02a | 459 | |
54de37bf | 460 | while (1) { |
93cb14d4 PH |
461 | drive_start: |
462 | ||
dc41eb22 | 463 | #ifdef WITH_INITIAL_PULSE |
70095677 | 464 | cli(); |
dc41eb22 | 465 | if (drive_on && !drive_last) |
70095677 PH |
466 | drive_slope = 1; |
467 | else | |
468 | drive_slope = 0; | |
dc41eb22 | 469 | drive_last = drive_on; |
70095677 | 470 | sei(); |
dc41eb22 PH |
471 | #endif |
472 | if (drive_on) { | |
56b25f8b | 473 | |
dc41eb22 | 474 | #ifdef WITH_INITIAL_PULSE |
70095677 | 475 | if (drive_slope) { |
dc41eb22 | 476 | DRIVE_FULL; |
70095677 | 477 | for (i = 0; i < config.initial_pulse; i++) { |
dc41eb22 | 478 | _delay_ms(5); |
70095677 | 479 | } |
af5fd980 | 480 | } |
dc41eb22 PH |
481 | #endif |
482 | DRIVE_ON; | |
483 | ||
54de37bf | 484 | } else { |
dc41eb22 PH |
485 | DRIVE_OFF; |
486 | ||
487 | if (!config.learn_mode || | |
488 | config.learn_mode > LM_LEARN_OP_MODE) | |
70095677 | 489 | continue; |
54de37bf | 490 | |
70095677 | 491 | |
93cb14d4 | 492 | for (i = 0; i < config.learn_mode; i++) { |
70095677 | 493 | setpin(PIN_LED, 1); |
dc41eb22 | 494 | snd_on(); |
93cb14d4 | 495 | _delay_ms(10); |
70095677 | 496 | setpin(PIN_LED, 0); |
dc41eb22 PH |
497 | snd_off(); |
498 | if (drive_on) goto drive_start; | |
93cb14d4 | 499 | _delay_ms(135); |
dc41eb22 | 500 | if (drive_on) goto drive_start; |
70095677 | 501 | } |
93cb14d4 | 502 | for (i = 0; i < 15 - config.learn_mode; i++) { |
dc41eb22 | 503 | if (drive_on) goto drive_start; |
93cb14d4 | 504 | _delay_ms(70); |
70095677 PH |
505 | } |
506 | } | |
54de37bf PH |
507 | } |
508 | return 0; | |
509 | } | |
510 | ||
511 | /****************************************************************************** | |
512 | * The end :-) | |
513 | */ |