35016fcd4769d6826847d12ef697b992bdfcd7e0
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>
42 * Check for stuff we need
45 #if !defined(MM_TSTART) || !defined(MM_SENSE) || !defined(MM_TIMER_INT_VECT)
46 #error Missing needed MM_... macro!
50 * Private global variables
60 enum recmode recmode
= INIT
;
62 #ifndef MM_USE_REGISTER_VARS
64 static volatile uint8_t mm_bitno
= 0;
65 static uint8_t shift_command
;
66 static uint8_t shift_function
;
67 static uint8_t shift_address
;
68 uint8_t mm_time_h
= 0;
69 uint8_t mm_time_l
= 0;
70 uint8_t mm_bit_val
= 23;
71 static uint8_t mm_flavor
;
72 static uint8_t mm_polarity
= 1;
77 * Lookup trinary nibble
79 * This was implemented using a switch statement before.
80 * Changing the lookup to a table did only add two bytes
81 * of memory and saved ca. 50 bytes program memory.
83 static const uint8_t nibble_table
[16]={
94 #define lookup_nibble(nibble) nibble_table[nibble & 0xf]
96 static uint8_t __attribute__((unused
)) lookup_decoder(uint8_t mm_byte
)
104 low
= lookup_nibble(mm_byte
>> 4);
105 high
= lookup_nibble(mm_byte
& 0xf);
109 /* retval = 9 * high + low; */
116 static uint8_t __attribute__((unused
)) lookup_command(uint8_t mm_command
)
120 * Check for aabbccdd condition
122 * a a b b c c d d mm_command
123 * XOR a b b c c d d 0 mm_command << 1
124 * Mask 1 0 1 0 1 0 1 0 0xaa
130 if ((mm_command
^ (mm_command
<< 1)) & 0xaa)
133 * Protocol differences:
134 * =====================
136 * I have an old "central control" 6022 and a "control unit" 6021
137 * for measurements and test. It is assumed that the 6022 outputs
138 * old MM1 format while the 6021 definitively outputs MM2 telegrams.
140 * In MM1, switch commands are different from MM2 with respect what
141 * happens if you release a button.
143 * When you press a button, both protocols send
145 * <aaaaaaaa><00><aabbcc11>
147 * where a = 1, b = 2, c = 4 and the keys are numerated from 0 to 7
148 * in the order 1 red, 1 green, 2 red, 2 green and so on.
150 * The last two bits correspond to "on" state of the button/coil.
152 * When a key is released under MM1 protocol, the sequence sent is
153 * analogue to the button down sequence:
155 * <aaaaaaaa><00><aabbcc00> where abc again represents the button's
156 * address and the last bits now signal "off".
158 * MM2 handles this differently:
159 * Whenever any key from the addressed decoder is released, the sequence
160 * <aaaaaaaa>00<00000000> is sent - not only for key 0, but for all
163 * While MM1 presents the theoretical possibility to press several keys
164 * independently and simultaneously (which my keyboard does NOT
165 * support), MM2 supports only one key at a time (besides strange
166 * sequences like "one down, another down, all up"...
168 * A decoder that strictly adheres to the MM1 standard would not work
169 * properly with MM2 control units. As far as I know all K83/K84
170 * decoders always worked with MM2 control units. That means that
171 * they reduce the commands to the possibilities of MM2 from the
174 * Possible use cases for the old protocol button release commands:
175 * - Determine if the protocol is MM1 or MM2
176 * - Implement hidden evil features into the controller which can
177 * only be summoned by old MM1 gear or selfmade control telegram
180 * What this code now actually does:
181 * =================================
183 * When key pressed (aabbcc11), it will send out the key number in the
184 * range 1-8 and 0 if it gets any key up command and therefore ignore
185 * the key number if it is transmitted with the key up command.
188 if (!(mm_command
& 0x01))
191 res
= (mm_command
& 0x80) * 1 + (mm_command
& 0x20) * 0x02
192 + (mm_command
& 0x08) * 0x04 + 1;
196 /* We will shift from right to left.
197 * XXXXXXXX XX XXXXXXXX
198 * shift_address shift_function shift_command
200 * The bits 7 downto 2 of shift_function are ignored.
202 * First comes the C implementation of the shift routine.
203 * It is usually not used anymore, but I left it for
204 * illustration purposes.
205 * The real shift function is written in assembly and saves many instructions.
208 void shift(uint8_t value
)
211 if (shift_function
& 2)
213 shift_function
<<= 1;
214 if (shift_command
& 0x80)
222 void shift(uint8_t value
)
224 asm("ror %[val] ; Shift value right into carry\n\t"
225 "rol %[cmd] ; and shift to command reg\n\t"
226 "mov __tmp_reg__, %[func] ; save function value \n\t"
227 "rol %[func] ; Shift up function value\n\t"
228 "ror __tmp_reg__ ; shift bit 1\n\t"
229 "ror __tmp_reg__ ; down to carry\n\t"
230 "rol %[addr] ; And we're at the address\n\t"
231 : [cmd
] "=r" (shift_command
), [func
] "=r" (shift_function
),
232 [addr
] "=r" (shift_address
)
233 : "0" (shift_command
), "1" (shift_function
),
234 "2" (shift_address
), [val
] "r" (value
)
239 static void mm_feed_bit(uint8_t bit
)
241 static volatile uint8_t shift_command_first
;
242 static volatile uint8_t shift_function_first
;
243 static volatile uint8_t shift_address_first
;
250 if (mm_bitno
== 18) { /* Save first received word */
251 shift_address_first
= shift_address
;
252 shift_function_first
= shift_function
;
253 shift_command_first
= shift_command
;
256 if (mm_bitno
== 36) {
258 if ((shift_command
== shift_command_first
) &&
259 (shift_address
== shift_address_first
) &&
260 (shift_function
== shift_function_first
)) {
261 address
= lookup_decoder(shift_address
);
262 if (recmode
== MM_SLOW
) {
265 mm_switch_drive(address
, shift_function
,
268 command
= lookup_command(shift_command
);
269 mm_switch_command(address
, command
);
277 * The timeout interrupt vector does nothing else
278 * than incrementing the mm_time_h round counter.
280 * It is written in naked assembly because we want to avoid pushing
281 * and popping of all upper registers.
283 void __attribute__((naked
)) MM_TIMER_INT_VECT(void)
285 asm("push r0 ; save r0 \n\t"
286 "in r0, __SREG__ \n\t"
287 #ifdef MM_USE_REGISTER_VARS
294 "lds r1, mm_time_h \n\t"
299 "sts mm_time_h, r1 \n\t"
302 "out __SREG__, r0 \n\t"
305 #ifdef MM_USE_REGISTER_VARS
306 :: [th
] "r" (mm_time_h
)
312 * Another naked interrupt trampoline
314 * Here we first save the timer value as fast as possible, then we jump (!)
315 * into the "official" interrupt handler with all its decorations.
317 void __attribute__((naked
)) PCINT0_vect(void)
319 #ifdef MM_USE_REGISTER_VARS
320 asm("in %[tl], %[tmr] \n\t"
321 "rjmp __vector_pinchange \n\t"
322 :: [tl
] "r" (mm_time_l
), [tmr
] "I" (_SFR_IO_ADDR(TCNT0
))
327 "sts mm_time_l, r0 \n\t"
329 "rjmp __vector_pinchange \n\t"
330 :: [tmr
] "I" (_SFR_IO_ADDR(TCNT0
))
335 /* Pin change interrupt vector, here we have a bit more time */
336 ISR(__vector_pinchange
){
339 /* First kill off that timer */
340 MM_TSTART
; /* Restart timer */
342 /* Account for not yet handled timer overflow */
345 duration
= mm_time_h
<< 8;
346 duration
+= mm_time_l
;
347 mm_bit_val
= MM_SENSE
;
351 * The nominal length of a bit cycle is 208 us.
352 * This consists of 8 parts, each 26 us:
355 * That means that the 1 pulse is 7 * 26 = 182us
356 * and the short pulse is 1 * 26 = 26us.
358 * Reality seems to look not as that exact. I measure
359 * 26us for the clock pulse, but 196 for the long pulse.
361 * Keep in mind: Time is counted in 500ns steps.
364 #define D_MATCH(d, v) ((duration > (d * 2 - 2 * v)) && (duration < (d * 2 + 2 * v)))
371 /* Maerklin only interested when signal is 1 */
372 if (mm_bit_val
== mm_polarity
) {
373 /* Fast short MM pulse (logical 0) */
380 /* Slow short MM pulse (logical 0) */
381 if (D_MATCH(26, 4)) {
387 /* Fast long MM pulse (logical 1) */
388 if (D_MATCH(91, 17)) {
394 /* Slow long MM pulse (logical 1) */
395 if (D_MATCH(182, 25)) {
404 if (mm_bit_val
== mm_polarity
) {
406 /* Fast short MM pulse (logical 0) */
411 /* Fast long MM pulse (logical 1) */
412 if (D_MATCH(91, 17)) {
422 if (D_MATCH(700, 200))
428 if (mm_bit_val
== mm_polarity
) {
429 /* Slow short MM pulse (logical 0) */
430 if (D_MATCH(26, 4)) {
434 /* Slow long MM pulse (logical 1) */
435 if (D_MATCH(182, 40)) {
442 if (D_MATCH(182, 40))
445 /* Accepted inter package gap */
446 if (D_MATCH(1400, 400))
453 * If we have reached here, our pulse comes in somehow unexpected.
454 * We kill of everything by re-arming the state machine.
456 /* Start over receiver */
458 mm_polarity
= !mm_bit_val
;
461 mm_switch_pinchange_callback();
465 void __attribute__((weak
))mm_switch_pinchange_callback(void)
469 void __attribute__((weak
))mm_switch_drive(uint8_t decoder
, uint8_t function
,
474 void __attribute__((weak
))mm_switch_command(uint8_t address
, uint8_t command
)
479 /******************************************************************************