trennfix/sw: Temporary
[eisenbahn.git] / trennfix / sw / 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
36#include "hardware.h"
37#include "mm_switch.h"
38
56b25f8b 39// #define DEBUG_TIMEOUT
70095677
PH
40#ifdef DEBUG_TIMEOUT
41#define MON(val) setpin(PIN_DRIVE, val)
42#else
43#define MON(xx) ({0;})
44#endif
45
46
47/*
48 * Lookup decoder number.
49 *
50 */
56b25f8b
PH
51
52static uint8_t lookup_nibble(uint8_t mm_nibble)
53{
54 switch(mm_nibble) {
55 case 0x0: return 0;
56 case 0xc: return 1;
57 case 0x8: return 2;
58 case 0x3: return 3;
59 case 0xf: return 4;
60 case 0xb: return 5;
61 case 0x2: return 6;
62 case 0xe: return 7;
63 case 0xa: return 8;
64 default: return 0;
65 }
66 return 0;
67}
68
70095677
PH
69static uint8_t lookup_decoder(uint8_t mm_byte)
70{
56b25f8b
PH
71 uint8_t low;
72 uint8_t high;
73 if (mm_byte == 0)
74 return 100;
75 low = lookup_nibble(mm_byte >> 4);
76 high = lookup_nibble(mm_byte & 0xf);
77 if (!low)
70095677 78 return 0;
56b25f8b 79 return 9 * high + low;
70095677
PH
80}
81
82static uint8_t lookup_command(uint8_t mm_byte)
83{
84 switch(mm_byte) {
85 case 0xc3: return 1;
86 case 0x03: return 2;
87 case 0xf3: return 3;
88 case 0x33: return 4;
89 case 0xcf: return 5;
90 case 0x0f: return 6;
91 case 0xff: return 7;
92 case 0x3f: return 8;
93 default:
94 return 0;
95 }
96}
97
56b25f8b
PH
98
99static __attribute__((unused)) void mm_switch_init(void)
70095677
PH
100{
101 /* Get rid of the 1/8 clock prescaler */
102 CLKPR = (1 << CLKPCE);
103 CLKPR = 0;
104
105 GIMSK |= _BV(PCIE); /* Enable pin change interrupt */
106 PCMSK = _BV(PCINT4); /* PB4, Rail sense input */
107
108 TCCR0A = 0; /* This is normal mode */
109 TCCR0B = 0; /* Timer off */
110 TIMSK |= _BV(OCIE0A); /* Get a match interrupt */
111 TIMSK |= _BV(TOV0); /* Overflow interrupt */
112
113 /* We need 13 + 45 us delay, That's 464 clocks @8MHz*/
114 OCR0A = 91; /* Prescaler 8 is used */
115
116 /* Timer 1 for timeout */
117 /* We set it to 1024us by prescaler 64 and running full 256 */
118 TCCR1 = 7;
119 TIMSK |= _BV(TOIE1); /* Overflow interrupt */
120}
121
56b25f8b
PH
122static volatile uint8_t shift_command;
123static volatile uint8_t shift_function;
124static volatile uint8_t shift_address;
125static volatile uint8_t shift_command_first;
126static volatile uint8_t shift_address_first;
70095677
PH
127
128/* We will shift from right to left.
129 * XXXXXXXX XX XXXXXXXX
56b25f8b 130 * shift_address shift_function shift_command
70095677 131 *
56b25f8b 132 * The bits 7 downto 2 of shift_function are ignored.
70095677
PH
133 */
134
135volatile uint8_t bitno = 0;
136
56b25f8b 137static void __attribute__((unused)) trigger(void)
70095677
PH
138{
139 setpin(PIN_DRIVE, 1);
56b25f8b 140 _delay_us(4);
70095677 141 setpin(PIN_DRIVE, 0);
56b25f8b 142 _delay_us(4);
70095677
PH
143}
144
145enum recstate {
146 IDLE = 0,
147 FIRST_FAST_SAMPLE,
148 FIRST_SLOW_SAMPLE, /* If clock arrives, we stay on the fast path! */
149 FAST_SAMPLE,
150 FAST_WAIT_FOR_CLOCK,
151 SLOW_SAMPLE,
152 SLOW_WAIT_FOR_CLOCK,
153};
154
155static enum recstate recstate = IDLE;
156
157static inline void shift(uint8_t value)
158{
56b25f8b
PH
159 shift_address <<= 1;
160 if (shift_function & 2)
161 shift_address |= 1;
162 shift_function <<= 1;
163 if (shift_command & 0x80)
164 shift_function |= 1;
165 shift_command <<= 1;
70095677 166 if (value)
56b25f8b 167 shift_command |= 1;
70095677
PH
168}
169
170
171ISR(TIMER0_COMPA_vect) {
70095677 172 static uint8_t patience = 0;
56b25f8b
PH
173 static uint8_t address_last = 0xff;
174 static uint8_t function_last = 0xff;
175 static uint8_t command_last = 0xff;
70095677
PH
176
177 switch(recstate) {
178 case FIRST_FAST_SAMPLE:
179 recstate = FIRST_SLOW_SAMPLE;
180 TSTART_CLK_TO_SAMPLE_FAST; /* Will not run out in fast! */
181 break;
182
183 case FIRST_SLOW_SAMPLE:
184 bitno = 0;
185
186 case SLOW_SAMPLE:
187 recstate = SLOW_WAIT_FOR_CLOCK;
188 TSTART_CLK_TO_SAMPLE_SLOW;
189 break;
190
191 case FAST_SAMPLE:
192 recstate = FAST_WAIT_FOR_CLOCK;
193 TSTART_CLK_TO_SAMPLE_FAST;
194 break;
195
196 case FAST_WAIT_FOR_CLOCK: /* A timeout! */
197 if (patience) {
198 patience--;
199 TSTART_CLK_TO_SAMPLE_FAST;
200 return;
201 }
202 recstate = IDLE;
203 TSTOP;
204 return;
205
206 case SLOW_WAIT_FOR_CLOCK:
207 if (patience) {
208 patience--;
209 TSTART_CLK_TO_SAMPLE_SLOW;
210 return;
211 }
56b25f8b 212 default:
70095677
PH
213 TSTOP;
214 recstate = IDLE;
215 return;
216 }
217
218 shift(MM_SENSE);
219 bitno++;
56b25f8b 220
70095677 221 if (bitno == 18) { /* Save first received word */
56b25f8b
PH
222 shift_command_first = shift_command;
223 shift_address_first = shift_address;
70095677
PH
224 patience = 18;
225
226 }
56b25f8b 227
70095677 228 if (bitno == 36) {
56b25f8b
PH
229 if ((shift_command == shift_command_first) &&
230 (shift_address == shift_address_first)) {
231 if ((shift_address != address_last) || (shift_command != command_last) ||
232 shift_function != function_last) {
233
234 uint8_t addr = lookup_decoder(shift_address);
235
236 if (recstate == SLOW_WAIT_FOR_CLOCK) {
237 mm_switch_drive(addr, shift_function, shift_command);
238 }
239
240
241 if (recstate == FAST_WAIT_FOR_CLOCK && addr) {
242 uint8_t command = lookup_command(shift_command);
243 /* Congratulations, we have a valid command */
244 mm_switch_command(addr, command);
70095677
PH
245 }
246 }
56b25f8b
PH
247 address_last = shift_address;
248 function_last = shift_function;
249 command_last = shift_command;
70095677 250 }
56b25f8b 251
70095677
PH
252 }
253}
254
56b25f8b
PH
255//void __attribute((weak)) mm_switch_drive(uint8_t address, uint8_t function, uint8_t command);
256
70095677
PH
257ISR(BADISR_vect)
258{
259 while(1) {
260 /*
261 setpin(PIN_LED, 1);
262 _delay_ms(30);
263 setpin(PIN_LED, 0);
264 _delay_ms(30);
265 setpin(PIN_LED, 1);
266 _delay_ms(30);
267 setpin(PIN_LED, 0);
268 _delay_ms(2000);
269 */
270 }
271}
272
273ISR(TIM0_OVF_vect)
274{
275 return;
276 while(1) {
56b25f8b 277 setpin(PIN_LED, 1);
70095677
PH
278 _delay_ms(30);
279 setpin(PIN_LED, 0);
280 _delay_ms(300);
281 }
282
283}
284
285
286/* Pin change interrupt vector */
287void mm_pinchange_handler(void)
288{
289 static uint8_t sense_last;
290
291 if (MM_SENSE == sense_last)
292 return;
293 sense_last = MM_SENSE;
294 if (!sense_last)
295 return;
56b25f8b 296
70095677
PH
297 switch(recstate) {
298 case IDLE:
299 bitno = 0;
300 recstate = FIRST_FAST_SAMPLE;
301 TSTART_CLK_TO_SAMPLE_FAST;
302 break;
303 case FIRST_SLOW_SAMPLE:
304 recstate = FAST_SAMPLE;
305 TSTART_CLK_TO_SAMPLE_FAST;
306 break;
307 case FAST_WAIT_FOR_CLOCK:
308 recstate = FAST_SAMPLE;
309 TSTART_CLK_TO_SAMPLE_FAST;
310 break;
311 case SLOW_WAIT_FOR_CLOCK:
312 recstate = SLOW_SAMPLE;
313 TSTART_CLK_TO_SAMPLE_SLOW;
314 break;
315
316 /* Not expected */
317 case FIRST_FAST_SAMPLE:
318 case FAST_SAMPLE:
319 case SLOW_SAMPLE:
320 default:
321 break;
322 }
323}
324
325/******************************************************************************
326 * The end :-)
327 */
328
329
330