1 /******************************************************************************
3 * Trennfix firmware - mm_switch.c
5 * Maerklin Motorola switch command receiver
7 * Copyright (C) 2017 Philipp Hachtmann
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.
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.
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/>.
22 *****************************************************************************/
29 #include <avr/eeprom.h>
30 #include <avr/interrupt.h>
31 #include <avr/pgmspace.h>
33 #include <util/delay.h>
36 #include <config/hardware.h>
37 #include <mm/mm_switch.h>
46 * Check for stuff we need
49 #if !defined(MM_TSTART_FAST) || !defined(MM_TSTART_SLOW) || !defined(MM_TSTOP) \
50 || !defined(MM_SENSE) || !defined(MM_TIMER_INT_VECT)
52 #error Missing timer start macro MM_TSTART_FAST!
57 * Private global variables
60 #ifndef MM_USE_REGISTER_VARS
62 static volatile uint8_t bitno
= 0;
63 static uint8_t shift_command
;
64 static uint8_t shift_function
;
65 static uint8_t shift_address
;
66 static enum mm_recstate recstate
= MM_IDLE
;
71 * Lookup trinary nibble
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.
77 static const uint8_t nibble_table
[16]={
88 #define lookup_nibble(nibble) nibble_table[nibble & 0xf]
90 static uint8_t lookup_decoder(uint8_t mm_byte
)
96 low
= lookup_nibble(mm_byte
>> 4);
97 high
= lookup_nibble(mm_byte
& 0xf);
100 return 9 * high
+ low
;
103 static uint8_t lookup_command(uint8_t mm_command
)
107 * Check for aabbccdd condition
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
117 if ((mm_command
^ (mm_command
<< 1)) & 0xaa)
120 * Protocol differences:
121 * =====================
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.
127 * In MM1, switch commands are different from MM2 with respect what
128 * happens if you release a button.
130 * When you press a button, both protocols send
132 * <aaaaaaaa><00><aabbcc11>
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.
137 * The last two bits correspond to "on" state of the button/coil.
139 * When a key is released under MM1 protocol, the sequence sent is
140 * analogue to the button down sequence:
142 * <aaaaaaaa><00><aabbcc00> where abc again represents the button's
143 * address and the last bits now signal "off".
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
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"...
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
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
167 * What this code now actually does:
168 * =================================
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.
175 if (!(mm_command
& 0x01))
178 res
= (mm_command
& 0x80) * 1 + (mm_command
& 0x20) * 0x02
179 + (mm_command
& 0x08) * 0x04 + 1;
184 /* We will shift from right to left.
185 * XXXXXXXX XX XXXXXXXX
186 * shift_address shift_function shift_command
188 * The bits 7 downto 2 of shift_function are ignored.
190 #define SAVE_ANOTHER_40_BYTES
191 #ifdef SAVE_ANOTHER_40_BYTES
193 void shift(uint8_t value
)
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
)
209 #else /* This is what we do to shift */
211 void shift(uint8_t value
)
214 if (shift_function
& 2)
216 shift_function
<<= 1;
217 if (shift_command
& 0x80)
225 static volatile uint8_t mm_rec_tolerated_timeouts
;
228 ISR(MM_TIMER_INT_VECT
) {
230 static volatile uint8_t shift_command_first
;
231 static volatile uint8_t shift_function_first
;
232 static volatile uint8_t shift_address_first
;
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;
246 case MM_FIRST_FAST_SAMPLE
:
247 recstate
= MM_FIRST_SLOW_SAMPLE
;
250 case MM_FIRST_SLOW_SAMPLE
:
254 recstate
= MM_SLOW_WAIT_FOR_CLOCK_DELAY
;
258 recstate
= MM_FAST_WAIT_FOR_CLOCK
;
261 case MM_FAST_WAIT_FOR_CLOCK
: /* A timeout! */
262 if (mm_rec_tolerated_timeouts
) {
263 mm_rec_tolerated_timeouts
--;
270 case MM_SLOW_SAMPLE_DELAY
:
271 recstate
= MM_SLOW_SAMPLE
;
274 case MM_SLOW_WAIT_FOR_CLOCK_DELAY
:
275 recstate
= MM_SLOW_WAIT_FOR_CLOCK
;
278 case MM_SLOW_WAIT_FOR_CLOCK
:
279 if (mm_rec_tolerated_timeouts
) {
280 mm_rec_tolerated_timeouts
--;
281 recstate
= MM_SLOW_WAIT_FOR_CLOCK_DELAY
;
293 if (bitno
== 18) { /* Save first received word */
294 shift_address_first
= shift_address
;
295 shift_function_first
= shift_function
;
296 shift_command_first
= shift_command
;
297 mm_rec_tolerated_timeouts
= 18;
301 if ((shift_command
== shift_command_first
) &&
302 (shift_address
== shift_address_first
) &&
303 (shift_function
== shift_function_first
)) {
305 #ifdef MM_FILTER_REPEATED
306 if ((shift_address
!= address_last
) || (shift_command
!= command_last
) ||
307 shift_function
!= function_last
) {
309 address
= lookup_decoder(shift_address
);
311 if (recstate
== MM_SLOW_WAIT_FOR_CLOCK_DELAY
) {
313 mm_switch_drive(address
, shift_function
, shift_command
);
314 } else if (recstate
== MM_FAST_WAIT_FOR_CLOCK
) {
316 command
= lookup_command(shift_command
);
317 mm_switch_command(address
, command
);
319 #ifdef MM_FILTER_REPEATED
321 address_last
= shift_address
;
322 function_last
= shift_function
;
323 command_last
= shift_command
;
330 //void __attribute((weak)) mm_switch_drive(uint8_t address, uint8_t function, uint8_t command);
360 /* Pin change interrupt vector */
361 void mm_pinchange_handler(void)
363 static uint8_t sense_last
;
365 if (MM_SENSE
== sense_last
)
367 sense_last
= MM_SENSE
;
376 recstate
= MM_FIRST_FAST_SAMPLE
;
379 case MM_FIRST_SLOW_SAMPLE
:
380 recstate
= MM_FAST_SAMPLE
;
383 case MM_FAST_WAIT_FOR_CLOCK
:
384 recstate
= MM_FAST_SAMPLE
;
385 mm_rec_tolerated_timeouts
= 0;
388 case MM_SLOW_WAIT_FOR_CLOCK_DELAY
: /* If clock comes early */
389 recstate
= MM_SLOW_WAIT_FOR_CLOCK
;
392 case MM_SLOW_WAIT_FOR_CLOCK
:
393 recstate
= MM_SLOW_SAMPLE_DELAY
;
394 mm_rec_tolerated_timeouts
= 0;
398 case MM_FIRST_FAST_SAMPLE
:
407 void __attribute__((weak
))mm_switch_drive(uint8_t decoder
, uint8_t function
,
413 void __attribute__((weak
))mm_switch_command(uint8_t address
, uint8_t command
)
418 /******************************************************************************