From: Jan "Yenya" Kasprzak Date: Thu, 19 Dec 2013 14:20:26 +0000 (+0100) Subject: Initial import X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?p=scxreader.git;a=commitdiff_plain;h=7ae4a5ad045631000b46619275553da3427226cf Initial import --- 7ae4a5ad045631000b46619275553da3427226cf diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a5fb25f --- /dev/null +++ b/Makefile @@ -0,0 +1,72 @@ + +PROGRAM=scxreader +SRC=scxreader.c uart.c version.c +OBJ=$(SRC:.c=.o) + + +MCU=atmega328p +# AVRDUDE_MCU=$(MCU) +AVRDUDE_MCU=atmega328p +AVRDUDE_PROGRAMMER=arduino + +CFLAGS=-Wall -Os -mmcu=$(MCU) -DUSE_LOGGING=1 -DF_CPU=16000000UL -std=gnu99 +LDFLAGS= +AVRDUDE_FLAGS= -p$(AVRDUDE_MCU) -c $(AVRDUDE_PROGRAMMER) -P /dev/ttyUSB0 -b 57600 + +FORMAT=ihex + +CC=avr-gcc +OBJCOPY=avr-objcopy +OBJDUMP=avr-objdump +AVRDUDE=avrdude + +all: $(PROGRAM).hex $(PROGRAM).eep + +program: $(PROGRAM).hex $(PROGRAM).eep + $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$(PROGRAM).hex:i -U eeprom:w:$(PROGRAM).eep:i + +program_flash: $(PROGRAM).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$(PROGRAM).hex:i + +program_eeprom: $(PROGRAM).eep + $(AVRDUDE) $(AVRDUDE_FLAGS) eeprom:w:$(PROGRAM).eep:i + +dump_eeprom: + $(AVRDUDE) $(AVRDUDE_FLAGS) -U eeprom:r:eeprom.raw:r + od -tx1 eeprom.raw + +objdump: $(PROGRAM).elf + $(OBJDUMP) --disassemble $< + +.PRECIOUS : $(OBJ) $(PROGRAM).elf + +%.hex: %.elf + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +%.eep: %.elf + $(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --change-section-lma .eeprom=0 -O $(FORMAT) $< $@ + +%.elf: $(OBJ) + $(CC) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS) + +%.o: %.c lights.h Makefile + $(CC) -c $(CFLAGS) $< -o $@ + +%.s: %.c lights.h Makefile + $(CC) -S -c $(CFLAGS) $< -o $@ + +%.o: %.S + $(CC) -c $(CFLAGS) $< -o $@ + +clean: + rm -f $(PROGRAM).hex $(PROGRAM).eep $(PROGRAM).elf *.o *.s eeprom.raw \ + version.c + +version.c: + ./version.pl > version.c + +.PHONY: all clean dump_eeprom program program_flash program_eeprom objdump \ + version.c + + diff --git a/scxreader.c b/scxreader.c new file mode 100644 index 0000000..71e1f7e --- /dev/null +++ b/scxreader.c @@ -0,0 +1,175 @@ +#include +#include +#include +#include + +#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 +} + diff --git a/uart.c b/uart.c new file mode 100644 index 0000000..265c608 --- /dev/null +++ b/uart.c @@ -0,0 +1,113 @@ +/* + * Minimalistic TX-only compile-time configured asynchronous buffered + * UART communication for Arduino Nano (Atmega 88/168/328). + */ +#include +#include +#include +#include + +// user settable parameters start +#define UART_BAUD 115200 +#define TX_BUFFER (1 << 4) // this has to be a power of two +// user settable parameters end + +#define TX_BUFFER_MASK (TX_BUFFER-1) + +#if (TX_BUFFER & TX_BUFFER_MASK) +#error TX buffer size TX_BUFFER must be a power of two +#endif + +#ifndef F_CPU +#error F_CPU not defined, set it in Makefile +#endif + +#define UBRR_VAL ((F_CPU + 8UL * UART_BAUD) / (16UL*UART_BAUD) - 1) + +static volatile unsigned char buffer[TX_BUFFER]; +static volatile unsigned char b_head, b_tail, b_overrun; +/* + * b_head == b_tail: empty queue + * b_head + 1 == b_tail: full queue + * b_overrun is set but not currently handled in any way + */ + +void uart_init() +{ + UBRR0H = (unsigned char)(UBRR_VAL >> 8); + UBRR0L = (unsigned char)(UBRR_VAL & 0xFF); + + UCSR0C = _BV(USBS0) | _BV(UCSZ00) | _BV(UCSZ01); + UCSR0B = _BV(TXEN0); // enable TX only + + b_head = 0; + b_tail = 0; + b_overrun = 0; +} + +ISR(USART_UDRE_vect) // tx end irq +{ + // maybe handle overrun here? + if (b_head == b_tail) { // queue empty + UCSR0B &= ~_BV(UDRIE0); // disable IRQ + } else { + UDR0 = buffer[b_tail]; + b_tail = (b_tail + 1) & TX_BUFFER_MASK; + } +} + +// low-level byte sending +unsigned char uart_tx_byte(unsigned char byte, unsigned char wait) +{ + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + retry: + if (((b_head + 1) & TX_BUFFER_MASK) == b_tail) { + if (wait) { + NONATOMIC_BLOCK(NONATOMIC_FORCEOFF) { + while (((b_head + 1) & TX_BUFFER_MASK) + == b_tail) + ; + } + goto retry; + } + + b_overrun = 1; + return 0; + } + + buffer[b_head] = byte; + b_head = (b_head + 1) & TX_BUFFER_MASK; + + UCSR0B |= _BV(UDRIE0); + } + return 1; +} + +// ASCII hex bytes, for debugging purposes + +#define hex_nibble(x) ((x) < 10 ? '0' + (x) : 'A' + (x) - 10) + +unsigned char uart_tx_hex(unsigned char byte, unsigned char wait) +{ + if ( + uart_tx_byte(hex_nibble(byte >> 4), wait) + && uart_tx_byte(hex_nibble(byte & 0xF), wait) + ) + return 1; + else + return 0; +} + +unsigned char uart_tx_hex2(unsigned short word, unsigned char wait) +{ + if ( + uart_tx_byte(hex_nibble((word >> 12) & 0xF), wait) + && uart_tx_byte(hex_nibble((word >> 8) & 0xF), wait) + && uart_tx_byte(hex_nibble((word >> 4) & 0xF), wait) + && uart_tx_byte(hex_nibble( word & 0xF), wait) + ) + return 1; + else + return 0; +} + diff --git a/uart.h b/uart.h new file mode 100644 index 0000000..c091798 --- /dev/null +++ b/uart.h @@ -0,0 +1,10 @@ +#ifndef UART_H__ +#define UART_H__ 1 + +void uart_init(); +unsigned char uart_tx_byte(unsigned char byte, unsigned char wait); +unsigned char uart_tx_hex(unsigned char byte, unsigned char wait); +unsigned char uart_tx_hex2(unsigned short word, unsigned char wait); + +#endif /* !UART_H__ */ + diff --git a/version.pl b/version.pl new file mode 100755 index 0000000..e73dbe5 --- /dev/null +++ b/version.pl @@ -0,0 +1,31 @@ +#!/usr/bin/perl -w + +use strict; +use POSIX qw(strftime); + +my $git = `git rev-parse --short HEAD`; +chomp $git; + +my $now = strftime('%Y%m%d', localtime(time)); + +print <