trennfix/sw: Fixed a bit beauty.
[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
PH
38
39
40/*
93cb14d4
PH
41 *
42 * Check for stuff we need
70095677
PH
43 *
44 */
dc41eb22 45#if !defined(MM_TSTART) || !defined(MM_SENSE) || !defined(MM_TIMER_INT_VECT)
56b25f8b 46
dc41eb22 47#error Missing needed MM_... macro!
56b25f8b 48
93cb14d4
PH
49#endif
50
51/*
52 * Private global variables
53 */
93cb14d4 54
7c08d02a
PH
55#ifndef MM_USE_REGISTER_VARS
56
27b551dd 57static volatile uint8_t mm_bitno = 0;
7c08d02a
PH
58static uint8_t shift_command;
59static uint8_t shift_function;
60static uint8_t shift_address;
27b551dd
PH
61uint8_t mm_time_h = 0;
62uint8_t mm_time_l = 0;
63uint8_t mm_bit_val = 23;
64static uint8_t mm_flavor;
65static uint8_t mm_polarity = 1;
66static uint8_t mm_last_was_short = 0;
7c08d02a
PH
67
68#endif
93cb14d4
PH
69
70/*
71 * Lookup trinary nibble
72 *
73 * This was implemented using a switch statement before.
74 * Changing the lookup to a table did only add two bytes
75 * of memory and saved ca. 50 bytes program memory.
76 */
77static const uint8_t nibble_table[16]={
78 [0x0] = 0,
79 [0xc] = 1,
80 [0x8] = 2,
81 [0x3] = 3,
82 [0xf] = 4,
83 [0xb] = 5,
84 [0x2] = 6,
85 [0xe] = 7,
86 [0xa] = 8
87};
88#define lookup_nibble(nibble) nibble_table[nibble & 0xf]
89
b1a7e65e 90static uint8_t __attribute__((unused)) lookup_decoder(uint8_t mm_byte)
70095677 91{
56b25f8b
PH
92 uint8_t low;
93 uint8_t high;
27b551dd
PH
94 uint8_t retval;
95
56b25f8b 96 if (mm_byte == 0)
93cb14d4 97 return 80;
56b25f8b
PH
98 low = lookup_nibble(mm_byte >> 4);
99 high = lookup_nibble(mm_byte & 0xf);
100 if (!low)
70095677 101 return 0;
27b551dd
PH
102
103 /* retval = 9 * high + low; */
104 retval = high << 3;
105 retval += high;
106 retval += low;
107 return retval;
70095677
PH
108}
109
b1a7e65e 110static uint8_t __attribute__((unused)) lookup_command(uint8_t mm_command)
70095677 111{
7c08d02a
PH
112 uint8_t res;
113 /*
114 * Check for aabbccdd condition
115 *
116 * a a b b c c d d mm_command
117 * XOR a b b c c d d 0 mm_command << 1
118 * Mask 1 0 1 0 1 0 1 0 0xaa
119 *
120 * Must be zero!
121 *
122 */
123
124 if ((mm_command ^ (mm_command << 1)) & 0xaa)
70095677 125 return 0;
7c08d02a
PH
126 /*
127 * Protocol differences:
128 * =====================
129 *
130 * I have an old "central control" 6022 and a "control unit" 6021
131 * for measurements and test. It is assumed that the 6022 outputs
132 * old MM1 format while the 6021 definitively outputs MM2 telegrams.
133 *
134 * In MM1, switch commands are different from MM2 with respect what
135 * happens if you release a button.
136 *
137 * When you press a button, both protocols send
138 *
139 * <aaaaaaaa><00><aabbcc11>
140 *
141 * where a = 1, b = 2, c = 4 and the keys are numerated from 0 to 7
142 * in the order 1 red, 1 green, 2 red, 2 green and so on.
143 *
144 * The last two bits correspond to "on" state of the button/coil.
145 *
146 * When a key is released under MM1 protocol, the sequence sent is
147 * analogue to the button down sequence:
148 *
149 * <aaaaaaaa><00><aabbcc00> where abc again represents the button's
150 * address and the last bits now signal "off".
151 *
152 * MM2 handles this differently:
153 * Whenever any key from the addressed decoder is released, the sequence
154 * <aaaaaaaa>00<00000000> is sent - not only for key 0, but for all
155 * keys!
156 *
157 * While MM1 presents the theoretical possibility to press several keys
158 * independently and simultaneously (which my keyboard does NOT
159 * support), MM2 supports only one key at a time (besides strange
160 * sequences like "one down, another down, all up"...
161 *
162 * A decoder that strictly adheres to the MM1 standard would not work
163 * properly with MM2 control units. As far as I know all K83/K84
164 * decoders always worked with MM2 control units. That means that
165 * they reduce the commands to the possibilities of MM2 from the
166 * beginning.
167 *
168 * Possible use cases for the old protocol button release commands:
169 * - Determine if the protocol is MM1 or MM2
170 * - Implement hidden evil features into the controller which can
171 * only be summoned by old MM1 gear or selfmade control telegram
172 * generators.
173 *
174 * What this code now actually does:
175 * =================================
176 *
177 * When key pressed (aabbcc11), it will send out the key number in the
178 * range 1-8 and 0 if it gets any key up command and therefore ignore
179 * the key number if it is transmitted with the key up command.
180 *
181 */
182 if (!(mm_command & 0x01))
183 res = 0;
184 else
185 res = (mm_command & 0x80) * 1 + (mm_command & 0x20) * 0x02
186 + (mm_command & 0x08) * 0x04 + 1;
187 return res;
70095677
PH
188}
189
70095677
PH
190/* We will shift from right to left.
191 * XXXXXXXX XX XXXXXXXX
56b25f8b 192 * shift_address shift_function shift_command
70095677 193 *
56b25f8b 194 * The bits 7 downto 2 of shift_function are ignored.
27b551dd
PH
195 *
196 * First comes the C implementation of the shift routine.
197 * It is usually not used anymore, but I left it for
198 * illustration purposes.
199 * The real shift function is written in assembly and saves many instructions.
70095677 200 */
27b551dd
PH
201#if 0
202void shift(uint8_t value)
203{
204 shift_address <<= 1;
205 if (shift_function & 2)
206 shift_address |= 1;
207 shift_function <<= 1;
208 if (shift_command & 0x80)
209 shift_function |= 1;
210 shift_command <<= 1;
211 if (value)
212 shift_command |= 1;
213}
214#else
70095677 215
7c08d02a
PH
216void shift(uint8_t value)
217{
218 asm("ror %[val] ; Shift value right into carry\n\t"
219 "rol %[cmd] ; and shift to command reg\n\t"
220 "mov __tmp_reg__, %[func] ; save function value \n\t"
221 "rol %[func] ; Shift up function value\n\t"
222 "ror __tmp_reg__ ; shift bit 1\n\t"
223 "ror __tmp_reg__ ; down to carry\n\t"
224 "rol %[addr] ; And we're at the address\n\t"
225 : [cmd] "=r" (shift_command), [func] "=r" (shift_function),
226 [addr] "=r" (shift_address)
227 : "0" (shift_command), "1" (shift_function),
228 "2" (shift_address), [val] "r" (value)
229 );
230}
7c08d02a
PH
231#endif
232
27b551dd 233static void mm_feed_bit(uint8_t bit, uint8_t seen_style)
b1a7e65e 234{
93cb14d4
PH
235 static volatile uint8_t shift_command_first;
236 static volatile uint8_t shift_function_first;
237 static volatile uint8_t shift_address_first;
238 uint8_t address;
239 uint8_t command;
240
27b551dd
PH
241 if (mm_bitno == 0)
242 mm_flavor = seen_style;
b1a7e65e 243 else
27b551dd
PH
244 if (seen_style != mm_flavor) {
245 mm_bitno = 0;
70095677
PH
246 return;
247 }
b1a7e65e
PH
248
249 shift(bit);
27b551dd
PH
250 mm_bitno++;
251 if (mm_bitno == 18) { /* Save first received word */
93cb14d4
PH
252 shift_address_first = shift_address;
253 shift_function_first = shift_function;
254 shift_command_first = shift_command;
70095677 255 }
56b25f8b 256
27b551dd 257 if (mm_bitno == 36) {
b1a7e65e 258
56b25f8b 259 if ((shift_command == shift_command_first) &&
93cb14d4
PH
260 (shift_address == shift_address_first) &&
261 (shift_function == shift_function_first)) {
27b551dd
PH
262 address = lookup_decoder(shift_address);
263 if (mm_flavor == MM_FLAVOR_DRIVE) {
264 mm_switch_drive(address, shift_function,
265 shift_command);
266 } else {
267 trigger();
268 command = lookup_command(shift_command);
269 mm_switch_command(address, command);
270 }
70095677 271 }
56b25f8b 272
70095677
PH
273 }
274}
275
b1a7e65e
PH
276/*
277 * The timeout interrupt vector does nothing else
27b551dd 278 * than incrementing the mm_time_h round counter.
b1a7e65e
PH
279 *
280 * It is written in naked assembly because we want to avoid pushing
281 * and popping of all upper registers.
282 */
283void __attribute__((naked)) MM_TIMER_INT_VECT(void)
70095677 284{
b1a7e65e
PH
285 asm("push r0 ; save r0 \n\t"
286 "in r0, __SREG__ \n\t"
287#ifdef MM_USE_REGISTER_VARS
288 "inc %[th] \n\t"
289#else
290 "push r1 \n\t"
27b551dd 291 "lds r1, mm_time_h \n\t"
b1a7e65e 292 "inc r1 \n\t"
27b551dd 293 "sts mm_time_h, r1 \n\t"
b1a7e65e
PH
294 "pop r1 \n\t"
295#endif
296 "out __SREG__, r0 \n\t"
297 "pop r0 \n\t"
b1a7e65e
PH
298 "reti \n\t"
299#ifdef MM_USE_REGISTER_VARS
27b551dd 300 :: [th] "r" (mm_time_h)
b1a7e65e
PH
301#endif
302 );
70095677
PH
303}
304
b1a7e65e
PH
305/*
306 * Another naked interrupt trampoline
307 *
308 * Here we first save the timer value as fast as possible, then we jump (!)
309 * into the "official" interrupt handler with all its decorations.
310 */
311void __attribute__((naked)) PCINT0_vect(void)
70095677 312{
b1a7e65e
PH
313#ifdef MM_USE_REGISTER_VARS
314 asm("in %[tl], %[tmr] \n\t"
315 "rjmp __vector_pinchange \n\t"
27b551dd 316 :: [tl] "r" (mm_time_l), [tmr] "I" (_SFR_IO_ADDR(TCNT0))
b1a7e65e
PH
317 );
318#else
319 asm("push r0 \n\t"
320 "in r0, %[tmr] \n\t"
27b551dd 321 "sts mm_time_l, r0 \n\t"
b1a7e65e
PH
322 "pop r0 \n\t"
323 "rjmp __vector_pinchange \n\t"
324 :: [tmr] "I" (_SFR_IO_ADDR(TCNT0))
325 );
326#endif
327}
328
329/* Pin change interrupt vector, here we have a bit more time */
330ISR(__vector_pinchange){
331 /* First kill off that timer */
b1a7e65e
PH
332 MM_TSTART; /* Restart timer */
333
334 /* Account for not yet handled timer overflow */
335 if (TIFR & _BV(TOV0)) {
27b551dd 336 mm_time_h++;
b1a7e65e 337 TIFR |= _BV(TOV0);
70095677
PH
338 }
339
27b551dd 340 mm_bit_val = !MM_SENSE;
b1a7e65e 341
27b551dd
PH
342 uint16_t duration = mm_time_h << 8;
343 duration += mm_time_l;
344 mm_time_h = 0;
b1a7e65e
PH
345
346 /*
347 * The nominal length of a bit cycle is 208 us.
348 * This consists of 8 parts, each 26 us:
349 * 1 d d d d d d 0
350 *
351 * That means that the 1 pulse is 7 * 26 = 182us
352 * and the short pulse is 1 * 26 = 26us.
353 *
354 * Reality seems to look not as that exact. I measure
355 * 26us for the clock pulse, but 196 for the long pulse.
356 *
357 */
358
359#define D_MATCH(d, v) ((duration > (d * 2 - 2 * v)) && (duration < (d * 2 + 2 * v)))
360
b1a7e65e 361 if (D_MATCH(26, 4)) {
27b551dd
PH
362 if (mm_last_was_short)
363 mm_polarity = mm_bit_val;
364 mm_last_was_short = 1;
d0047978 365
27b551dd
PH
366 if (mm_bit_val == mm_polarity)
367 mm_feed_bit(0, MM_FLAVOR_DRIVE);
b1a7e65e 368 } else {
27b551dd 369 mm_last_was_short = 0;
b1a7e65e 370 }
d0047978 371
27b551dd
PH
372 if (mm_flavor == MM_FLAVOR_DRIVE) {
373 if (duration > 4096) { /* Maerklin inter package timeout 2ms */
374 mm_bitno = 0;
b1a7e65e
PH
375 goto done;
376 }
377 } else {
378 if (duration > 2000) { /* Maerklin inter package timeout 1ms */
27b551dd 379 mm_bitno = 0;
b1a7e65e 380 goto done;
d0047978
PH
381 }
382 }
b1a7e65e 383
27b551dd 384 if (mm_bit_val != mm_polarity)
b1a7e65e
PH
385 goto done;
386
27b551dd
PH
387 if (D_MATCH(182, 25)) mm_feed_bit(1, MM_FLAVOR_DRIVE);
388 if (D_MATCH(91, 17)) mm_feed_bit(1, MM_FLAVOR_SWITCH);
389 if (D_MATCH(13, 4)) mm_feed_bit(0, MM_FLAVOR_SWITCH);
b1a7e65e 390 done:
d0047978
PH
391 mm_switch_pinchange_callback();
392 }
9c77e706 393
27b551dd 394
d0047978
PH
395void __attribute__((weak))mm_switch_pinchange_callback(void)
396{
70095677
PH
397}
398
93cb14d4
PH
399void __attribute__((weak))mm_switch_drive(uint8_t decoder, uint8_t function,
400 uint8_t command)
401{
93cb14d4
PH
402}
403
404void __attribute__((weak))mm_switch_command(uint8_t address, uint8_t command)
405{
406}
407
408
70095677
PH
409/******************************************************************************
410 * The end :-)
411 */