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