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) || !defined(MM_SENSE) || !defined(MM_TIMER_INT_VECT)
51 #error Missing needed MM_... macro!
56 * Private global variables
59 #ifndef MM_USE_REGISTER_VARS
61 static volatile uint8_t bitno
= 0;
62 static uint8_t shift_command
;
63 static uint8_t shift_function
;
64 static uint8_t shift_address
;
65 static enum mm_recstate recstate
= MM_IDLE
;
70 * Lookup trinary nibble
72 * This was implemented using a switch statement before.
73 * Changing the lookup to a table did only add two bytes
74 * of memory and saved ca. 50 bytes program memory.
76 static const uint8_t nibble_table
[16]={
87 #define lookup_nibble(nibble) nibble_table[nibble & 0xf]
89 static uint8_t lookup_decoder(uint8_t mm_byte
)
95 low
= lookup_nibble(mm_byte
>> 4);
96 high
= lookup_nibble(mm_byte
& 0xf);
99 return 9 * high
+ low
;
102 static uint8_t lookup_command(uint8_t mm_command
)
106 * Check for aabbccdd condition
108 * a a b b c c d d mm_command
109 * XOR a b b c c d d 0 mm_command << 1
110 * Mask 1 0 1 0 1 0 1 0 0xaa
116 if ((mm_command
^ (mm_command
<< 1)) & 0xaa)
119 * Protocol differences:
120 * =====================
122 * I have an old "central control" 6022 and a "control unit" 6021
123 * for measurements and test. It is assumed that the 6022 outputs
124 * old MM1 format while the 6021 definitively outputs MM2 telegrams.
126 * In MM1, switch commands are different from MM2 with respect what
127 * happens if you release a button.
129 * When you press a button, both protocols send
131 * <aaaaaaaa><00><aabbcc11>
133 * where a = 1, b = 2, c = 4 and the keys are numerated from 0 to 7
134 * in the order 1 red, 1 green, 2 red, 2 green and so on.
136 * The last two bits correspond to "on" state of the button/coil.
138 * When a key is released under MM1 protocol, the sequence sent is
139 * analogue to the button down sequence:
141 * <aaaaaaaa><00><aabbcc00> where abc again represents the button's
142 * address and the last bits now signal "off".
144 * MM2 handles this differently:
145 * Whenever any key from the addressed decoder is released, the sequence
146 * <aaaaaaaa>00<00000000> is sent - not only for key 0, but for all
149 * While MM1 presents the theoretical possibility to press several keys
150 * independently and simultaneously (which my keyboard does NOT
151 * support), MM2 supports only one key at a time (besides strange
152 * sequences like "one down, another down, all up"...
154 * A decoder that strictly adheres to the MM1 standard would not work
155 * properly with MM2 control units. As far as I know all K83/K84
156 * decoders always worked with MM2 control units. That means that
157 * they reduce the commands to the possibilities of MM2 from the
160 * Possible use cases for the old protocol button release commands:
161 * - Determine if the protocol is MM1 or MM2
162 * - Implement hidden evil features into the controller which can
163 * only be summoned by old MM1 gear or selfmade control telegram
166 * What this code now actually does:
167 * =================================
169 * When key pressed (aabbcc11), it will send out the key number in the
170 * range 1-8 and 0 if it gets any key up command and therefore ignore
171 * the key number if it is transmitted with the key up command.
174 if (!(mm_command
& 0x01))
177 res
= (mm_command
& 0x80) * 1 + (mm_command
& 0x20) * 0x02
178 + (mm_command
& 0x08) * 0x04 + 1;
183 /* We will shift from right to left.
184 * XXXXXXXX XX XXXXXXXX
185 * shift_address shift_function shift_command
187 * The bits 7 downto 2 of shift_function are ignored.
189 #define SAVE_ANOTHER_40_BYTES
190 #ifdef SAVE_ANOTHER_40_BYTES
192 void shift(uint8_t value
)
194 asm("ror %[val] ; Shift value right into carry\n\t"
195 "rol %[cmd] ; and shift to command reg\n\t"
196 "mov __tmp_reg__, %[func] ; save function value \n\t"
197 "rol %[func] ; Shift up function value\n\t"
198 "ror __tmp_reg__ ; shift bit 1\n\t"
199 "ror __tmp_reg__ ; down to carry\n\t"
200 "rol %[addr] ; And we're at the address\n\t"
201 : [cmd
] "=r" (shift_command
), [func
] "=r" (shift_function
),
202 [addr
] "=r" (shift_address
)
203 : "0" (shift_command
), "1" (shift_function
),
204 "2" (shift_address
), [val
] "r" (value
)
208 #else /* This is what we do to shift */
210 void shift(uint8_t value
)
213 if (shift_function
& 2)
215 shift_function
<<= 1;
216 if (shift_command
& 0x80)
224 static volatile uint8_t mm_rec_tolerated_timeouts
;
227 ISR(MM_TIMER_INT_VECT
) {
229 static volatile uint8_t shift_command_first
;
230 static volatile uint8_t shift_function_first
;
231 static volatile uint8_t shift_address_first
;
235 #ifdef MM_FILTER_REPEATED
236 static uint8_t address_last
= 0xff;
237 static uint8_t function_last
= 0xff;
238 static uint8_t command_last
= 0xff;
244 case MM_FIRST_FAST_SAMPLE
:
245 recstate
= MM_FIRST_SLOW_SAMPLE
;
248 case MM_FIRST_SLOW_SAMPLE
:
252 recstate
= MM_SLOW_WAIT_FOR_CLOCK_DELAY
;
256 recstate
= MM_FAST_WAIT_FOR_CLOCK
;
259 case MM_FAST_WAIT_FOR_CLOCK
: /* A timeout! */
260 if (mm_rec_tolerated_timeouts
) {
261 mm_rec_tolerated_timeouts
--;
268 case MM_SLOW_SAMPLE_DELAY
:
269 recstate
= MM_SLOW_SAMPLE
;
272 case MM_SLOW_WAIT_FOR_CLOCK_DELAY
:
273 recstate
= MM_SLOW_WAIT_FOR_CLOCK
;
276 case MM_SLOW_WAIT_FOR_CLOCK
:
277 if (mm_rec_tolerated_timeouts
) {
279 mm_rec_tolerated_timeouts
--;
280 recstate
= MM_SLOW_WAIT_FOR_CLOCK_DELAY
;
292 if (bitno
== 18) { /* Save first received word */
293 shift_address_first
= shift_address
;
294 shift_function_first
= shift_function
;
295 shift_command_first
= shift_command
;
296 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;
397 case MM_SLOW_SAMPLE_DELAY
:
398 recstate
= MM_SLOW_SAMPLE
;
401 case MM_FIRST_FAST_SAMPLE
:
410 void __attribute__((weak
))mm_switch_drive(uint8_t decoder
, uint8_t function
,
416 void __attribute__((weak
))mm_switch_command(uint8_t address
, uint8_t command
)
421 /******************************************************************************