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> | |
33 | ||
70095677 PH |
34 | #include "mm_switch.h" |
35 | #include "hardware.h" | |
54de37bf PH |
36 | |
37 | // #define DEBUG_TIMEOUT | |
54de37bf PH |
38 | #ifdef DEBUG_TIMEOUT |
39 | #define MON_TIMEOUT(val) setpin(DRIVE, val) | |
40 | #else | |
41 | #define MON_TIMEOUT(xx) ({0;}) | |
42 | #endif | |
43 | ||
70095677 PH |
44 | #define EE_MAGIC 0xab |
45 | ||
56b25f8b PH |
46 | static void trigger(void) |
47 | { | |
48 | setpin(PIN_DRIVE, 1); | |
49 | _delay_us(10); | |
50 | setpin(PIN_DRIVE, 0); | |
51 | } | |
52 | ||
70095677 PH |
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; | |
56b25f8b | 83 | static volatile enum learn_mode learn_mode = LM_OFF; |
70095677 PH |
84 | |
85 | static void load_config(void) | |
54de37bf | 86 | { |
70095677 | 87 | eeprom_read_block(&config, &ee_config, sizeof(config)); |
54de37bf PH |
88 | } |
89 | ||
70095677 | 90 | static void save_config(void) |
54de37bf | 91 | { |
56b25f8b | 92 | eeprom_update_block(&config, &ee_config, sizeof(config)); |
54de37bf PH |
93 | } |
94 | ||
af5fd980 | 95 | static volatile uint8_t drive_on = 0; |
54de37bf | 96 | |
70095677 PH |
97 | ISR(PCINT0_vect){ |
98 | static uint8_t btn_last = 0; | |
56b25f8b | 99 | |
70095677 | 100 | mm_pinchange_handler(); |
54de37bf | 101 | |
70095677 PH |
102 | if (BTN_PRESSED && !btn_last) { |
103 | learn_mode++; | |
104 | learn_mode %= LM_END; | |
105 | } | |
106 | btn_last = BTN_PRESSED; | |
54de37bf PH |
107 | } |
108 | ||
56b25f8b PH |
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 | ||
70095677 | 172 | void mm_switch_command(uint8_t decoder, uint8_t command) |
54de37bf | 173 | { |
70095677 PH |
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 | } | |
54de37bf | 196 | |
70095677 PH |
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 | } | |
54de37bf | 210 | |
70095677 PH |
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; | |
70095677 PH |
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 | ||
56b25f8b | 237 | #ifdef LEARN_THE_STUFF |
70095677 PH |
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; | |
54de37bf | 275 | |
70095677 PH |
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; | |
56b25f8b | 321 | #endif |
54de37bf PH |
322 | } |
323 | } | |
324 | ||
325 | ||
54de37bf PH |
326 | /****************************************************************************** |
327 | * | |
328 | * main() - The main routine | |
329 | * | |
330 | */ | |
331 | ||
332 | int main(void) { | |
56b25f8b | 333 | // uint8_t learn_mode_off; |
70095677 PH |
334 | uint8_t drive_last = 0; |
335 | uint8_t drive_slope = 0; | |
336 | uint8_t i; | |
337 | ||
70095677 | 338 | load_config(); |
56b25f8b | 339 | setup_hw(); |
70095677 | 340 | |
56b25f8b | 341 | if ((config.op_mode == OM_ERASED) || (config.magic != EE_MAGIC)) { |
70095677 PH |
342 | config.magic = EE_MAGIC; |
343 | config.op_mode = OM_MOMENTARY; | |
56b25f8b PH |
344 | config.decoder_on = 1; |
345 | config.key_on = 1; | |
70095677 PH |
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 | } | |
54de37bf | 352 | sei(); |
70095677 | 353 | |
54de37bf | 354 | while (1) { |
70095677 | 355 | |
56b25f8b | 356 | // drive_start: |
70095677 PH |
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(); | |
56b25f8b | 365 | |
70095677 | 366 | if (drive_on) { |
56b25f8b | 367 | #ifdef ROOM_FOR_KICK |
70095677 PH |
368 | if (drive_slope) { |
369 | for (i = 0; i < config.initial_pulse; i++) { | |
370 | setpin(PIN_DRIVE, 1); | |
371 | _delay_ms(10); | |
372 | } | |
af5fd980 | 373 | } |
56b25f8b PH |
374 | #endif |
375 | ||
376 | #define ROOM_FOR_PWM | |
377 | #ifdef ROOM_FOR_PWM | |
70095677 PH |
378 | for (i = 0; i < config.on_duty_cycle; i++) { |
379 | setpin(PIN_DRIVE, drive_on); | |
56b25f8b | 380 | _delay_us(1); |
af5fd980 | 381 | } |
56b25f8b | 382 | for (i = 0; i < (14 - config.on_duty_cycle); i++) { |
70095677 | 383 | setpin(PIN_DRIVE, 0); |
56b25f8b | 384 | _delay_us(1); |
70095677 PH |
385 | } |
386 | ||
56b25f8b PH |
387 | #else |
388 | setpin(PIN_DRIVE, 1); | |
389 | #endif | |
390 | ||
54de37bf | 391 | } else { |
56b25f8b | 392 | setpin(PIN_DRIVE, 0); |
70095677 PH |
393 | if (!learn_mode) |
394 | continue; | |
54de37bf | 395 | |
56b25f8b | 396 | //learn_mode_off = !learn_mode; |
70095677 PH |
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 | } | |
54de37bf PH |
422 | } |
423 | return 0; | |
424 | } | |
425 | ||
70095677 | 426 | |
54de37bf PH |
427 | /****************************************************************************** |
428 | * The end :-) | |
429 | */ |