]> www.fi.muni.cz Git - scxreader.git/blob - scxreader.c
scxreader.sch: schematics in gschem
[scxreader.git] / scxreader.c
1 #include <avr/io.h>
2 #include <avr/interrupt.h>
3 #include <util/atomic.h>
4 #include <util/delay.h>
5
6 #include "uart.h"
7
8 // which pin is the zener diode connected to
9 #define read_scx_pin() (PINB & _BV(PB2))
10
11 // how many consecutive readings have to be the same in order to
12 // accept the pin value
13 #define DENOISE_COUNT 4
14
15 #ifndef F_CPU
16 #error "F_CPU not defined. Set F_CPU in Makefile."
17 #endif
18
19 #define STEP (F_CPU/115200)             // duration of one bit
20 #define BYTE_PAUSE_MAX (20*STEP)        // inter-packet length threshold
21
22 void tc1_init()
23 {
24         TCNT1 = 0;
25         TCCR1B = _BV(CS10); // run at F_CPU clock, no prescaling
26 }
27
28 // read the value until it is stable, i.e. until DENOISE_COUNT consecutive
29 // readings give the same value
30 static unsigned char read_scx_pin_denoised()
31 {
32         unsigned char prev, count;
33
34         count = 0;
35         prev = read_scx_pin();
36
37         while (count < DENOISE_COUNT) {
38                 unsigned char curr = read_scx_pin();
39                 if (curr != prev) {
40                         count = 0;
41                         prev = curr;
42                 } else {
43                         ++count;
44                 }
45         }
46         return prev;
47 }
48
49 // how long we waited for the start bit
50 static unsigned char long_byte_wait;
51
52 static unsigned char read_scx_byte()
53 {
54         unsigned short curr_time, prev_time, interval;
55         unsigned char data, i, toolong = 0, step;
56
57 retry:
58         // wait for 0, distinguish between inter- and intra-packet wait times
59         prev_time = TCNT1;
60
61         long_byte_wait = 0;
62         while (read_scx_pin_denoised()) {
63                 curr_time = TCNT1;
64                 if (curr_time - prev_time > BYTE_PAUSE_MAX)
65                         long_byte_wait = 1;
66         }
67
68         ATOMIC_BLOCK(ATOMIC_FORCEON) {
69                 // wait for 1 (end of the first start-bit),
70                 // remember the length of the start bit
71                 // in order to distinguish between car and other packets
72                 prev_time = curr_time = TCNT1;
73                 interval = 0;
74
75                 while (!read_scx_pin_denoised()) { // wait for 1
76                         curr_time = TCNT1;
77                         interval = curr_time - prev_time;
78
79                         if (interval >= 4*STEP) { // start bit too long
80                                 if (!toolong) {
81                                         uart_tx_byte(0x12, 0);
82                                         toolong = 1;
83                                 }
84                                 goto retry;
85                         }
86                 };
87
88                 toolong = 0;
89
90                 // start bit too short
91                 if (interval < 3*STEP/4) {
92                         uart_tx_byte(0x10, 0);
93                         goto retry;
94                 }
95
96                 // car packets are at 57600 baud, start bit is twice as long
97                 step = interval > (3*STEP/2) ? 2*STEP : STEP;
98
99                 // advance into the middle of the next bit
100                 curr_time += step / 2;
101
102                 // start bit detected, now read the eight data bits
103                 data = 0;
104                 for (i = 0; i < 8; i++) {
105                         data >>= 1;
106                         while (TCNT1 - curr_time < step)
107                                 ;
108                         data |= read_scx_pin_denoised() ? 0x80 : 0;
109                         curr_time += step;
110                 }
111
112                 // read stop bit
113                 while (TCNT1 - curr_time < step)
114                         ;
115
116                 if (!read_scx_pin_denoised()) // no stop bit?
117                         uart_tx_byte(0x13, 0); // report error, but return data anyway
118         }; // ATOMIC
119         return data;
120 }
121
122 extern unsigned char version[], version_len;
123
124 static void print_version()
125 {
126         unsigned char i;
127
128         for (i = 0; i < version_len; i++)
129                 uart_tx_byte(version[i], 0);
130 }
131
132 int main()
133 {
134         unsigned char count = 0, pkt_len = 0;
135
136         uart_init();
137         tc1_init();
138
139         sei();
140
141         _delay_ms(1000);
142         print_version();
143         _delay_ms(500);
144
145         // main loop
146         while (1) {
147                 unsigned char byte = read_scx_byte();
148
149                 uart_tx_byte(byte, 0);
150
151                 count++;
152                 count &= 0x7F; // avoid overflow
153
154                 // try to synchronize with the packet start
155                 // - we need this in order to determine the packet length
156                 // and in order to add 0x05 end byte after the packet
157                 if (byte == 0x55 && long_byte_wait)
158                         count = 1;
159
160                 if (count == 2) {
161                         if (byte >= 0x40 && byte <= 0x45) { // car pkt
162                                 pkt_len = 5;
163                         } else {
164                                 pkt_len = 9;
165                         }
166                 }
167
168                 if (count == pkt_len) {
169                         uart_tx_byte(0x05, 0); // SEB interface compatibility
170                         count = 0;
171                 }
172         }
173         // NOTREACHED
174 }
175