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>
37 #include "mm_switch.h"
39 // #define DEBUG_TIMEOUT
41 #define MON(val) setpin(PIN_DRIVE, val)
43 #define MON(xx) ({0;})
48 * Lookup decoder number.
52 static uint8_t lookup_nibble(uint8_t mm_nibble
)
69 static uint8_t lookup_decoder(uint8_t mm_byte
)
75 low
= lookup_nibble(mm_byte
>> 4);
76 high
= lookup_nibble(mm_byte
& 0xf);
79 return 9 * high
+ low
;
82 static uint8_t lookup_command(uint8_t mm_byte
)
99 static __attribute__((unused
)) void mm_switch_init(void)
101 /* Get rid of the 1/8 clock prescaler */
102 CLKPR
= (1 << CLKPCE
);
105 GIMSK
|= _BV(PCIE
); /* Enable pin change interrupt */
106 PCMSK
= _BV(PCINT4
); /* PB4, Rail sense input */
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 */
113 /* We need 13 + 45 us delay, That's 464 clocks @8MHz*/
114 OCR0A
= 91; /* Prescaler 8 is used */
116 /* Timer 1 for timeout */
117 /* We set it to 1024us by prescaler 64 and running full 256 */
119 TIMSK
|= _BV(TOIE1
); /* Overflow interrupt */
122 static volatile uint8_t shift_command
;
123 static volatile uint8_t shift_function
;
124 static volatile uint8_t shift_address
;
125 static volatile uint8_t shift_command_first
;
126 static volatile uint8_t shift_address_first
;
128 /* We will shift from right to left.
129 * XXXXXXXX XX XXXXXXXX
130 * shift_address shift_function shift_command
132 * The bits 7 downto 2 of shift_function are ignored.
135 volatile uint8_t bitno
= 0;
137 static void __attribute__((unused
)) trigger(void)
139 setpin(PIN_DRIVE
, 1);
141 setpin(PIN_DRIVE
, 0);
148 FIRST_SLOW_SAMPLE
, /* If clock arrives, we stay on the fast path! */
155 static enum recstate recstate
= IDLE
;
157 static inline void shift(uint8_t value
)
160 if (shift_function
& 2)
162 shift_function
<<= 1;
163 if (shift_command
& 0x80)
171 ISR(TIMER0_COMPA_vect
) {
172 static uint8_t patience
= 0;
173 static uint8_t address_last
= 0xff;
174 static uint8_t function_last
= 0xff;
175 static uint8_t command_last
= 0xff;
178 case FIRST_FAST_SAMPLE
:
179 recstate
= FIRST_SLOW_SAMPLE
;
180 TSTART_CLK_TO_SAMPLE_FAST
; /* Will not run out in fast! */
183 case FIRST_SLOW_SAMPLE
:
187 recstate
= SLOW_WAIT_FOR_CLOCK
;
188 TSTART_CLK_TO_SAMPLE_SLOW
;
192 recstate
= FAST_WAIT_FOR_CLOCK
;
193 TSTART_CLK_TO_SAMPLE_FAST
;
196 case FAST_WAIT_FOR_CLOCK
: /* A timeout! */
199 TSTART_CLK_TO_SAMPLE_FAST
;
206 case SLOW_WAIT_FOR_CLOCK
:
209 TSTART_CLK_TO_SAMPLE_SLOW
;
221 if (bitno
== 18) { /* Save first received word */
222 shift_command_first
= shift_command
;
223 shift_address_first
= shift_address
;
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
) {
234 uint8_t addr
= lookup_decoder(shift_address
);
236 if (recstate
== SLOW_WAIT_FOR_CLOCK
) {
237 mm_switch_drive(addr
, shift_function
, shift_command
);
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
);
247 address_last
= shift_address
;
248 function_last
= shift_function
;
249 command_last
= shift_command
;
255 //void __attribute((weak)) mm_switch_drive(uint8_t address, uint8_t function, uint8_t command);
286 /* Pin change interrupt vector */
287 void mm_pinchange_handler(void)
289 static uint8_t sense_last
;
291 if (MM_SENSE
== sense_last
)
293 sense_last
= MM_SENSE
;
300 recstate
= FIRST_FAST_SAMPLE
;
301 TSTART_CLK_TO_SAMPLE_FAST
;
303 case FIRST_SLOW_SAMPLE
:
304 recstate
= FAST_SAMPLE
;
305 TSTART_CLK_TO_SAMPLE_FAST
;
307 case FAST_WAIT_FOR_CLOCK
:
308 recstate
= FAST_SAMPLE
;
309 TSTART_CLK_TO_SAMPLE_FAST
;
311 case SLOW_WAIT_FOR_CLOCK
:
312 recstate
= SLOW_SAMPLE
;
313 TSTART_CLK_TO_SAMPLE_SLOW
;
317 case FIRST_FAST_SAMPLE
:
325 /******************************************************************************