trennfix/sw: More versatile file structure
[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
39 /*
40 * Private data types
41 */
42
43 enum recstate {
44 IDLE = 0,
45 FIRST_FAST_SAMPLE,
46 FIRST_SLOW_SAMPLE, /* If clock arrives, we stay on the fast path! */
47 FAST_SAMPLE,
48 FAST_WAIT_FOR_CLOCK,
49 SLOW_SAMPLE,
50 SLOW_WAIT_FOR_CLOCK,
51 };
52
53 /*
54 *
55 * Check for stuff we need
56 *
57 */
58 #if !defined(MM_TSTART_FAST) || !defined(MM_TSTART_SLOW) || !defined(MM_TSTOP) \
59 || !defined(MM_SENSE) || !defined(MM_TIMER_INT_VECT)
60
61 #error Missing timer start macro MM_TSTART_FAST!
62
63 #endif
64
65 /*
66 * Private global variables
67 */
68 static volatile uint8_t shift_command;
69 static volatile uint8_t shift_function;
70 static volatile uint8_t shift_address;
71 static enum recstate recstate = IDLE;
72 volatile uint8_t bitno = 0;
73
74
75 /*
76 * Lookup trinary nibble
77 *
78 * This was implemented using a switch statement before.
79 * Changing the lookup to a table did only add two bytes
80 * of memory and saved ca. 50 bytes program memory.
81 */
82 static const uint8_t nibble_table[16]={
83 [0x0] = 0,
84 [0xc] = 1,
85 [0x8] = 2,
86 [0x3] = 3,
87 [0xf] = 4,
88 [0xb] = 5,
89 [0x2] = 6,
90 [0xe] = 7,
91 [0xa] = 8
92 };
93 #define lookup_nibble(nibble) nibble_table[nibble & 0xf]
94
95 static uint8_t lookup_decoder(uint8_t mm_byte)
96 {
97 uint8_t low;
98 uint8_t high;
99 if (mm_byte == 0)
100 return 80;
101 low = lookup_nibble(mm_byte >> 4);
102 high = lookup_nibble(mm_byte & 0xf);
103 if (!low)
104 return 0;
105 return 9 * high + low;
106 }
107
108 static uint8_t lookup_command(uint8_t mm_byte)
109 {
110 switch(mm_byte) {
111 case 0xc3: return 1;
112 case 0x03: return 2;
113 case 0xf3: return 3;
114 case 0x33: return 4;
115 case 0xcf: return 5;
116 case 0x0f: return 6;
117 case 0xff: return 7;
118 case 0x3f: return 8;
119 default:
120 return 0;
121 }
122 }
123
124 /* We will shift from right to left.
125 * XXXXXXXX XX XXXXXXXX
126 * shift_address shift_function shift_command
127 *
128 * The bits 7 downto 2 of shift_function are ignored.
129 */
130
131 static inline void shift(uint8_t value)
132 {
133 shift_address <<= 1;
134 if (shift_function & 2)
135 shift_address |= 1;
136 shift_function <<= 1;
137 if (shift_command & 0x80)
138 shift_function |= 1;
139 shift_command <<= 1;
140 if (value)
141 shift_command |= 1;
142 }
143
144 ISR(MM_TIMER_INT_VECT) {
145 static uint8_t tolerated_timeouts = 0;
146
147 static volatile uint8_t shift_command_first;
148 static volatile uint8_t shift_function_first;
149 static volatile uint8_t shift_address_first;
150 uint8_t address;
151 uint8_t command;
152
153 #ifdef MM_FILTER_REPEATED
154 static uint8_t address_last = 0xff;
155 static uint8_t function_last = 0xff;
156 static uint8_t command_last = 0xff;
157 #endif
158
159 switch(recstate) {
160 case FIRST_FAST_SAMPLE:
161 recstate = FIRST_SLOW_SAMPLE;
162 MM_TSTART_FAST; /* Will not run out in fast! */
163 break;
164
165 case FIRST_SLOW_SAMPLE:
166 bitno = 0;
167
168 case SLOW_SAMPLE:
169 recstate = SLOW_WAIT_FOR_CLOCK;
170 MM_TSTART_SLOW;
171 break;
172
173 case FAST_SAMPLE:
174 recstate = FAST_WAIT_FOR_CLOCK;
175 MM_TSTART_FAST;
176 break;
177
178 case FAST_WAIT_FOR_CLOCK: /* A timeout! */
179 if (tolerated_timeouts) {
180 tolerated_timeouts--;
181 MM_TSTART_FAST;
182 return;
183 }
184 recstate = IDLE;
185 MM_TSTOP;
186 return;
187
188 case SLOW_WAIT_FOR_CLOCK:
189 if (tolerated_timeouts) {
190 tolerated_timeouts--;
191 MM_TSTART_SLOW;
192 return;
193 }
194 default:
195 MM_TSTOP;
196 recstate = IDLE;
197 return;
198 }
199
200 shift(MM_SENSE);
201 bitno++;
202
203 if (bitno == 18) { /* Save first received word */
204 shift_address_first = shift_address;
205 shift_function_first = shift_function;
206 shift_command_first = shift_command;
207
208 tolerated_timeouts = 18;
209 }
210
211 if (bitno == 36) {
212 if ((shift_command == shift_command_first) &&
213 (shift_address == shift_address_first) &&
214 (shift_function == shift_function_first)) {
215
216 #ifdef MM_FILTER_REPEATED
217 if ((shift_address != address_last) || (shift_command != command_last) ||
218 shift_function != function_last) {
219 #endif
220 address = lookup_decoder(shift_address);
221
222 if (recstate == SLOW_WAIT_FOR_CLOCK) {
223 mm_switch_drive(address, shift_function, shift_command);
224 } else if (recstate == FAST_WAIT_FOR_CLOCK) {
225 command = lookup_command(shift_command);
226 mm_switch_command(address, command);
227 }
228 #ifdef MM_FILTER_REPEATED
229 }
230 address_last = shift_address;
231 function_last = shift_function;
232 command_last = shift_command;
233 #endif
234 }
235
236 }
237 }
238
239 //void __attribute((weak)) mm_switch_drive(uint8_t address, uint8_t function, uint8_t command);
240
241 ISR(BADISR_vect)
242 {
243 while(1) {
244 /*
245 setpin(PIN_LED, 1);
246 _delay_ms(30);
247 setpin(PIN_LED, 0);
248 _delay_ms(30);
249 setpin(PIN_LED, 1);
250 _delay_ms(30);
251 setpin(PIN_LED, 0);
252 _delay_ms(2000);
253 */
254 }
255 }
256
257 ISR(TIM0_OVF_vect)
258 {
259 return;
260 while(1) {
261 setpin(PIN_LED, 1);
262 _delay_ms(30);
263 setpin(PIN_LED, 0);
264 _delay_ms(300);
265 }
266
267 }
268
269 /* Pin change interrupt vector */
270 void mm_pinchange_handler(void)
271 {
272 static uint8_t sense_last;
273
274 if (MM_SENSE == sense_last)
275 return;
276 sense_last = MM_SENSE;
277 if (!sense_last)
278 return;
279
280 switch(recstate) {
281 case IDLE:
282 bitno = 0;
283 recstate = FIRST_FAST_SAMPLE;
284 MM_TSTART_FAST;
285 break;
286 case FIRST_SLOW_SAMPLE:
287 recstate = FAST_SAMPLE;
288 MM_TSTART_FAST;
289 break;
290 case FAST_WAIT_FOR_CLOCK:
291 recstate = FAST_SAMPLE;
292 MM_TSTART_FAST;
293 break;
294 case SLOW_WAIT_FOR_CLOCK:
295 recstate = SLOW_SAMPLE;
296 MM_TSTART_SLOW;
297 break;
298
299 /* Not expected */
300 case FIRST_FAST_SAMPLE:
301 case FAST_SAMPLE:
302 case SLOW_SAMPLE:
303 default:
304 break;
305 }
306 }
307
308 void __attribute__((weak))mm_switch_drive(uint8_t decoder, uint8_t function,
309 uint8_t command)
310 {
311 while(1);
312 }
313
314 void __attribute__((weak))mm_switch_command(uint8_t address, uint8_t command)
315 {
316 }
317
318
319 /******************************************************************************
320 * The end :-)
321 */
322
323
324