trennfix/sw: Nice, keep! Good!
[eisenbahn.git] / trennfix / sw / mm / src / mm_switch.c
CommitLineData
70095677
PH
1/******************************************************************************
2 *
3 * Trennfix firmware - mm_switch.c
4 *
5 * Maerklin Motorola switch command receiver
6 *
7 * Copyright (C) 2017 Philipp Hachtmann
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 *****************************************************************************/
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include <avr/io.h>
29#include <avr/eeprom.h>
30#include <avr/interrupt.h>
31#include <avr/pgmspace.h>
32
33#include <util/delay.h>
34#include <stdint.h>
35
93cb14d4
PH
36#include <config/hardware.h>
37#include <mm/mm_switch.h>
70095677 38
93cb14d4
PH
39/*
40 * Private data types
41 */
42
70095677
PH
43
44/*
93cb14d4
PH
45 *
46 * Check for stuff we need
70095677
PH
47 *
48 */
dc41eb22 49#if !defined(MM_TSTART) || !defined(MM_SENSE) || !defined(MM_TIMER_INT_VECT)
56b25f8b 50
dc41eb22 51#error Missing needed MM_... macro!
56b25f8b 52
93cb14d4
PH
53#endif
54
55/*
56 * Private global variables
57 */
93cb14d4 58
7c08d02a
PH
59#ifndef MM_USE_REGISTER_VARS
60
61static volatile uint8_t bitno = 0;
62static uint8_t shift_command;
63static uint8_t shift_function;
64static uint8_t shift_address;
65static enum mm_recstate recstate = MM_IDLE;
66
67#endif
93cb14d4
PH
68
69/*
70 * Lookup trinary nibble
71 *
72 * This was implemented using a switch statement before.
73 * Changing the lookup to a table did only add two bytes
74 * of memory and saved ca. 50 bytes program memory.
75 */
76static const uint8_t nibble_table[16]={
77 [0x0] = 0,
78 [0xc] = 1,
79 [0x8] = 2,
80 [0x3] = 3,
81 [0xf] = 4,
82 [0xb] = 5,
83 [0x2] = 6,
84 [0xe] = 7,
85 [0xa] = 8
86};
87#define lookup_nibble(nibble) nibble_table[nibble & 0xf]
88
70095677
PH
89static uint8_t lookup_decoder(uint8_t mm_byte)
90{
56b25f8b
PH
91 uint8_t low;
92 uint8_t high;
93 if (mm_byte == 0)
93cb14d4 94 return 80;
56b25f8b
PH
95 low = lookup_nibble(mm_byte >> 4);
96 high = lookup_nibble(mm_byte & 0xf);
97 if (!low)
70095677 98 return 0;
56b25f8b 99 return 9 * high + low;
70095677
PH
100}
101
7c08d02a 102static uint8_t lookup_command(uint8_t mm_command)
70095677 103{
7c08d02a
PH
104 uint8_t res;
105 /*
106 * Check for aabbccdd condition
107 *
108 * a a b b c c d d mm_command
109 * XOR a b b c c d d 0 mm_command << 1
110 * Mask 1 0 1 0 1 0 1 0 0xaa
111 *
112 * Must be zero!
113 *
114 */
115
116 if ((mm_command ^ (mm_command << 1)) & 0xaa)
70095677 117 return 0;
7c08d02a
PH
118 /*
119 * Protocol differences:
120 * =====================
121 *
122 * I have an old "central control" 6022 and a "control unit" 6021
123 * for measurements and test. It is assumed that the 6022 outputs
124 * old MM1 format while the 6021 definitively outputs MM2 telegrams.
125 *
126 * In MM1, switch commands are different from MM2 with respect what
127 * happens if you release a button.
128 *
129 * When you press a button, both protocols send
130 *
131 * <aaaaaaaa><00><aabbcc11>
132 *
133 * where a = 1, b = 2, c = 4 and the keys are numerated from 0 to 7
134 * in the order 1 red, 1 green, 2 red, 2 green and so on.
135 *
136 * The last two bits correspond to "on" state of the button/coil.
137 *
138 * When a key is released under MM1 protocol, the sequence sent is
139 * analogue to the button down sequence:
140 *
141 * <aaaaaaaa><00><aabbcc00> where abc again represents the button's
142 * address and the last bits now signal "off".
143 *
144 * MM2 handles this differently:
145 * Whenever any key from the addressed decoder is released, the sequence
146 * <aaaaaaaa>00<00000000> is sent - not only for key 0, but for all
147 * keys!
148 *
149 * While MM1 presents the theoretical possibility to press several keys
150 * independently and simultaneously (which my keyboard does NOT
151 * support), MM2 supports only one key at a time (besides strange
152 * sequences like "one down, another down, all up"...
153 *
154 * A decoder that strictly adheres to the MM1 standard would not work
155 * properly with MM2 control units. As far as I know all K83/K84
156 * decoders always worked with MM2 control units. That means that
157 * they reduce the commands to the possibilities of MM2 from the
158 * beginning.
159 *
160 * Possible use cases for the old protocol button release commands:
161 * - Determine if the protocol is MM1 or MM2
162 * - Implement hidden evil features into the controller which can
163 * only be summoned by old MM1 gear or selfmade control telegram
164 * generators.
165 *
166 * What this code now actually does:
167 * =================================
168 *
169 * When key pressed (aabbcc11), it will send out the key number in the
170 * range 1-8 and 0 if it gets any key up command and therefore ignore
171 * the key number if it is transmitted with the key up command.
172 *
173 */
174 if (!(mm_command & 0x01))
175 res = 0;
176 else
177 res = (mm_command & 0x80) * 1 + (mm_command & 0x20) * 0x02
178 + (mm_command & 0x08) * 0x04 + 1;
179 return res;
70095677
PH
180}
181
7c08d02a 182
70095677
PH
183/* We will shift from right to left.
184 * XXXXXXXX XX XXXXXXXX
56b25f8b 185 * shift_address shift_function shift_command
70095677 186 *
56b25f8b 187 * The bits 7 downto 2 of shift_function are ignored.
70095677 188 */
7c08d02a
PH
189#define SAVE_ANOTHER_40_BYTES
190#ifdef SAVE_ANOTHER_40_BYTES
70095677 191
7c08d02a
PH
192void shift(uint8_t value)
193{
194 asm("ror %[val] ; Shift value right into carry\n\t"
195 "rol %[cmd] ; and shift to command reg\n\t"
196 "mov __tmp_reg__, %[func] ; save function value \n\t"
197 "rol %[func] ; Shift up function value\n\t"
198 "ror __tmp_reg__ ; shift bit 1\n\t"
199 "ror __tmp_reg__ ; down to carry\n\t"
200 "rol %[addr] ; And we're at the address\n\t"
201 : [cmd] "=r" (shift_command), [func] "=r" (shift_function),
202 [addr] "=r" (shift_address)
203 : "0" (shift_command), "1" (shift_function),
204 "2" (shift_address), [val] "r" (value)
205 );
206}
207
208#else /* This is what we do to shift */
209
210void shift(uint8_t value)
70095677 211{
56b25f8b
PH
212 shift_address <<= 1;
213 if (shift_function & 2)
214 shift_address |= 1;
215 shift_function <<= 1;
216 if (shift_command & 0x80)
217 shift_function |= 1;
218 shift_command <<= 1;
70095677 219 if (value)
56b25f8b 220 shift_command |= 1;
70095677 221}
7c08d02a
PH
222#endif
223
9c77e706
PH
224static volatile uint8_t mm_rec_tolerated_timeouts;
225
70095677 226
93cb14d4 227ISR(MM_TIMER_INT_VECT) {
70095677 228
93cb14d4
PH
229 static volatile uint8_t shift_command_first;
230 static volatile uint8_t shift_function_first;
231 static volatile uint8_t shift_address_first;
232 uint8_t address;
233 uint8_t command;
234
235#ifdef MM_FILTER_REPEATED
56b25f8b
PH
236 static uint8_t address_last = 0xff;
237 static uint8_t function_last = 0xff;
238 static uint8_t command_last = 0xff;
93cb14d4 239#endif
70095677 240
dc41eb22
PH
241 MM_TSTART;
242
70095677 243 switch(recstate) {
7c08d02a 244 case MM_FIRST_FAST_SAMPLE:
dc41eb22 245 recstate = MM_FIRST_SLOW_SAMPLE;
70095677
PH
246 break;
247
7c08d02a 248 case MM_FIRST_SLOW_SAMPLE:
70095677
PH
249 bitno = 0;
250
7c08d02a 251 case MM_SLOW_SAMPLE:
9c77e706 252 recstate = MM_SLOW_WAIT_FOR_CLOCK_DELAY;
70095677 253 break;
9c77e706 254
7c08d02a
PH
255 case MM_FAST_SAMPLE:
256 recstate = MM_FAST_WAIT_FOR_CLOCK;
70095677
PH
257 break;
258
7c08d02a 259 case MM_FAST_WAIT_FOR_CLOCK: /* A timeout! */
9c77e706
PH
260 if (mm_rec_tolerated_timeouts) {
261 mm_rec_tolerated_timeouts--;
262 } else {
263 recstate = MM_IDLE;
264
70095677 265 }
9c77e706
PH
266 return;
267
268 case MM_SLOW_SAMPLE_DELAY:
269 recstate = MM_SLOW_SAMPLE;
270 return;
271
272 case MM_SLOW_WAIT_FOR_CLOCK_DELAY:
273 recstate = MM_SLOW_WAIT_FOR_CLOCK;
70095677
PH
274 return;
275
7c08d02a 276 case MM_SLOW_WAIT_FOR_CLOCK:
9c77e706 277 if (mm_rec_tolerated_timeouts) {
dc41eb22 278 trigger();
9c77e706
PH
279 mm_rec_tolerated_timeouts--;
280 recstate = MM_SLOW_WAIT_FOR_CLOCK_DELAY;
70095677
PH
281 return;
282 }
56b25f8b 283 default:
7c08d02a 284 recstate = MM_IDLE;
9c77e706 285 case MM_IDLE:
70095677
PH
286 return;
287 }
288
289 shift(MM_SENSE);
290 bitno++;
56b25f8b 291
70095677 292 if (bitno == 18) { /* Save first received word */
93cb14d4
PH
293 shift_address_first = shift_address;
294 shift_function_first = shift_function;
295 shift_command_first = shift_command;
9c77e706 296 mm_rec_tolerated_timeouts = 18;
dc41eb22 297 trigger();
70095677 298 }
56b25f8b 299
70095677 300 if (bitno == 36) {
56b25f8b 301 if ((shift_command == shift_command_first) &&
93cb14d4
PH
302 (shift_address == shift_address_first) &&
303 (shift_function == shift_function_first)) {
304
305#ifdef MM_FILTER_REPEATED
56b25f8b
PH
306 if ((shift_address != address_last) || (shift_command != command_last) ||
307 shift_function != function_last) {
93cb14d4
PH
308#endif
309 address = lookup_decoder(shift_address);
56b25f8b 310
9c77e706
PH
311 if (recstate == MM_SLOW_WAIT_FOR_CLOCK_DELAY) {
312 trigger();
93cb14d4 313 mm_switch_drive(address, shift_function, shift_command);
7c08d02a 314 } else if (recstate == MM_FAST_WAIT_FOR_CLOCK) {
9c77e706 315 trigger();
93cb14d4
PH
316 command = lookup_command(shift_command);
317 mm_switch_command(address, command);
70095677 318 }
93cb14d4 319#ifdef MM_FILTER_REPEATED
70095677 320 }
93cb14d4
PH
321 address_last = shift_address;
322 function_last = shift_function;
323 command_last = shift_command;
324#endif
70095677 325 }
56b25f8b 326
70095677
PH
327 }
328}
329
56b25f8b
PH
330//void __attribute((weak)) mm_switch_drive(uint8_t address, uint8_t function, uint8_t command);
331
70095677
PH
332ISR(BADISR_vect)
333{
334 while(1) {
335 /*
336 setpin(PIN_LED, 1);
337 _delay_ms(30);
338 setpin(PIN_LED, 0);
339 _delay_ms(30);
340 setpin(PIN_LED, 1);
341 _delay_ms(30);
342 setpin(PIN_LED, 0);
343 _delay_ms(2000);
344 */
345 }
346}
347
348ISR(TIM0_OVF_vect)
349{
350 return;
351 while(1) {
56b25f8b 352 setpin(PIN_LED, 1);
70095677
PH
353 _delay_ms(30);
354 setpin(PIN_LED, 0);
355 _delay_ms(300);
356 }
357
358}
359
70095677
PH
360/* Pin change interrupt vector */
361void mm_pinchange_handler(void)
362{
363 static uint8_t sense_last;
364
365 if (MM_SENSE == sense_last)
366 return;
367 sense_last = MM_SENSE;
368 if (!sense_last)
369 return;
56b25f8b 370
dc41eb22 371 MM_TSTART;
9c77e706 372
70095677 373 switch(recstate) {
7c08d02a 374 case MM_IDLE:
70095677 375 bitno = 0;
7c08d02a 376 recstate = MM_FIRST_FAST_SAMPLE;
70095677 377 break;
9c77e706 378
7c08d02a
PH
379 case MM_FIRST_SLOW_SAMPLE:
380 recstate = MM_FAST_SAMPLE;
70095677 381 break;
9c77e706 382
7c08d02a
PH
383 case MM_FAST_WAIT_FOR_CLOCK:
384 recstate = MM_FAST_SAMPLE;
9c77e706 385 mm_rec_tolerated_timeouts = 0;
70095677 386 break;
9c77e706
PH
387
388 case MM_SLOW_WAIT_FOR_CLOCK_DELAY: /* If clock comes early */
389 recstate = MM_SLOW_WAIT_FOR_CLOCK;
390 break;
391
7c08d02a 392 case MM_SLOW_WAIT_FOR_CLOCK:
9c77e706
PH
393 recstate = MM_SLOW_SAMPLE_DELAY;
394 mm_rec_tolerated_timeouts = 0;
70095677
PH
395 break;
396
dc41eb22
PH
397 case MM_SLOW_SAMPLE_DELAY:
398 recstate = MM_SLOW_SAMPLE;
399 break;
70095677 400 /* Not expected */
7c08d02a
PH
401 case MM_FIRST_FAST_SAMPLE:
402 case MM_FAST_SAMPLE:
403 case MM_SLOW_SAMPLE:
9c77e706 404 recstate = MM_IDLE;
70095677
PH
405 default:
406 break;
407 }
408}
409
93cb14d4
PH
410void __attribute__((weak))mm_switch_drive(uint8_t decoder, uint8_t function,
411 uint8_t command)
412{
413 while(1);
414}
415
416void __attribute__((weak))mm_switch_command(uint8_t address, uint8_t command)
417{
418}
419
420
70095677
PH
421/******************************************************************************
422 * The end :-)
423 */
424
425
426