trennfix/sw: Temporary first working time measurement method
[eisenbahn.git] / trennfix / sw / mm / src / mm_switch.c
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
36 #include <config/hardware.h>
37 #include <mm/mm_switch.h>
38
39
40 /*
41 *
42 * Check for stuff we need
43 *
44 */
45 #if !defined(MM_TSTART) || !defined(MM_SENSE) || !defined(MM_TIMER_INT_VECT)
46
47 #error Missing needed MM_... macro!
48
49 #endif
50
51 /*
52 * Private global variables
53 */
54
55 #ifndef MM_USE_REGISTER_VARS
56
57 static volatile uint8_t bitno = 0;
58 static uint8_t shift_command;
59 static uint8_t shift_function;
60 static uint8_t shift_address;
61 static enum mm_recstate recstate = MM_IDLE;
62 static uint8_t sense_last = 23;
63 uint8_t time_h = 0;
64 uint8_t time_l = 0;
65 uint8_t bit_val = 23;
66
67 #endif
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 */
76 static 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
89 static uint8_t __attribute__((unused)) lookup_decoder(uint8_t mm_byte)
90 {
91 uint8_t low;
92 uint8_t high;
93 if (mm_byte == 0)
94 return 80;
95 low = lookup_nibble(mm_byte >> 4);
96 high = lookup_nibble(mm_byte & 0xf);
97 if (!low)
98 return 0;
99 return 9 * high + low;
100 }
101
102 static uint8_t __attribute__((unused)) lookup_command(uint8_t mm_command)
103 {
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)
117 return 0;
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;
180 }
181
182 /* We will shift from right to left.
183 * XXXXXXXX XX XXXXXXXX
184 * shift_address shift_function shift_command
185 *
186 * The bits 7 downto 2 of shift_function are ignored.
187 */
188 #define SAVE_ANOTHER_40_BYTES
189 #ifdef SAVE_ANOTHER_40_BYTES
190
191 void shift(uint8_t value)
192 {
193 asm("ror %[val] ; Shift value right into carry\n\t"
194 "rol %[cmd] ; and shift to command reg\n\t"
195 "mov __tmp_reg__, %[func] ; save function value \n\t"
196 "rol %[func] ; Shift up function value\n\t"
197 "ror __tmp_reg__ ; shift bit 1\n\t"
198 "ror __tmp_reg__ ; down to carry\n\t"
199 "rol %[addr] ; And we're at the address\n\t"
200 : [cmd] "=r" (shift_command), [func] "=r" (shift_function),
201 [addr] "=r" (shift_address)
202 : "0" (shift_command), "1" (shift_function),
203 "2" (shift_address), [val] "r" (value)
204 );
205 }
206
207 #else /* This is what we do to shift */
208
209 void shift(uint8_t value)
210 {
211 shift_address <<= 1;
212 if (shift_function & 2)
213 shift_address |= 1;
214 shift_function <<= 1;
215 if (shift_command & 0x80)
216 shift_function |= 1;
217 shift_command <<= 1;
218 if (value)
219 shift_command |= 1;
220 }
221 #endif
222
223 static volatile uint8_t mm_rec_tolerated_timeouts;
224
225 #define MM_SLOW 0
226 #define MM_FAST 1
227
228 static uint8_t style;
229
230 void mm_feed_bit(uint8_t bit, uint8_t seen_style)
231 {
232 static volatile uint8_t shift_command_first;
233 static volatile uint8_t shift_function_first;
234 static volatile uint8_t shift_address_first;
235 uint8_t address;
236 uint8_t command;
237
238 #ifdef MM_FILTER_REPEATED
239 static uint8_t address_last = 0xff;
240 static uint8_t function_last = 0xff;
241 static uint8_t command_last = 0xff;
242 #endif
243
244 if (bitno == 0)
245 style = seen_style;
246 else
247 if (seen_style != style) {
248 bitno = 0;
249 return;
250 }
251
252 shift(bit);
253 bitno++;
254 if (bitno == 18) { /* Save first received word */
255 shift_address_first = shift_address;
256 shift_function_first = shift_function;
257 shift_command_first = shift_command;
258 mm_rec_tolerated_timeouts = 18;
259 }
260
261 if (bitno == 36) {
262
263 if ((shift_command == shift_command_first) &&
264 (shift_address == shift_address_first) &&
265 (shift_function == shift_function_first)) {
266
267
268 #ifdef MM_FILTER_REPEATED
269 if ((shift_address != address_last) || (shift_command != command_last) ||
270 shift_function != function_last) {
271 #endif
272 if (style == MM_SLOW) {
273 address = lookup_decoder(shift_address);
274 mm_switch_drive(address, shift_function, shift_command);
275 } else {
276 trigger();
277
278 command = lookup_command(shift_command);
279 mm_switch_command(address, command);
280 }
281 bitno = 0;
282 #ifdef MM_FILTER_REPEATED
283 }
284 address_last = shift_address;
285 function_last = shift_function;
286 command_last = shift_command;
287 #endif
288 }
289
290 }
291 }
292
293 ISR(__vector_timer_extra) {
294 //trigger();
295 }
296
297 /*
298 * The timeout interrupt vector does nothing else
299 * than incrementing the time_h round counter.
300 *
301 * It is written in naked assembly because we want to avoid pushing
302 * and popping of all upper registers.
303 */
304 void __attribute__((naked)) MM_TIMER_INT_VECT(void)
305 {
306 asm("push r0 ; save r0 \n\t"
307 "in r0, __SREG__ \n\t"
308 #ifdef MM_USE_REGISTER_VARS
309 "inc %[th] \n\t"
310 #else
311 "push r1 \n\t"
312 "lds r1, time_h \n\t"
313 "inc r1 \n\t"
314 "sts time_h, r1 \n\t"
315 "pop r1 \n\t"
316 #endif
317 "out __SREG__, r0 \n\t"
318 "pop r0 \n\t"
319 // "rjmp __vector_timer_extra \n\t"
320 "reti \n\t"
321 #ifdef MM_USE_REGISTER_VARS
322 :: [th] "r" (time_h)
323 #endif
324 );
325 }
326
327 /*
328 * Another naked interrupt trampoline
329 *
330 * Here we first save the timer value as fast as possible, then we jump (!)
331 * into the "official" interrupt handler with all its decorations.
332 */
333 void __attribute__((naked)) PCINT0_vect(void)
334 {
335 #ifdef MM_USE_REGISTER_VARS
336 asm("in %[tl], %[tmr] \n\t"
337 "rjmp __vector_pinchange \n\t"
338 :: [tl] "r" (time_l), [tmr] "I" (_SFR_IO_ADDR(TCNT0))
339 );
340 #else
341 asm("push r0 \n\t"
342 "in r0, %[tmr] \n\t"
343 "sts time_l, r0 \n\t"
344 "pop r0 \n\t"
345 "rjmp __vector_pinchange \n\t"
346 :: [tmr] "I" (_SFR_IO_ADDR(TCNT0))
347 );
348 #endif
349 }
350
351 /* Pin change interrupt vector, here we have a bit more time */
352 ISR(__vector_pinchange){
353 /* First kill off that timer */
354
355 MM_TSTART; /* Restart timer */
356
357 /* Account for not yet handled timer overflow */
358 if (TIFR & _BV(TOV0)) {
359 time_h++;
360 TIFR |= _BV(TOV0);
361 }
362
363 bit_val = !MM_SENSE;
364
365 uint16_t duration = time_h << 8;
366 duration += time_l;
367 time_h = 0;
368
369 /*
370 * The nominal length of a bit cycle is 208 us.
371 * This consists of 8 parts, each 26 us:
372 * 1 d d d d d d 0
373 *
374 * That means that the 1 pulse is 7 * 26 = 182us
375 * and the short pulse is 1 * 26 = 26us.
376 *
377 * Reality seems to look not as that exact. I measure
378 * 26us for the clock pulse, but 196 for the long pulse.
379 *
380 */
381
382 #define D_MATCH(d, v) ((duration > (d * 2 - 2 * v)) && (duration < (d * 2 + 2 * v)))
383
384 static uint8_t mm_positive = 1;
385 static uint8_t last_was_short = 0;
386
387 if (D_MATCH(26, 4)) {
388 if (last_was_short)
389 mm_positive = bit_val;
390 last_was_short = 1;
391
392 if (bit_val == mm_positive)
393 mm_feed_bit(0, MM_SLOW);
394 } else {
395 last_was_short = 0;
396 }
397
398 if (style == MM_SLOW) {
399 if (duration > 4000) { /* Maerklin inter package timeout 2ms */
400 bitno = 0;
401 goto done;
402 }
403 } else {
404 if (duration > 2000) { /* Maerklin inter package timeout 1ms */
405 bitno = 0;
406 goto done;
407 }
408 }
409
410 if (bit_val != mm_positive)
411 goto done;
412
413 if (D_MATCH(182, 25)) mm_feed_bit(1, MM_SLOW);
414
415 if (D_MATCH(91, 17)) {mm_feed_bit(1, MM_FAST);}
416 if (D_MATCH(13, 4)) {mm_feed_bit(0, MM_FAST);}
417
418 done:
419 mm_switch_pinchange_callback();
420 }
421
422 void __attribute__((weak))mm_switch_pinchange_callback(void)
423 {
424 }
425
426 void __attribute__((weak))mm_switch_drive(uint8_t decoder, uint8_t function,
427 uint8_t command)
428 {
429 }
430
431 void __attribute__((weak))mm_switch_command(uint8_t address, uint8_t command)
432 {
433 }
434
435
436
437 /******************************************************************************
438 * The end :-)
439 */