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