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