trennfix/sw: Added smokefix, further modularized the mm decoder
[eisenbahn.git] / trennfix / sw / mm / src / mm_switch.c
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
36 #include <config/hardware.h>
37 #include <mm/mm_switch.h>
38 #include <mm/mm_decode.h>
39
40 /*
41 *
42 * Check for stuff we need
43 *
44 */
45 #if !defined(MM_TSTART) || !defined(MM_SENSE) || !defined(MM_TIMER_INT_VECT)
46 #error Missing needed MM_... macro!
47 #endif
48
49 #ifndef MM_QUEUE_DEPTH
50 #define MM_QUEUE_DEPTH 8
51 #endif
52
53
54 /*
55 * The nominal length of a bit cycle is 208 us.
56 * This consists of 8 parts, each 26 us:
57 * 1 d d d d d d 0
58 *
59 * That means that the 1 pulse is 7 * 26 = 182us
60 * and the short pulse is 1 * 26 = 26us.
61 *
62 * Reality seems to look not as that exact. I measure
63 * 26us for the clock pulse, but 196 for the long pulse.
64 *
65 * Keep in mind: Time is counted in 500ns steps.
66 *
67 */
68 #define IN_RANGE(duration, lower, upper) \
69 ((duration >= (2 * lower) \
70 && (duration <= (2 * upper))))
71
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)
75
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)
79
80 #define MM_SUFFICIENT_IDLE(duration) (duration > 800)
81
82
83
84 static enum mm_recmode recmode = __MM_INIT;
85
86 #ifdef MM_USE_QUEUE
87 static uint8_t queue_wpos = 0;
88 static uint8_t queue_rpos = 0;
89 static struct mm_command queue[MM_QUEUE_DEPTH];
90 #endif
91
92 #ifndef MM_USE_REGISTER_VARS
93
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;
103
104 #endif
105
106 /* We will shift from right to left.
107 * XXXXXXXX XX XXXXXXXX
108 * shift_address shift_function shift_command
109 *
110 * The bits 7 downto 2 of shift_function are ignored.
111 *
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.
116 */
117 #if 0
118 static void shift(uint8_t value)
119 {
120 shift_address <<= 1;
121 if (shift_function & 2)
122 shift_address |= 1;
123 shift_function <<= 1;
124 if (shift_command & 0x80)
125 shift_function |= 1;
126 shift_command <<= 1;
127 if (value)
128 shift_command |= 1;
129 }
130 #else
131
132 static void shift(uint8_t value)
133 {
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)
145 );
146 }
147 #endif
148
149 static void mm_feed_bit(uint8_t bit)
150 {
151 static volatile uint8_t shift_command_first;
152 static volatile uint8_t shift_function_first;
153 static volatile uint8_t shift_address_first;
154 uint8_t address;
155
156 shift(bit);
157 mm_bitno++;
158
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;
163 }
164
165 if (mm_bitno == 36) {
166 if ((shift_command == shift_command_first) &&
167 (shift_address == shift_address_first) &&
168 (shift_function == shift_function_first)) {
169
170 #ifdef MM_USE_CALLBACK
171 address = mm_lookup_decoder(shift_address);
172 if (recmode == MM_SLOW) {
173 mm_drive_cb(address, shift_function,
174 shift_command);
175 } else {
176 mm_key_cb(address, shift_function, shift_command);
177 }
178 #endif
179
180 #ifdef MM_USE_QUEUE
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;
186 #endif
187 }
188 }
189 }
190 #ifdef MM_USE_QUEUE
191
192 struct mm_command *mm_get(void)
193 {
194 struct mm_command *result = NULL;
195 uint8_t sreg_bak = SREG;
196 cli();
197 if (queue_rpos != queue_wpos) {
198 result = &queue[queue_rpos];
199 queue_rpos = (queue_rpos + 1) % MM_QUEUE_DEPTH;
200 }
201 SREG = sreg_bak;
202 return result;
203 }
204 #endif
205
206 /*
207 * The timeout interrupt vector does nothing else
208 * than incrementing the mm_time_h round counter.
209 *
210 * It is written in naked assembly because we want to avoid pushing
211 * and popping of all upper registers.
212 */
213 void __attribute__((naked)) MM_TIMER_INT_VECT(void)
214 {
215 asm("push r0 ; save r0 \n\t"
216 "in r0, __SREG__ \n\t"
217 #ifdef MM_USE_REGISTER_VARS
218 "inc %[th] \n\t"
219 "brne nover \n\t"
220 "dec %[th] \n\t"
221 "nover: \n\t"
222 #else
223 "push r1 \n\t"
224 "lds r1, mm_time_h \n\t"
225 "inc r1 \n\t"
226 "brne nover \n\t"
227 "dec r1 \n\t"
228 "nover: \n\t"
229 "sts mm_time_h, r1 \n\t"
230 "pop r1 \n\t"
231 #endif
232 "out __SREG__, r0 \n\t"
233 "pop r0 \n\t"
234 "reti \n\t"
235 #ifdef MM_USE_REGISTER_VARS
236 :: [th] "r" (mm_time_h)
237 #endif
238 );
239 }
240
241 /*
242 * Another naked interrupt trampoline
243 *
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.
246 */
247 void __attribute__((naked)) PCINT0_vect(void)
248 {
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))
253 );
254 #else
255 asm("push r0 \n\t"
256 "in r0, %[tmr] \n\t"
257 "sts mm_time_l, r0 \n\t"
258 "pop r0 \n\t"
259 "rjmp __vector_pinchange \n\t"
260 :: [tmr] "I" (_SFR_IO_ADDR(TCNT0))
261 );
262 #endif
263 }
264
265 /* Pin change interrupt vector, here we have a bit more time */
266 ISR(__vector_pinchange){
267 uint16_t duration;
268
269 /* First kill off that timer */
270 MM_TSTART; /* Restart timer */
271
272 /* Account for not yet handled timer overflow */
273 TIFR |= _BV(TOV0);
274
275 duration = mm_time_h << 8;
276 duration += mm_time_l;
277 mm_bit_val = MM_SENSE;
278 mm_time_h = 0;
279
280 if (recmode != MM_SLOW) {
281 /* Fast short MM pulse */
282 if (MM_FAST_SHORT(duration)){
283 recmode = MM_FAST;
284 if (mm_bit_val == mm_polarity)
285 mm_feed_bit(0);
286 goto done;
287 }
288 /* Fast long MM pulse */
289 if (MM_FAST_LONG(duration)) {
290 recmode = MM_FAST;
291 if (mm_bit_val == mm_polarity)
292 mm_feed_bit(1);
293 goto done;
294 }
295 } else {
296 /* Accepted slow inter package gap */
297 if (MM_SLOW_GAP(duration))
298 if (mm_bit_val != mm_polarity)
299 goto done;
300 }
301
302 if (recmode != MM_FAST) {
303
304 /* Slow short MM pulse */
305 if (MM_SLOW_SHORT(duration)) {
306 recmode = MM_SLOW;
307 if (mm_bit_val == mm_polarity)
308 mm_feed_bit(0);
309 goto done;
310 }
311 /* Slow long MM pulse */
312 if (MM_SLOW_LONG(duration)) {
313 recmode = MM_SLOW;
314 if (mm_bit_val == mm_polarity)
315 mm_feed_bit(1);
316 goto done;
317 }
318 } else {
319 /* Accepted fast interpackage gap */
320 if (MM_FAST_GAP(duration)) {
321 if (mm_bit_val != mm_polarity)
322 goto done;
323 }
324 }
325
326 /*
327 * If we have reached here, our pulse comes in somehow unexpected.
328 * We kill of everything by re-arming the state machine.
329 */
330 /* Start over receiver */
331 mm_bitno = 0;
332
333 if (MM_SUFFICIENT_IDLE(duration)) {
334 recmode = __MM_ARMED;
335 mm_polarity = !mm_bit_val;
336 } else {
337 recmode = __MM_INIT;
338 }
339 done:
340 mm_pinchange_callback();
341 }
342
343
344 void __attribute__((weak))mm_pinchange_callback(void)
345 {
346 }
347
348 void __attribute__((weak))mm_drive_cb(uint8_t decoder, uint8_t function,
349 uint8_t command)
350 {
351 }
352
353 void __attribute__((weak))mm_key_cb(uint8_t address, uint8_t function,
354 uint8_t command)
355 {
356 }
357
358
359 /******************************************************************************
360 * The end :-)
361 */