trennfix/sw: Some cleanup
[eisenbahn.git] / trennfix / sw / main.c
1 /******************************************************************************
2 *
3 * Trennfix firmware - main.c
4 *
5 * Copyright (C) 2017 Philipp Hachtmann
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 *****************************************************************************/
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <avr/io.h>
27 #include <avr/eeprom.h>
28 #include <avr/interrupt.h>
29 #include <avr/pgmspace.h>
30
31 #include <util/delay.h>
32 #include <stdint.h>
33
34 #include "pin_magic.h"
35 #include "mm_lookup.h"
36
37 #define LED _PIN(PORTB, PORTB2)
38 #define SENSE _PIN(PORTB, PORTB4)
39 #define DRIVE _PIN(PORTB, PORTB3)
40 #define BTN _PIN(PORTB, PB1)
41
42 #define SIGIN (!PINVAL(SENSE))
43 #define BTN_PRESSED (!PINVAL(BTN))
44
45 // #define DEBUG_TIMEOUT
46 #ifdef DEBUG_TIMEOUT
47 #define MON_TIMEOUT(val) setpin(DRIVE, val)
48 #else
49 #define MON_TIMEOUT(xx) ({0;})
50 #endif
51
52 uint8_t EEMEM ee_our_decoder;
53 uint8_t EEMEM ee_our_key;
54
55 static void setup_hw(void)
56 {
57 /* Get rid of the 1/8 clock prescaler */
58 CLKPR = (1 << CLKPCE);
59 CLKPR = 0;
60
61 INPUT_PIN(SENSE);
62 INPUT_PIN(BTN);
63 OUTPUT_PIN(DRIVE);
64 OUTPUT_PIN(LED);
65 setpin(BTN, 1); /* Need pullup */
66 _delay_ms(1); /* Let the line rise */
67
68
69 GIMSK |= _BV(PCIE); /* Enable pin change interrupt */
70 PCMSK = _BV(PCINT4); /* PB4, Rail sense input */
71
72 TCCR0A = 0; /* This is normal mode */
73 TCCR0B = 0; /* Timer off */
74 TIMSK |= _BV(OCIE0A); /* Get a match interrupt */
75 TIMSK |= _BV(TOV0); /* Overflow interrupt */
76
77 /* We need 13 + 45 us delay, That's 464 clocks @8MHz*/
78 OCR0A = 91; /* Prescaler 8 is used */
79
80 /* Timer 1 for timeout */
81 /* We set it to 1024us by prescaler 64 and running full 256 */
82 TCCR1 = 7;
83 TIMSK |= _BV(TOIE1); /* Overflow interrupt */
84 }
85
86 static uint8_t ctr = 0;
87
88 static volatile uint8_t shift_sub;
89 static volatile uint8_t shift_zero;
90 static volatile uint8_t shift_decoder;
91 static volatile uint8_t shift_sub_first;
92 static volatile uint8_t shift_decoder_first;
93
94 /* We will shift from right to left.
95 * XXXXXXXX XX XXXXXXXX
96 * shift_decoder shift_zero shift_sub
97 *
98 * The bits 7 downto 2 of shift_zero are ignored.
99 */
100
101 #define STOP_TIMER0 (TCCR0B = 0)
102
103 volatile uint8_t bitno = 0;
104
105
106 static void zero(void)
107 {
108 setpin(LED, 1);
109 _delay_ms(20);
110 setpin(LED, 0);
111 _delay_ms(980);
112 GIFR |= _BV(PCIF);
113 }
114
115
116 static void one(void)
117 {
118 setpin(LED, 1);
119 _delay_ms(20);
120 setpin(LED, 0);
121 _delay_ms(160);
122 setpin(LED, 1);
123 _delay_ms(20);
124 setpin(LED, 0);
125 _delay_ms(800);
126 GIFR |= _BV(PCIF);
127 }
128
129 static void trigger(void)
130 {
131 return;
132 setpin(DRIVE, 1);
133 _delay_us(20);
134 setpin(DRIVE, 0);
135 }
136
137 static volatile uint8_t our_decoder = 3;
138 static volatile uint8_t our_key = 5;
139 static volatile uint8_t learn_mode = 0;
140 static volatile uint8_t drive_on = 0;
141
142 ISR(TIMER0_COMPA_vect) {
143
144 STOP_TIMER0;
145
146 shift_decoder <<= 1;
147 if (shift_zero & 2)
148 shift_decoder |= 1;
149 shift_zero <<= 1;
150 if (shift_sub & 0x80)
151 shift_zero |= 1;
152 shift_sub <<= 1;
153 if (SIGIN)
154 shift_sub |= 1;
155
156 bitno++;
157
158 if (bitno == 18) { /* Save first received word */
159 shift_sub_first = shift_sub;
160 shift_decoder_first = shift_decoder;
161 } else if (bitno == 36) {
162
163 if ((shift_sub == shift_sub_first) &&
164 (shift_decoder == shift_decoder_first)) {
165 uint8_t decoder = lookup_decoder(shift_decoder);
166 uint8_t command = lookup_command(shift_sub);
167 if (decoder) {
168 /* Congratulations, we have a valid command */
169 trigger();
170
171 if (learn_mode) {
172 if (command) {
173 our_decoder = decoder;
174 our_key = command;
175 learn_mode = 0;
176 eeprom_write_byte(&ee_our_decoder, our_decoder);
177 eeprom_write_byte(&ee_our_key, our_key);
178 }
179 } else {
180 if (decoder == our_decoder) {
181 if (command == our_key)
182 drive_on = 1;
183 if (command == 0)
184 drive_on = 0;
185 }
186 }
187 }
188 }
189 }
190 }
191
192 #define START_TIMER0 ({TCNT0 = 0; TCCR0B = 2;})
193 #define START_TIMER1 ({TCNT1 = 1; TCCR1 = 7; GTCCR |= 2;})
194 #define STOP_TIMER1 ({TCCR1 = 0; TCNT1 = 1;})
195
196 ISR(TIM1_OVF_vect)
197 {
198 STOP_TIMER1;
199 // if (!SIGIN)
200 // setpin(LED, 1);
201 bitno = 0;
202 MON_TIMEOUT(1);
203 }
204
205 ISR(TIM0_OVF_vect)
206 {
207 while(1) {
208 setpin(LED, 1);
209 _delay_ms(30);
210 setpin(LED, 1);
211 _delay_ms(30);
212 }
213
214 }
215
216 /* Shortest element seen 184 clocks */
217 /* Officially 208 clocks */
218 /* (1 + 7) * 208 clocks */
219
220 /* Pin change interrupt vector */
221 ISR(PCINT0_vect)
222 {
223 START_TIMER1;
224 MON_TIMEOUT(0);
225
226 if (SIGIN) {
227 START_TIMER0;
228 }
229 }
230
231
232
233 /******************************************************************************
234 *
235 * main() - The main routine
236 *
237 */
238
239 int main(void) {
240 uint8_t duty = 30;
241 uint8_t old_drive = 0;
242 setup_hw();
243 our_decoder = eeprom_read_byte(&ee_our_decoder);
244 our_key = eeprom_read_byte(&ee_our_key);
245 if (our_decoder == 0xff)
246 learn_mode = 1;
247 sei();
248 uint8_t i;
249 while (1) {
250 if (!learn_mode) {
251 if (old_drive == 0 && drive_on) {
252 setpin(DRIVE, 1);
253 _delay_ms(100);
254 }
255 old_drive = drive_on;
256 setpin(DRIVE, drive_on);
257 for (i = 0; i < duty; i++)
258 _delay_us(4);
259 for (i = 0; i < (255 - duty); i++) {
260 setpin(DRIVE, 0);
261 _delay_us(4);
262 }
263
264 if (!PINVAL(BTN))
265 learn_mode = 1;
266 } else {
267 setpin(LED, 1);
268 _delay_ms(30);
269 setpin(LED, 0);
270 _delay_ms(600);
271 }
272
273 /* Main loop here */
274 }
275 return 0;
276 }
277
278 /******************************************************************************
279 * The end :-)
280 */