+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/atomic.h>
+#include <util/delay.h>
+
+#include "uart.h"
+
+// which pin is the zener diode connected to
+#define read_scx_pin() (PINB & _BV(PB2))
+
+// how many consecutive readings have to be the same in order to
+// accept the pin value
+#define DENOISE_COUNT 4
+
+#ifndef F_CPU
+#error "F_CPU not defined. Set F_CPU in Makefile."
+#endif
+
+#define STEP (F_CPU/115200) // duration of one bit
+#define BYTE_PAUSE_MAX (20*STEP) // inter-packet length threshold
+
+void tc1_init()
+{
+ TCNT1 = 0;
+ TCCR1B = _BV(CS10); // run at F_CPU clock, no prescaling
+}
+
+// read the value until it is stable, i.e. until DENOISE_COUNT consecutive
+// readings give the same value
+static unsigned char read_scx_pin_denoised()
+{
+ unsigned char prev, count;
+
+ count = 0;
+ prev = read_scx_pin();
+
+ while (count < DENOISE_COUNT) {
+ unsigned char curr = read_scx_pin();
+ if (curr != prev) {
+ count = 0;
+ prev = curr;
+ } else {
+ ++count;
+ }
+ }
+ return prev;
+}
+
+// how long we waited for the start bit
+static unsigned char long_byte_wait;
+
+static unsigned char read_scx_byte()
+{
+ unsigned short curr_time, prev_time, interval;
+ unsigned char data, i, toolong = 0, step;
+
+retry:
+ // wait for 0, distinguish between inter- and intra-packet wait times
+ prev_time = TCNT1;
+
+ long_byte_wait = 0;
+ while (read_scx_pin_denoised()) {
+ curr_time = TCNT1;
+ if (curr_time - prev_time > BYTE_PAUSE_MAX)
+ long_byte_wait = 1;
+ }
+
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ // wait for 1 (end of the first start-bit),
+ // remember the length of the start bit
+ // in order to distinguish between car and other packets
+ prev_time = curr_time = TCNT1;
+ interval = 0;
+
+ while (!read_scx_pin_denoised()) { // wait for 1
+ curr_time = TCNT1;
+ interval = curr_time - prev_time;
+
+ if (interval >= 4*STEP) { // start bit too long
+ if (!toolong) {
+ uart_tx_byte(0x12, 0);
+ toolong = 1;
+ }
+ goto retry;
+ }
+ };
+
+ toolong = 0;
+
+ // start bit too short
+ if (interval < 3*STEP/4) {
+ uart_tx_byte(0x10, 0);
+ goto retry;
+ }
+
+ // car packets are at 57600 baud, start bit is twice as long
+ step = interval > (3*STEP/2) ? 2*STEP : STEP;
+
+ // advance into the middle of the next bit
+ curr_time += step / 2;
+
+ // start bit detected, now read the eight data bits
+ data = 0;
+ for (i = 0; i < 8; i++) {
+ data >>= 1;
+ while (TCNT1 - curr_time < step)
+ ;
+ data |= read_scx_pin_denoised() ? 0x80 : 0;
+ curr_time += step;
+ }
+
+ // read stop bit
+ while (TCNT1 - curr_time < step)
+ ;
+
+ if (!read_scx_pin_denoised()) // no stop bit?
+ uart_tx_byte(0x13, 0); // report error, but return data anyway
+ }; // ATOMIC
+ return data;
+}
+
+extern unsigned char version[], version_len;
+
+static void print_version()
+{
+ unsigned char i;
+
+ for (i = 0; i < version_len; i++)
+ uart_tx_byte(version[i], 0);
+}
+
+int main()
+{
+ unsigned char count = 0, pkt_len = 0;
+
+ uart_init();
+ tc1_init();
+
+ sei();
+
+ _delay_ms(1000);
+ print_version();
+ _delay_ms(500);
+
+ // main loop
+ while (1) {
+ unsigned char byte = read_scx_byte();
+
+ uart_tx_byte(byte, 0);
+
+ count++;
+ count &= 0x7F; // avoid overflow
+
+ // try to synchronize with the packet start
+ // - we need this in order to determine the packet length
+ // and in order to add 0x05 end byte after the packet
+ if (byte == 0x55 && long_byte_wait)
+ count = 1;
+
+ if (count == 2) {
+ if (byte >= 0x40 && byte <= 0x45) { // car pkt
+ pkt_len = 5;
+ } else {
+ pkt_len = 9;
+ }
+ }
+
+ if (count == pkt_len) {
+ uart_tx_byte(0x05, 0); // SEB interface compatibility
+ count = 0;
+ }
+ }
+ // NOTREACHED
+}
+