4ae273a383ca19faac52497c85f4d63eb290d5cc
[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 enum op_mode {
47 OM_MOMENTARY, /* on as long as "key on" pressed */
48 OM_DOUBLE, /* On off with "on" and "off" keys */
49 OM_TOGGLE, /* toggle on "key on" pressed */
50 OM_ERASED = 0xff /* EEPROM erased, need setup */
51 };
52
53 enum learn_mode {
54 LM_OFF = 0,
55 LM_LEARN_ON_KEY, /* Learn primary key */
56 LM_LEARN_OFF_KEY, /* Learn secondary key, relevant for OM_DOUBLE */
57 LM_LEARN_INITIAL, /* Learn initial pulse length, 10ms steps */
58 LM_LEARN_DUTY, /* Learn duty cycle 0-10 */
59 LM_LEARN_OP_MODE, /* Learn operation mode */
60 LM_END, /* Only a label */
61 };
62
63 struct config {
64 uint8_t magic; /* Magic value */
65 enum op_mode op_mode;
66 uint8_t decoder_on;
67 uint8_t key_on;
68 uint8_t decoder_off;
69 uint8_t key_off;
70 uint8_t initial_pulse; /* Lenghth of initial pulse in 10ms steps */
71 uint8_t on_duty_cycle; /* Duty cycle for on. 0-10 */
72 };
73
74 static struct EEMEM config ee_config;
75 static struct config config;
76 static volatile enum learn_mode learn_mode = LM_LEARN_ON_KEY;
77
78 static void load_config(void)
79 {
80 eeprom_read_block(&config, &ee_config, sizeof(config));
81 }
82
83 static void save_config(void)
84 {
85 eeprom_write_block(&config, &ee_config, sizeof(config));
86 }
87
88 static volatile uint8_t drive_on = 0;
89
90
91 ISR(PCINT0_vect){
92 static uint8_t btn_last = 0;
93 mm_pinchange_handler();
94
95 if (BTN_PRESSED && !btn_last) {
96 learn_mode++;
97 learn_mode %= LM_END;
98 }
99 btn_last = BTN_PRESSED;
100 }
101
102 void mm_switch_command(uint8_t decoder, uint8_t command)
103 {
104 static uint8_t toggle_lock = 0;
105 switch(learn_mode) {
106
107 case LM_OFF:
108 default:
109 if ((decoder == config.decoder_on) &&
110 (command == config.key_on)) { /* Primary key pressed */
111 switch(config.op_mode) {
112 case OM_MOMENTARY:
113 case OM_DOUBLE:
114 drive_on = 1;
115 break;
116 case OM_TOGGLE:
117 if (!toggle_lock) {
118 drive_on = !drive_on;
119 toggle_lock = 1;
120 }
121 break;
122 default:
123 break;
124 }
125 }
126
127 if ((decoder == config.decoder_on) &&
128 (command == 0)) { /* Primary key released */
129 switch(config.op_mode) {
130 case OM_MOMENTARY:
131 drive_on = 0;
132 break;
133 case OM_TOGGLE:
134 toggle_lock = 0;
135 break;
136 default:
137 break;
138 }
139 }
140
141 if ((decoder == config.decoder_off) &&
142 (command == config.key_off)) { /* Secondary "off" key pressed */
143 switch(config.op_mode) {
144 case OM_DOUBLE:
145 drive_on = 0;
146 break;
147 case OM_TOGGLE:
148 case OM_MOMENTARY:
149 default:
150 break;
151
152 }
153 }
154 break;
155
156 case LM_LEARN_ON_KEY:
157 if (command) {
158 config.decoder_on = decoder;
159 config.key_on = command;
160 save_config();
161 if (config.op_mode == OM_DOUBLE)
162 learn_mode = LM_LEARN_OFF_KEY;
163 else
164 learn_mode = LM_OFF;
165 }
166 break;
167
168 case LM_LEARN_OFF_KEY:
169 if (command) {
170 config.decoder_off = decoder;
171 config.key_off = command;
172 save_config();
173 learn_mode = LM_OFF;
174 }
175 break;
176
177 case LM_LEARN_INITIAL:
178 if (drive_on) {
179 if (command == 0)
180 drive_on = 0;
181
182 } else {
183 switch(command) {
184 case 1:
185 if (config.initial_pulse >= 10)
186 config.initial_pulse -= 10;
187 else
188 config.initial_pulse = 0;
189 save_config();
190 drive_on = 1;
191 break;
192 case 2:
193 if (config.initial_pulse <= 245)
194 config.initial_pulse += 10;
195 else
196 config.initial_pulse = 255;
197 save_config();
198 drive_on = 1;
199 break;
200 default:
201 break;
202 }
203 }
204 break;
205
206 case LM_LEARN_DUTY:
207 if (drive_on) {
208 if (command == 0)
209 drive_on = 0;
210 } else {
211 switch(command) {
212 case 1:
213 if (config.on_duty_cycle > 0)
214 config.on_duty_cycle -= 1;
215 save_config();
216 drive_on = 1;
217 break;
218 case 2:
219 if (config.on_duty_cycle < 10)
220 config.on_duty_cycle += 1;
221 save_config();
222 drive_on = 1;
223 break;
224 default:
225 break;
226 }
227 }
228 break;
229
230 case LM_LEARN_OP_MODE:
231 switch(command) {
232 case 1:
233 config.op_mode = OM_MOMENTARY;
234 save_config();
235 learn_mode = LM_OFF;
236 break;
237 case 3:
238 config.op_mode = OM_DOUBLE;
239 save_config();
240 learn_mode = LM_OFF;
241 break;
242 case 5:
243 config.op_mode = OM_TOGGLE;
244 save_config();
245 learn_mode = LM_OFF;
246 break;
247 default:
248 break;
249 }
250 break;
251
252 }
253 }
254
255
256 /******************************************************************************
257 *
258 * main() - The main routine
259 *
260 */
261
262 int main(void) {
263 uint8_t learn_mode_off;
264 uint8_t drive_last = 0;
265 uint8_t drive_slope = 0;
266 uint8_t i;
267
268 setup_hw();
269 load_config();
270
271 if (config.op_mode == OM_ERASED || config.magic != EE_MAGIC) {
272 config.magic = EE_MAGIC;
273 config.op_mode = OM_MOMENTARY;
274 // config.decoder_on = 1;
275 // config.key_on = 1;
276 config.decoder_off = 1;
277 config.key_off = 2;
278 config.initial_pulse = 10;
279 config.on_duty_cycle = 5;
280 learn_mode = LM_LEARN_ON_KEY;
281 }
282 sei();
283
284 while (1) {
285
286 drive_start:
287
288 cli();
289 if (!drive_last && drive_on)
290 drive_slope = 1;
291 else
292 drive_slope = 0;
293 drive_last = drive_on;
294 sei();
295 if (drive_on) {
296 if (drive_slope) {
297 for (i = 0; i < config.initial_pulse; i++) {
298 setpin(PIN_DRIVE, 1);
299 _delay_ms(10);
300 }
301 }
302
303 for (i = 0; i < config.on_duty_cycle; i++) {
304 setpin(PIN_DRIVE, drive_on);
305 _delay_us(2);
306 }
307
308 for (i = 0; i < (10 - config.on_duty_cycle); i++) {
309 setpin(PIN_DRIVE, 0);
310 _delay_us(2);
311 }
312
313 } else {
314 if (!learn_mode)
315 continue;
316
317 learn_mode_off = !learn_mode;
318
319 //if (drive_on || (learn_mode && learn_mode_off))
320 // goto drive_start;
321
322 for (i = 0; i < learn_mode; i++) {
323 setpin(PIN_LED, 1);
324 _delay_ms(30);
325 setpin(PIN_LED, 0);
326 _delay_ms(280);
327
328 //if (drive_on || (learn_mode && learn_mode_off))
329 // goto drive_start;
330 //if (drive_on || (learn_mode && learn_mode_off))
331 // goto drive_start;
332 }
333 for (i = 0; i < 15 - learn_mode; i++) {
334 //if (drive_on || (learn_mode && learn_mode_off))
335 // goto drive_start;
336 setpin(PIN_LED, 0);
337 _delay_ms(80);
338 //if (drive_on || (learn_mode && learn_mode_off))
339 // goto drive_start;
340 // _delay_ms(40);
341 }
342 }
343 }
344 return 0;
345 }
346
347
348 /******************************************************************************
349 * The end :-)
350 */