7d5c10a395f4f1e5adc73eea95750de3c71e5be3
[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
37 #define EE_MAGIC 0xab
38
39 enum 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
46 enum 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
56 struct 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;
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;
66 };
67
68 static struct EEMEM config ee_config;
69 static volatile struct config config;
70 static volatile uint8_t drive_on = 0;
71
72 static void load_config(void)
73 {
74 eeprom_read_block((uint8_t *)&config, &ee_config, sizeof(config));
75 }
76
77 static void save_config(void)
78 {
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
84 }
85
86
87
88 ISR(PCINT0_vect){
89 static uint8_t btn_last = 0;
90
91 mm_pinchange_handler();
92
93 if (BTN_PRESSED && !btn_last) {
94 config.learn_mode++;
95 config.learn_mode %= LM_END;
96 }
97 btn_last = BTN_PRESSED;
98 }
99
100 static uint8_t get_speed(uint8_t command)
101 {
102 uint8_t b1, b3, b5, b7;
103
104 b1 = ((command & 0x80) != 0);
105 // b2 = ((command & 0x40) != 0);
106 b3 = ((command & 0x20) != 0);
107 // b4 = ((command & 0x10) != 0);
108 b5 = ((command & 0x8) != 0);
109 // b6 = ((command & 0x4) != 0);
110 b7 = ((command & 0x2) != 0);
111 // b8 = ((command & 0x1) != 0);
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
119 #ifdef INTERPRET_DRIVE_COMMANDS
120
121 void 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
126 if (!seen_before) {
127 if ((decoder == 80) && (function == 0) && (command == 0xc0)) {
128 config.learn_mode = 1;
129 save_config();
130 }
131 }
132 seen_before = 1;
133
134 speed = get_speed(command);
135 static uint8_t itsme = 0;
136
137 if (config.learn_mode) {
138 if (decoder == 70) {
139 if (speed == 1) {
140 itsme = 1;
141 drive_on = 1;
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
158 }
159 }
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();
169 }
170 }
171
172 #else
173 #ifdef INTERPRET_DRIVE_SIMPLE
174
175 static volatile uint8_t current_loco;
176
177 void 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) {
187 if (speed >= 1) speed -= 1;
188 if (speed <= 14)
189 config.on_duty_cycle = speed;
190 else
191 config.on_duty_cycle = 14;
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
198 }
199 }
200 }
201
202 #endif
203 #endif
204 static volatile uint8_t switch_on = 0;
205
206 void mm_switch_command(uint8_t decoder, uint8_t command)
207 {
208 static uint8_t toggle_lock = 0;
209 switch(config.learn_mode) {
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:
218 if (!switch_on && current_loco == 52)
219 save_config();
220 switch_on = 1;
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 }
232
233 if ((decoder == config.decoder_on) &&
234 (command == 0)) { /* Primary key released */
235 switch(config.op_mode) {
236 case OM_MOMENTARY:
237 switch_on = 0;
238 break;
239 case OM_TOGGLE:
240 toggle_lock = 0;
241 break;
242 default:
243 break;
244 }
245 }
246 #ifdef HANDLE_OFF_KEY
247
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;
258 }
259 }
260 #endif
261 break;
262
263 case LM_LEARN_ON_KEY:
264 if (command) {
265 config.decoder_on = decoder;
266 config.key_on = command;
267 if (config.op_mode == OM_DOUBLE)
268 config.learn_mode = LM_LEARN_OFF_KEY;
269 else
270 config.learn_mode = LM_OFF;
271 save_config();
272 }
273 break;
274 #ifdef LEARN_ADVANCED
275 case LM_LEARN_OFF_KEY:
276 if (command) {
277 config.decoder_off = decoder;
278 config.key_off = command;
279 config.learn_mode = LM_OFF;
280 save_config();
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;
312
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();
342 config.learn_mode = LM_OFF;
343 break;
344 case 3:
345 config.op_mode = OM_DOUBLE;
346 save_config();
347 config.learn_mode = LM_OFF;
348 break;
349 case 5:
350 config.op_mode = OM_TOGGLE;
351 save_config();
352 config.learn_mode = LM_OFF;
353 break;
354 default:
355 break;
356 }
357 break;
358 #endif
359 }
360 }
361
362
363 /******************************************************************************
364 *
365 * main() - The main routine
366 *
367 */
368
369 int main(void) {
370 uint8_t learn_mode_off;
371 uint8_t drive_last = 0;
372 uint8_t drive_slope = 0;
373 uint8_t i;
374 uint8_t output_on;
375
376 load_config();
377 setup_hw();
378
379 if ((config.op_mode == OM_ERASED) || (config.magic != EE_MAGIC)) {
380 config.magic = EE_MAGIC;
381 config.op_mode = OM_MOMENTARY;
382 config.decoder_on = 1;
383 config.key_on = 1;
384 config.decoder_off = 1;
385 config.key_off = 2;
386 config.initial_pulse = 10;
387 config.on_duty_cycle = 5;
388 config.learn_mode = LM_LEARN_ON_KEY;
389 }
390 drive_on = 0;
391 sei();
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 );
399 while (1) {
400
401 drive_start:
402
403 output_on = drive_on || switch_on;
404 cli();
405 if (!drive_last && output_on)
406 drive_slope = 1;
407 else
408 drive_slope = 0;
409 drive_last = output_on;
410 sei();
411
412 if (output_on) {
413 if (drive_slope) {
414 for (i = 0; i < config.initial_pulse; i++) {
415 setpin(PIN_DRIVE, 1);
416 _delay_ms(20);
417 }
418 }
419
420 for (i = 0; i < config.on_duty_cycle; i++) {
421 setpin(PIN_DRIVE, output_on);
422 _delay_us(3);
423 }
424 for (i = 0; i < (28 - config.on_duty_cycle); i++) {
425 setpin(PIN_DRIVE, 0);
426 _delay_us(3);
427 }
428
429 } else {
430 setpin(PIN_DRIVE, 0);
431 if (!config.learn_mode)
432 continue;
433
434 learn_mode_off = !config.learn_mode;
435
436 if (output_on || (config.learn_mode && learn_mode_off))
437 goto drive_start;
438
439 for (i = 0; i < config.learn_mode; i++) {
440 setpin(PIN_LED, 1);
441 _delay_ms(10);
442 //if (output_on || (config.learn_mode && learn_mode_off))
443 // goto drive_start;
444 setpin(PIN_LED, 0);
445 _delay_ms(135);
446 //if (output_on || (config.learn_mode && learn_mode_off))
447 // goto drive_start;
448
449 //if (output_on || (config.learn_mode && learn_mode_off))
450 // goto drive_start;
451 }
452 for (i = 0; i < 15 - config.learn_mode; i++) {
453 //if (output_on || (config.learn_mode && learn_mode_off))
454 // goto drive_start;
455 setpin(PIN_LED, 0);
456 _delay_ms(70);
457 //if (output_on || (config.learn_mode && learn_mode_off))
458 // goto drive_start;
459 // _delay_ms(40);
460 }
461 }
462 }
463 return 0;
464 }
465
466
467 /******************************************************************************
468 * The end :-)
469 */