a960355298ba8aac8f1354f862eebf1e34d70144
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>
38 #include <mm/mm_decode.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!
49 #ifndef MM_QUEUE_DEPTH
50 #define MM_QUEUE_DEPTH 8
55 * The nominal length of a bit cycle is 208 us.
56 * This consists of 8 parts, each 26 us:
59 * That means that the 1 pulse is 7 * 26 = 182us
60 * and the short pulse is 1 * 26 = 26us.
62 * Reality seems to look not as that exact. I measure
63 * 26us for the clock pulse, but 196 for the long pulse.
65 * Keep in mind: Time is counted in 500ns steps.
68 #define IN_RANGE(duration, lower, upper) \
69 ((duration >= (2 * lower) \
70 && (duration <= (2 * upper))))
72 #define MM_FAST_SHORT(duration) IN_RANGE(duration, 7, 18)
73 #define MM_FAST_LONG(duration) IN_RANGE(duration, 70, 130)
74 #define MM_FAST_GAP(duration) IN_RANGE(duration, 500, 1100)
76 #define MM_SLOW_SHORT(duration) IN_RANGE(duration, 18, 35)
77 #define MM_SLOW_LONG(duration) IN_RANGE(duration, 150, 210)
78 #define MM_SLOW_GAP(duration) IN_RANGE(duration, 1000, 2000)
80 #define MM_SUFFICIENT_IDLE(duration) (duration > 800)
84 static enum mm_recmode recmode
= __MM_INIT
;
87 static uint8_t queue_wpos
= 0;
88 static uint8_t queue_rpos
= 0;
89 static struct mm_command queue
[MM_QUEUE_DEPTH
];
92 #ifndef MM_USE_REGISTER_VARS
94 static volatile uint8_t mm_bitno
= 0;
95 static uint8_t shift_command
;
96 static uint8_t shift_function
;
97 static uint8_t shift_address
;
98 uint8_t mm_time_h
= 0;
99 uint8_t mm_time_l
= 0;
100 uint8_t mm_bit_val
= 23;
101 static uint8_t mm_flavor
;
102 static uint8_t mm_polarity
= 1;
106 /* We will shift from right to left.
107 * XXXXXXXX XX XXXXXXXX
108 * shift_address shift_function shift_command
110 * The bits 7 downto 2 of shift_function are ignored.
112 * First comes the C implementation of the shift routine.
113 * It is usually not used anymore, but I left it for
114 * illustration purposes.
115 * The real shift function is written in assembly and saves many instructions.
118 static void shift(uint8_t value
)
121 if (shift_function
& 2)
123 shift_function
<<= 1;
124 if (shift_command
& 0x80)
132 static void shift(uint8_t value
)
134 asm("ror %[val] ; Shift value right into carry\n\t"
135 "rol %[cmd] ; and shift to command reg\n\t"
136 "mov __tmp_reg__, %[func] ; save function value \n\t"
137 "rol %[func] ; Shift up function value\n\t"
138 "ror __tmp_reg__ ; shift bit 1\n\t"
139 "ror __tmp_reg__ ; down to carry\n\t"
140 "rol %[addr] ; And we're at the address\n\t"
141 : [cmd
] "=r" (shift_command
), [func
] "=r" (shift_function
),
142 [addr
] "=r" (shift_address
)
143 : "0" (shift_command
), "1" (shift_function
),
144 "2" (shift_address
), [val
] "r" (value
)
149 static void mm_feed_bit(uint8_t bit
)
151 static volatile uint8_t shift_command_first
;
152 static volatile uint8_t shift_function_first
;
153 static volatile uint8_t shift_address_first
;
159 if (mm_bitno
== 18) { /* Save first received word */
160 shift_address_first
= shift_address
;
161 shift_function_first
= shift_function
;
162 shift_command_first
= shift_command
;
165 if (mm_bitno
== 36) {
166 if ((shift_command
== shift_command_first
) &&
167 (shift_address
== shift_address_first
) &&
168 (shift_function
== shift_function_first
)) {
170 #ifdef MM_USE_CALLBACK
171 address
= mm_lookup_decoder(shift_address
);
172 if (recmode
== MM_SLOW
) {
173 mm_drive_cb(address
, shift_function
,
176 mm_key_cb(address
, shift_function
, shift_command
);
181 queue
[queue_wpos
].recmode
= recmode
;
182 queue
[queue_wpos
].address
= shift_address
;
183 queue
[queue_wpos
].function
= shift_function
;
184 queue
[queue_wpos
].command
= shift_command
;
185 queue_wpos
= (queue_wpos
+ 1) % MM_QUEUE_DEPTH
;
192 struct mm_command
*mm_get(void)
194 struct mm_command
*result
= NULL
;
195 uint8_t sreg_bak
= SREG
;
197 if (queue_rpos
!= queue_wpos
) {
198 result
= &queue
[queue_rpos
];
199 queue_rpos
= (queue_rpos
+ 1) % MM_QUEUE_DEPTH
;
207 * The timeout interrupt vector does nothing else
208 * than incrementing the mm_time_h round counter.
210 * It is written in naked assembly because we want to avoid pushing
211 * and popping of all upper registers.
213 void __attribute__((naked
)) MM_TIMER_INT_VECT(void)
215 asm("push r0 ; save r0 \n\t"
216 "in r0, __SREG__ \n\t"
217 #ifdef MM_USE_REGISTER_VARS
224 "lds r1, mm_time_h \n\t"
229 "sts mm_time_h, r1 \n\t"
232 "out __SREG__, r0 \n\t"
235 #ifdef MM_USE_REGISTER_VARS
236 :: [th
] "r" (mm_time_h
)
242 * Another naked interrupt trampoline
244 * Here we first save the timer value as fast as possible, then we jump (!)
245 * into the "official" interrupt handler with all its decorations.
247 void __attribute__((naked
)) PCINT0_vect(void)
249 #ifdef MM_USE_REGISTER_VARS
250 asm("in %[tl], %[tmr] \n\t"
251 "rjmp __vector_pinchange \n\t"
252 :: [tl
] "r" (mm_time_l
), [tmr
] "I" (_SFR_IO_ADDR(TCNT0
))
257 "sts mm_time_l, r0 \n\t"
259 "rjmp __vector_pinchange \n\t"
260 :: [tmr
] "I" (_SFR_IO_ADDR(TCNT0
))
265 /* Pin change interrupt vector, here we have a bit more time */
266 ISR(__vector_pinchange
){
269 /* First kill off that timer */
270 MM_TSTART
; /* Restart timer */
272 /* Account for not yet handled timer overflow */
275 duration
= mm_time_h
<< 8;
276 duration
+= mm_time_l
;
277 mm_bit_val
= MM_SENSE
;
280 if (recmode
!= MM_SLOW
) {
281 /* Fast short MM pulse */
282 if (MM_FAST_SHORT(duration
)){
284 if (mm_bit_val
== mm_polarity
)
288 /* Fast long MM pulse */
289 if (MM_FAST_LONG(duration
)) {
291 if (mm_bit_val
== mm_polarity
)
296 /* Accepted slow inter package gap */
297 if (MM_SLOW_GAP(duration
))
298 if (mm_bit_val
!= mm_polarity
)
302 if (recmode
!= MM_FAST
) {
304 /* Slow short MM pulse */
305 if (MM_SLOW_SHORT(duration
)) {
307 if (mm_bit_val
== mm_polarity
)
311 /* Slow long MM pulse */
312 if (MM_SLOW_LONG(duration
)) {
314 if (mm_bit_val
== mm_polarity
)
319 /* Accepted fast interpackage gap */
320 if (MM_FAST_GAP(duration
)) {
321 if (mm_bit_val
!= mm_polarity
)
327 * If we have reached here, our pulse comes in somehow unexpected.
328 * We kill of everything by re-arming the state machine.
330 /* Start over receiver */
333 if (MM_SUFFICIENT_IDLE(duration
)) {
334 recmode
= __MM_ARMED
;
335 mm_polarity
= !mm_bit_val
;
340 mm_pinchange_callback();
344 void __attribute__((weak
))mm_pinchange_callback(void)
348 void __attribute__((weak
))mm_drive_cb(uint8_t decoder
, uint8_t function
,
353 void __attribute__((weak
))mm_key_cb(uint8_t address
, uint8_t function
,
359 /******************************************************************************