trennfix/sw: Added smokefix, further modularized the mm decoder
[eisenbahn.git] / trennfix / sw / mm / src / mm_switch.c
CommitLineData
70095677
PH
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
93cb14d4
PH
36#include <config/hardware.h>
37#include <mm/mm_switch.h>
f12652df 38#include <mm/mm_decode.h>
70095677
PH
39
40/*
93cb14d4
PH
41 *
42 * Check for stuff we need
70095677
PH
43 *
44 */
dc41eb22 45#if !defined(MM_TSTART) || !defined(MM_SENSE) || !defined(MM_TIMER_INT_VECT)
dc41eb22 46#error Missing needed MM_... macro!
93cb14d4
PH
47#endif
48
f12652df
PH
49#ifndef MM_QUEUE_DEPTH
50#define MM_QUEUE_DEPTH 8
51#endif
93cb14d4 52
f12652df
PH
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
84static enum mm_recmode recmode = __MM_INIT;
85
86#ifdef MM_USE_QUEUE
87static uint8_t queue_wpos = 0;
88static uint8_t queue_rpos = 0;
89static struct mm_command queue[MM_QUEUE_DEPTH];
90#endif
c839c431 91
7c08d02a
PH
92#ifndef MM_USE_REGISTER_VARS
93
27b551dd 94static volatile uint8_t mm_bitno = 0;
7c08d02a
PH
95static uint8_t shift_command;
96static uint8_t shift_function;
97static uint8_t shift_address;
27b551dd
PH
98uint8_t mm_time_h = 0;
99uint8_t mm_time_l = 0;
100uint8_t mm_bit_val = 23;
101static uint8_t mm_flavor;
102static uint8_t mm_polarity = 1;
7c08d02a
PH
103
104#endif
93cb14d4 105
70095677
PH
106/* We will shift from right to left.
107 * XXXXXXXX XX XXXXXXXX
56b25f8b 108 * shift_address shift_function shift_command
70095677 109 *
56b25f8b 110 * The bits 7 downto 2 of shift_function are ignored.
27b551dd
PH
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.
70095677 116 */
27b551dd 117#if 0
f12652df 118static void shift(uint8_t value)
27b551dd
PH
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
70095677 131
f12652df 132static void shift(uint8_t value)
7c08d02a
PH
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}
7c08d02a
PH
147#endif
148
c839c431 149static void mm_feed_bit(uint8_t bit)
b1a7e65e 150{
93cb14d4
PH
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;
93cb14d4 155
b1a7e65e 156 shift(bit);
27b551dd 157 mm_bitno++;
c839c431 158
27b551dd 159 if (mm_bitno == 18) { /* Save first received word */
93cb14d4
PH
160 shift_address_first = shift_address;
161 shift_function_first = shift_function;
162 shift_command_first = shift_command;
f12652df 163 }
56b25f8b 164
27b551dd 165 if (mm_bitno == 36) {
56b25f8b 166 if ((shift_command == shift_command_first) &&
93cb14d4
PH
167 (shift_address == shift_address_first) &&
168 (shift_function == shift_function_first)) {
c839c431 169
f12652df
PH
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);
27b551dd 175 } else {
f12652df
PH
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
70095677
PH
187 }
188 }
189}
f12652df
PH
190#ifdef MM_USE_QUEUE
191
192struct 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
70095677 205
b1a7e65e
PH
206/*
207 * The timeout interrupt vector does nothing else
27b551dd 208 * than incrementing the mm_time_h round counter.
b1a7e65e
PH
209 *
210 * It is written in naked assembly because we want to avoid pushing
211 * and popping of all upper registers.
212 */
213void __attribute__((naked)) MM_TIMER_INT_VECT(void)
70095677 214{
b1a7e65e
PH
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"
c839c431
PH
219 "brne nover \n\t"
220 "dec %[th] \n\t"
221 "nover: \n\t"
b1a7e65e
PH
222#else
223 "push r1 \n\t"
c839c431 224 "lds r1, mm_time_h \n\t"
b1a7e65e 225 "inc r1 \n\t"
c839c431
PH
226 "brne nover \n\t"
227 "dec r1 \n\t"
228 "nover: \n\t"
27b551dd 229 "sts mm_time_h, r1 \n\t"
b1a7e65e
PH
230 "pop r1 \n\t"
231#endif
232 "out __SREG__, r0 \n\t"
233 "pop r0 \n\t"
b1a7e65e
PH
234 "reti \n\t"
235#ifdef MM_USE_REGISTER_VARS
27b551dd 236 :: [th] "r" (mm_time_h)
b1a7e65e
PH
237#endif
238 );
70095677
PH
239}
240
b1a7e65e
PH
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 */
247void __attribute__((naked)) PCINT0_vect(void)
70095677 248{
b1a7e65e
PH
249#ifdef MM_USE_REGISTER_VARS
250 asm("in %[tl], %[tmr] \n\t"
251 "rjmp __vector_pinchange \n\t"
27b551dd 252 :: [tl] "r" (mm_time_l), [tmr] "I" (_SFR_IO_ADDR(TCNT0))
b1a7e65e
PH
253 );
254#else
255 asm("push r0 \n\t"
256 "in r0, %[tmr] \n\t"
27b551dd 257 "sts mm_time_l, r0 \n\t"
b1a7e65e
PH
258 "pop r0 \n\t"
259 "rjmp __vector_pinchange \n\t"
260 :: [tmr] "I" (_SFR_IO_ADDR(TCNT0))
261 );
262#endif
263}
f12652df 264
b1a7e65e
PH
265/* Pin change interrupt vector, here we have a bit more time */
266ISR(__vector_pinchange){
c839c431
PH
267 uint16_t duration;
268
b1a7e65e 269 /* First kill off that timer */
b1a7e65e 270 MM_TSTART; /* Restart timer */
f12652df 271
b1a7e65e 272 /* Account for not yet handled timer overflow */
c839c431 273 TIFR |= _BV(TOV0);
b1a7e65e 274
c839c431 275 duration = mm_time_h << 8;
27b551dd 276 duration += mm_time_l;
c839c431 277 mm_bit_val = MM_SENSE;
27b551dd 278 mm_time_h = 0;
b1a7e65e 279
f12652df
PH
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)
c839c431 285 mm_feed_bit(0);
f12652df
PH
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)
c839c431 292 mm_feed_bit(1);
f12652df
PH
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)
c839c431 299 goto done;
f12652df 300 }
c839c431 301
f12652df 302 if (recmode != MM_FAST) {
c839c431 303
f12652df
PH
304 /* Slow short MM pulse */
305 if (MM_SLOW_SHORT(duration)) {
306 recmode = MM_SLOW;
307 if (mm_bit_val == mm_polarity)
c839c431 308 mm_feed_bit(0);
f12652df 309 goto done;
b1a7e65e 310 }
f12652df
PH
311 /* Slow long MM pulse */
312 if (MM_SLOW_LONG(duration)) {
313 recmode = MM_SLOW;
314 if (mm_bit_val == mm_polarity)
c839c431 315 mm_feed_bit(1);
f12652df
PH
316 goto done;
317 }
318 } else {
319 /* Accepted fast interpackage gap */
320 if (MM_FAST_GAP(duration)) {
321 if (mm_bit_val != mm_polarity)
c839c431 322 goto done;
d0047978
PH
323 }
324 }
b1a7e65e 325
f12652df 326 /*
c839c431
PH
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;
f12652df
PH
332
333 if (MM_SUFFICIENT_IDLE(duration)) {
334 recmode = __MM_ARMED;
335 mm_polarity = !mm_bit_val;
336 } else {
337 recmode = __MM_INIT;
338 }
b1a7e65e 339 done:
f12652df 340 mm_pinchange_callback();
d0047978 341 }
9c77e706 342
27b551dd 343
f12652df 344void __attribute__((weak))mm_pinchange_callback(void)
d0047978 345{
70095677
PH
346}
347
f12652df 348void __attribute__((weak))mm_drive_cb(uint8_t decoder, uint8_t function,
93cb14d4
PH
349 uint8_t command)
350{
93cb14d4
PH
351}
352
f12652df
PH
353void __attribute__((weak))mm_key_cb(uint8_t address, uint8_t function,
354 uint8_t command)
93cb14d4
PH
355{
356}
357
358
70095677
PH
359/******************************************************************************
360 * The end :-)
361 */