trennfix/sw: Register variables, restructuring, whatever
[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 */
93cb14d4
PH
49#if !defined(MM_TSTART_FAST) || !defined(MM_TSTART_SLOW) || !defined(MM_TSTOP) \
50 || !defined(MM_SENSE) || !defined(MM_TIMER_INT_VECT)
56b25f8b 51
93cb14d4 52#error Missing timer start macro MM_TSTART_FAST!
56b25f8b 53
93cb14d4
PH
54#endif
55
56/*
57 * Private global variables
58 */
93cb14d4 59
7c08d02a
PH
60#ifndef MM_USE_REGISTER_VARS
61
62static volatile uint8_t bitno = 0;
63static uint8_t shift_command;
64static uint8_t shift_function;
65static uint8_t shift_address;
66static enum mm_recstate recstate = MM_IDLE;
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
70095677
PH
90static uint8_t lookup_decoder(uint8_t mm_byte)
91{
56b25f8b
PH
92 uint8_t low;
93 uint8_t high;
94 if (mm_byte == 0)
93cb14d4 95 return 80;
56b25f8b
PH
96 low = lookup_nibble(mm_byte >> 4);
97 high = lookup_nibble(mm_byte & 0xf);
98 if (!low)
70095677 99 return 0;
56b25f8b 100 return 9 * high + low;
70095677
PH
101}
102
7c08d02a 103static uint8_t lookup_command(uint8_t mm_command)
70095677 104{
7c08d02a
PH
105 uint8_t res;
106 /*
107 * Check for aabbccdd condition
108 *
109 * a a b b c c d d mm_command
110 * XOR a b b c c d d 0 mm_command << 1
111 * Mask 1 0 1 0 1 0 1 0 0xaa
112 *
113 * Must be zero!
114 *
115 */
116
117 if ((mm_command ^ (mm_command << 1)) & 0xaa)
70095677 118 return 0;
7c08d02a
PH
119 /*
120 * Protocol differences:
121 * =====================
122 *
123 * I have an old "central control" 6022 and a "control unit" 6021
124 * for measurements and test. It is assumed that the 6022 outputs
125 * old MM1 format while the 6021 definitively outputs MM2 telegrams.
126 *
127 * In MM1, switch commands are different from MM2 with respect what
128 * happens if you release a button.
129 *
130 * When you press a button, both protocols send
131 *
132 * <aaaaaaaa><00><aabbcc11>
133 *
134 * where a = 1, b = 2, c = 4 and the keys are numerated from 0 to 7
135 * in the order 1 red, 1 green, 2 red, 2 green and so on.
136 *
137 * The last two bits correspond to "on" state of the button/coil.
138 *
139 * When a key is released under MM1 protocol, the sequence sent is
140 * analogue to the button down sequence:
141 *
142 * <aaaaaaaa><00><aabbcc00> where abc again represents the button's
143 * address and the last bits now signal "off".
144 *
145 * MM2 handles this differently:
146 * Whenever any key from the addressed decoder is released, the sequence
147 * <aaaaaaaa>00<00000000> is sent - not only for key 0, but for all
148 * keys!
149 *
150 * While MM1 presents the theoretical possibility to press several keys
151 * independently and simultaneously (which my keyboard does NOT
152 * support), MM2 supports only one key at a time (besides strange
153 * sequences like "one down, another down, all up"...
154 *
155 * A decoder that strictly adheres to the MM1 standard would not work
156 * properly with MM2 control units. As far as I know all K83/K84
157 * decoders always worked with MM2 control units. That means that
158 * they reduce the commands to the possibilities of MM2 from the
159 * beginning.
160 *
161 * Possible use cases for the old protocol button release commands:
162 * - Determine if the protocol is MM1 or MM2
163 * - Implement hidden evil features into the controller which can
164 * only be summoned by old MM1 gear or selfmade control telegram
165 * generators.
166 *
167 * What this code now actually does:
168 * =================================
169 *
170 * When key pressed (aabbcc11), it will send out the key number in the
171 * range 1-8 and 0 if it gets any key up command and therefore ignore
172 * the key number if it is transmitted with the key up command.
173 *
174 */
175 if (!(mm_command & 0x01))
176 res = 0;
177 else
178 res = (mm_command & 0x80) * 1 + (mm_command & 0x20) * 0x02
179 + (mm_command & 0x08) * 0x04 + 1;
180 return res;
70095677
PH
181}
182
7c08d02a 183
70095677
PH
184/* We will shift from right to left.
185 * XXXXXXXX XX XXXXXXXX
56b25f8b 186 * shift_address shift_function shift_command
70095677 187 *
56b25f8b 188 * The bits 7 downto 2 of shift_function are ignored.
70095677 189 */
7c08d02a
PH
190#define SAVE_ANOTHER_40_BYTES
191#ifdef SAVE_ANOTHER_40_BYTES
70095677 192
7c08d02a
PH
193void shift(uint8_t value)
194{
195 asm("ror %[val] ; Shift value right into carry\n\t"
196 "rol %[cmd] ; and shift to command reg\n\t"
197 "mov __tmp_reg__, %[func] ; save function value \n\t"
198 "rol %[func] ; Shift up function value\n\t"
199 "ror __tmp_reg__ ; shift bit 1\n\t"
200 "ror __tmp_reg__ ; down to carry\n\t"
201 "rol %[addr] ; And we're at the address\n\t"
202 : [cmd] "=r" (shift_command), [func] "=r" (shift_function),
203 [addr] "=r" (shift_address)
204 : "0" (shift_command), "1" (shift_function),
205 "2" (shift_address), [val] "r" (value)
206 );
207}
208
209#else /* This is what we do to shift */
210
211void shift(uint8_t value)
70095677 212{
56b25f8b
PH
213 shift_address <<= 1;
214 if (shift_function & 2)
215 shift_address |= 1;
216 shift_function <<= 1;
217 if (shift_command & 0x80)
218 shift_function |= 1;
219 shift_command <<= 1;
70095677 220 if (value)
56b25f8b 221 shift_command |= 1;
70095677 222}
7c08d02a
PH
223#endif
224
70095677 225
93cb14d4
PH
226ISR(MM_TIMER_INT_VECT) {
227 static uint8_t tolerated_timeouts = 0;
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
PH
240
241 switch(recstate) {
7c08d02a
PH
242 case MM_FIRST_FAST_SAMPLE:
243 recstate = MM_FIRST_SLOW_SAMPLE;
93cb14d4 244 MM_TSTART_FAST; /* Will not run out in fast! */
70095677
PH
245 break;
246
7c08d02a 247 case MM_FIRST_SLOW_SAMPLE:
70095677
PH
248 bitno = 0;
249
7c08d02a
PH
250 case MM_SLOW_SAMPLE:
251 recstate = MM_SLOW_WAIT_FOR_CLOCK;
93cb14d4 252 MM_TSTART_SLOW;
70095677
PH
253 break;
254
7c08d02a
PH
255 case MM_FAST_SAMPLE:
256 recstate = MM_FAST_WAIT_FOR_CLOCK;
93cb14d4 257 MM_TSTART_FAST;
70095677
PH
258 break;
259
7c08d02a 260 case MM_FAST_WAIT_FOR_CLOCK: /* A timeout! */
93cb14d4
PH
261 if (tolerated_timeouts) {
262 tolerated_timeouts--;
263 MM_TSTART_FAST;
70095677
PH
264 return;
265 }
7c08d02a 266 recstate = MM_IDLE;
93cb14d4 267 MM_TSTOP;
70095677
PH
268 return;
269
7c08d02a 270 case MM_SLOW_WAIT_FOR_CLOCK:
93cb14d4
PH
271 if (tolerated_timeouts) {
272 tolerated_timeouts--;
273 MM_TSTART_SLOW;
70095677
PH
274 return;
275 }
56b25f8b 276 default:
93cb14d4 277 MM_TSTOP;
7c08d02a 278 recstate = MM_IDLE;
70095677
PH
279 return;
280 }
281
282 shift(MM_SENSE);
283 bitno++;
56b25f8b 284
70095677 285 if (bitno == 18) { /* Save first received word */
93cb14d4
PH
286 shift_address_first = shift_address;
287 shift_function_first = shift_function;
288 shift_command_first = shift_command;
70095677 289
93cb14d4 290 tolerated_timeouts = 18;
70095677 291 }
56b25f8b 292
70095677 293 if (bitno == 36) {
56b25f8b 294 if ((shift_command == shift_command_first) &&
93cb14d4
PH
295 (shift_address == shift_address_first) &&
296 (shift_function == shift_function_first)) {
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);
56b25f8b 303
7c08d02a 304 if (recstate == MM_SLOW_WAIT_FOR_CLOCK) {
93cb14d4 305 mm_switch_drive(address, shift_function, shift_command);
7c08d02a 306 } else if (recstate == MM_FAST_WAIT_FOR_CLOCK) {
93cb14d4
PH
307 command = lookup_command(shift_command);
308 mm_switch_command(address, command);
70095677 309 }
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
70095677
PH
351/* Pin change interrupt vector */
352void mm_pinchange_handler(void)
353{
354 static uint8_t sense_last;
355
356 if (MM_SENSE == sense_last)
357 return;
358 sense_last = MM_SENSE;
359 if (!sense_last)
360 return;
56b25f8b 361
70095677 362 switch(recstate) {
7c08d02a 363 case MM_IDLE:
70095677 364 bitno = 0;
7c08d02a 365 recstate = MM_FIRST_FAST_SAMPLE;
93cb14d4 366 MM_TSTART_FAST;
70095677 367 break;
7c08d02a
PH
368 case MM_FIRST_SLOW_SAMPLE:
369 recstate = MM_FAST_SAMPLE;
93cb14d4 370 MM_TSTART_FAST;
70095677 371 break;
7c08d02a
PH
372 case MM_FAST_WAIT_FOR_CLOCK:
373 recstate = MM_FAST_SAMPLE;
93cb14d4 374 MM_TSTART_FAST;
70095677 375 break;
7c08d02a
PH
376 case MM_SLOW_WAIT_FOR_CLOCK:
377 recstate = MM_SLOW_SAMPLE;
93cb14d4 378 MM_TSTART_SLOW;
70095677
PH
379 break;
380
381 /* Not expected */
7c08d02a
PH
382 case MM_FIRST_FAST_SAMPLE:
383 case MM_FAST_SAMPLE:
384 case MM_SLOW_SAMPLE:
70095677
PH
385 default:
386 break;
387 }
388}
389
93cb14d4
PH
390void __attribute__((weak))mm_switch_drive(uint8_t decoder, uint8_t function,
391 uint8_t command)
392{
393 while(1);
394}
395
396void __attribute__((weak))mm_switch_command(uint8_t address, uint8_t command)
397{
398}
399
400
70095677
PH
401/******************************************************************************
402 * The end :-)
403 */
404
405
406