From: Mikko Rasa Date: Sat, 25 Sep 2010 08:46:20 +0000 (+0000) Subject: Add some utility functions for interfacing with hardware X-Git-Url: http://git.tdb.fi/?p=model-railway-devices.git;a=commitdiff_plain;h=d8a31ed675778c08ca781beb62863c62d6f0bd94 Add some utility functions for interfacing with hardware Improve makefile to support uploading to Arduino Mini Expand s88w protocol to support multiple transmitters Add lcd output to s88w-r for debugging --- diff --git a/firmware/Makefile b/firmware/Makefile index 68d6f4c..c17d991 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -1,23 +1,45 @@ # $Id$ -MCU = atmega328p -CLOCK = 16000000 -CFLAGS = -Wall -Os -ffunction-sections -fdata-sections -mmcu=$(MCU) -DF_CPU=$(CLOCK) -LDFLAGS = -Os -Wl,--gc-sections -mmcu=$(MCU) +MCU := atmega328p +CLOCK := 16000000 +CC := avr-gcc +CFLAGS := -Wall -Os -ffunction-sections -fdata-sections -mmcu=$(MCU) -DF_CPU=$(CLOCK) +LDFLAGS := -Os -Wl,--gc-sections -mmcu=$(MCU) +AVRDUDE := avrdude +OBJCOPY := avr-objcopy +ifeq ($(MCU),atmega168) +BAUD := 19200 +else +BAUD := 57600 +endif help: @echo "Targets:" @echo " %.hex: Build firmware from %.c" @echo " upload-%: Upload firmware to AVR" + @echo " clean: Clean all built files" + @echo + @echo "Variables:" + @echo " MCU: Microcontroller type" + @echo " Arduino Duemilanove = atmega328p (default)" + @echo " Arduino Mini = atmega168" %.hex: %.elf - avr-objcopy -O ihex $< $@ + $(OBJCOPY) -O ihex $< $@ %.elf: %.o - avr-gcc $(LDFLAGS) -o $@ $< + $(CC) $(LDFLAGS) -o $@ $^ %.o: %.c - avr-gcc -c $(CFLAGS) -o $@ $< + $(CC) -c $(CFLAGS) -o $@ $< upload-%: %.hex - avrdude -p$(MCU) -cstk500v1 -P/dev/ttyUSB0 -b57600 -D -Uflash:w:$<:i + $(AVRDUDE) -p$(MCU) -carduino -P/dev/ttyUSB0 -b$(BAUD) -D -Uflash:w:$<:i + +clean: + $(RM) *.hex + $(RM) *.elf + $(RM) *.o + +s88w-t.elf: serial.o timer.o eeprom.o +s88w-r.elf: serial.o lcd.o diff --git a/firmware/eeprom.c b/firmware/eeprom.c new file mode 100644 index 0000000..9027ed0 --- /dev/null +++ b/firmware/eeprom.c @@ -0,0 +1,29 @@ +#include +#include "eeprom.h" + +#define BIT(n) (1<<(n)) + +static void eeprom_wait(void) +{ + while(EECR&BIT(EEPE)) ; +} + +void eeprom_write(uint16_t addr, uint8_t data) +{ + eeprom_wait(); + EEARH = addr>>8; + EEARL = addr; + EEDR = data; + EECR = BIT(EEMPE); + EECR |= BIT(EEPE); + eeprom_wait(); +} + +uint8_t eeprom_read(uint16_t addr) +{ + eeprom_wait(); + EEARH = addr>>8; + EEARL = addr; + EECR = BIT(EERE); + return EEDR; +} diff --git a/firmware/eeprom.h b/firmware/eeprom.h new file mode 100644 index 0000000..903ce96 --- /dev/null +++ b/firmware/eeprom.h @@ -0,0 +1,7 @@ +#ifndef EEPROM_H_ +#define EEPROM_H_ + +void eeprom_write(uint16_t, uint8_t); +uint8_t eeprom_read(uint16_t); + +#endif diff --git a/firmware/lcd.c b/firmware/lcd.c new file mode 100644 index 0000000..8c722d8 --- /dev/null +++ b/firmware/lcd.c @@ -0,0 +1,83 @@ +#include +#include + +#define USE_SHIFTREG 1 + +#define NOP() __asm__("nop") + +void lcd_init(void); +void lcd_on(void); +void lcd_clear(void); +void lcd_goto(uint8_t); +void lcd_write(uint8_t); + +void lcd_clock(void) +{ + _delay_us(1); + PORTB |= 0x02; + _delay_us(1); + PORTB &= ~0x02; +} + +void lcd_set_data(uint8_t d) +{ +#ifdef USE_SHIFTREG + uint8_t i; + for(i=0; i<8; ++i) + { + PORTB = (PORTB&~0x04)|((d&1)<<2); + PORTB |= 0x08; + NOP(); + PORTB &= ~0x08; + d >>= 1; + } +#else + PORTD = (PORTD&0x3F) | (d<<6); + PORTB = (PORTB&0xC0) | (d>>2); +#endif +} + +void lcd_command(uint8_t c) +{ + PORTB &= ~0x01; + lcd_set_data(c); + lcd_clock(); +} + +void lcd_init(void) +{ + DDRB |= 0x0F; + PORTB &= ~0x0F; + _delay_ms(20); + lcd_command(0x38); + _delay_us(50); +} + +void lcd_on(void) +{ + lcd_command(0x0C); + _delay_us(50); +} + +void lcd_clear(void) +{ + lcd_command(0x01); + _delay_us(1500); +} + +void lcd_gotoxy(uint8_t x, uint8_t y) +{ + uint8_t a = x+10*y; + if(y&1) + a += 54; + lcd_command(a|0x80); + _delay_us(50); +} + +void lcd_write(uint8_t c) +{ + PORTB |= 0x01; + lcd_set_data(c); + lcd_clock(); + _delay_us(50); +} diff --git a/firmware/lcd.h b/firmware/lcd.h new file mode 100644 index 0000000..16d64d5 --- /dev/null +++ b/firmware/lcd.h @@ -0,0 +1,10 @@ +#ifndef LCD_H_ +#define LCD_H_ + +void lcd_init(void); +void lcd_on(void); +void lcd_clear(void); +void lcd_gotoxy(uint8_t, uint8_t); +void lcd_write(uint8_t); + +#endif diff --git a/firmware/s88w-r.c b/firmware/s88w-r.c index dac0cfc..f64445d 100644 --- a/firmware/s88w-r.c +++ b/firmware/s88w-r.c @@ -25,6 +25,8 @@ D5 - S88 RESET #include #include +#include "lcd.h" +#include "serial.h" #define DATA_OUT PORTD2 #define CLOCK PIND3 @@ -33,28 +35,33 @@ D5 - S88 RESET #define BIT(n) (1<<(n)) +void receive(uint8_t); +uint8_t hexdigit(uint8_t); uint8_t decode_hex(uint8_t); -volatile uint8_t rx_buf[3]; -volatile uint8_t rx_fill = 0; -volatile uint8_t input = 0; -volatile uint8_t latch = 0; +volatile uint8_t rx_buf[7]; +volatile uint8_t rx_fill = 0xFF; +volatile uint8_t input[128] = { 0 }; +volatile uint8_t latch[128] = { 0 }; +uint8_t log_pos = 0; int main() { uint8_t clock_high = 0; uint8_t bits = 0; + uint8_t n_bits = 8; + uint8_t offset = 0; DDRD = 0x06; // 00000110 PIND = 0xC0; // 11000000 DDRB = 0x20; // 00100000 PINB = 0x1F; // 00011111 - // 9600 baud, 8N1 - UBRR0H = 0; - UBRR0L = 103; - UCSR0C = BIT(UCSZ00) | BIT(UCSZ01); - UCSR0B = BIT(RXEN0) | BIT(TXEN0) | BIT(RXCIE0); + serial_init(9600); + serial_set_callback(receive); + lcd_init(); + lcd_on(); + lcd_clear(); sei(); @@ -69,9 +76,21 @@ int main() if(!clock_high) { if(d_pins&BIT(LOAD)) - bits = latch; + { + offset = 0; + bits = latch[0]; + n_bits = 8; + } else + { bits >>= 1; + if(!--n_bits) + { + ++offset; + bits = latch[offset]; + n_bits = 8; + } + } if(bits&1) PORTD |= BIT(DATA_OUT); @@ -85,28 +104,77 @@ int main() clock_high = 0; if(d_pins&BIT(RESET)) - latch = input; + { + uint8_t i; + for(i=0; i<128; ++i) + latch[i] = input[i]; + } } } -ISR(USART_RX_vect) +void receive(uint8_t c) { - uint8_t c = UDR0; - if(rx_fill==0) + if(rx_fill==0xFF) { if(c==':') - rx_buf[rx_fill++] = c; + rx_fill = 0; } - else + else if(c=='.') { - rx_buf[rx_fill++] = c; - if(rx_buf[0]==':' && rx_fill==3) + if(rx_fill>=4) { - input = (decode_hex(rx_buf[1])<<4) | decode_hex(rx_buf[2]); - latch |= input; - rx_fill = 0; + uint16_t offset; + uint8_t nibbles; + uint8_t i; + + offset = (decode_hex(rx_buf[0])<<8) | (decode_hex(rx_buf[1])<<4) | decode_hex(rx_buf[2]); + nibbles = (offset&3); + offset >>= 2; + if(rx_fill>3+nibbles) + { + for(i=0; i<=nibbles; ++i) + { + uint16_t j = offset+nibbles-i; + uint8_t shift = 4*(j&1); + uint8_t bits = decode_hex(rx_buf[3+i]); + input[j/2] = (input[j/2]&~(0xF<>4)); + lcd_write(hexdigit(input[i])); + } } + rx_fill = 0xFF; + } + else + { + if(rx_fill=60) + log_pos = 0; + lcd_gotoxy(log_pos%20, 1+log_pos/20); + lcd_write(255); +} + +uint8_t hexdigit(uint8_t n) +{ + n &= 0xF; + if(n<10) + return '0'+n; + else + return 'A'+(n-0xA); } uint8_t decode_hex(uint8_t c) diff --git a/firmware/s88w-t.c b/firmware/s88w-t.c index e88a1f5..e394d60 100644 --- a/firmware/s88w-t.c +++ b/firmware/s88w-t.c @@ -24,48 +24,55 @@ activate. #include #include +#include "eeprom.h" +#include "serial.h" +#include "timer.h" #define BIT(n) (1<<(n)) +void receive(uint8_t); void send_state(void); -void write_serial(uint8_t); uint8_t hexdigit(uint8_t); +uint8_t decode_hex(uint8_t); -volatile uint8_t state = 0; +uint8_t rx_buf[4]; +uint8_t rx_fill = 0xFF; +volatile uint8_t nibbles = 2; +volatile uint8_t offset = 0; +volatile uint16_t state = 0; int main() { + if(eeprom_read(0)==0xA5) + { + offset = eeprom_read(1)<<8; + offset |= eeprom_read(2); + nibbles = eeprom_read(3); + } + DDRD = 0x02; // 00000010 PORTD = 0xFC; // 11111100 - DDRB = 0x20; // 00100000 + DDRB = 0x20; // 00000000 PORTB = 0x3F; // 00111111 - // 9600 baud, 8N1 - UBRR0H = 0; - UBRR0L = 103; - UCSR0C = BIT(UCSZ00) | BIT(UCSZ01); - UCSR0B = BIT(RXEN0) | BIT(TXEN0); - - // 0.5 Hz (CK/1024, TOP=31250) - TCCR1A = 0; - TCCR1B = BIT(WGM12) | BIT(CS12) | BIT(CS10); - OCR1AH = 122; - OCR1AL = 18; - TIMSK1 = BIT(OCIE1A); + serial_init(9600); + serial_set_callback(receive); + timer_init(1, 2); + timer_set_callback(send_state); sei(); while(1) { uint8_t i; - uint8_t input = 0; - uint8_t valid = 0xFF; + uint16_t input = 0; + uint16_t valid = 0xFFF; for(i=0; i<100; ++i) { - uint8_t pins; + uint16_t pins; - pins = ~((PIND>>2) | (PINB<<6)); + pins = ~((PIND>>2) | ((PINB&0x3F)<<6)); if(i==0) input = pins; valid &= ~(pins^input); @@ -73,6 +80,7 @@ int main() input &= valid; input |= state&~valid; + input &= (1<<(nibbles*4))-1; if(input!=state) { @@ -84,28 +92,64 @@ int main() return 0; } -ISR(TIMER1_COMPA_vect) +void receive(uint8_t c) { - send_state(); + if(rx_fill==0xFF) + { + if(c==':') + rx_fill = 0; + } + else if(c=='.') + { + if(rx_buf[0]=='S' && rx_fill==4) + { + offset = (decode_hex(rx_buf[1])<<8) | (decode_hex(rx_buf[2])<<4) | decode_hex(rx_buf[3]); + nibbles = (offset&3)+1; + offset &= 0xFFC; + + eeprom_write(0, 0xA5); + eeprom_write(1, offset>>8); + eeprom_write(2, offset); + eeprom_write(3, nibbles); + } + rx_fill = 0xFF; + } + else + { + if(rx_fill>4)); - write_serial(hexdigit(state&0xF)); -} - -void write_serial(uint8_t c) -{ - while(!(UCSR0A&(1<>8)); + serial_write(hexdigit(offset>>4)); + serial_write(hexdigit(offset|(nibbles-1))); + for(i=nibbles; i--;) + serial_write(hexdigit(state>>(i*4))); + serial_write('.'); } uint8_t hexdigit(uint8_t n) { + n &= 0xF; if(n<10) return '0'+n; else return 'A'+(n-0xA); } + +uint8_t decode_hex(uint8_t c) +{ + if(c>='0' && c<='9') + return c-'0'; + else if(c>='A' && c<='F') + return 0xA+(c-'A'); + else + return 0; +} diff --git a/firmware/serial.c b/firmware/serial.c new file mode 100644 index 0000000..b7219ae --- /dev/null +++ b/firmware/serial.c @@ -0,0 +1,36 @@ +#include +#include +#include "serial.h" + +#define BIT(n) (1<<(n)) + +static SerialCallback *serial_callback = 0; + +void serial_init(uint16_t baud) +{ + DDRD = (DDRD&~0x03) | 0x02; + PORTD &= ~0x03; + + baud = (F_CPU/16+baud/2)/baud-1; + UBRR0H = baud>>8; + UBRR0L = baud; + UCSR0C = BIT(UCSZ00) | BIT(UCSZ01); // 8N1 + UCSR0B = BIT(RXEN0) | BIT(TXEN0); +} + +void serial_set_callback(SerialCallback *cb) +{ + serial_callback = cb; + UCSR0B |= BIT(RXCIE0); +} + +void serial_write(uint8_t c) +{ + while(!(UCSR0A&(1< +#include +#include "timer.h" + +#define BIT(n) (1<<(n)) + +static TimerCallback *timer_callback; + +void timer_init(uint16_t freq_p, uint8_t freq_q) +{ + uint32_t period = F_CPU*freq_q/freq_p; + uint8_t cs; + + if(period<0x10000) + { + cs = BIT(CS10); + } + else if(period<0x80000) + { + cs = BIT(CS11); + period /= 8; + } + else if(period<0x400000) + { + cs = BIT(CS11) | BIT(CS10); + period /= 64; + } + else if(period<0x1000000) + { + cs = BIT(CS12); + period /= 256; + } + else + { + cs = BIT(CS12) | BIT(CS10); + period /= 1024; + if(period>0xFFFF) + period = 0xFFFF; + } + TCCR1A = 0; + TCCR1B = BIT(WGM12) | cs; + OCR1AH = period>>8; + OCR1AL = period; +} + +void timer_set_callback(TimerCallback *cb) +{ + timer_callback = cb; + TIMSK1 |= BIT(OCIE1A); +} + +ISR(TIMER1_COMPA_vect) +{ + timer_callback(); +} diff --git a/firmware/timer.h b/firmware/timer.h new file mode 100644 index 0000000..fa2cd86 --- /dev/null +++ b/firmware/timer.h @@ -0,0 +1,9 @@ +#ifndef TIMER_H_ +#define TIMER_H_ + +typedef void TimerCallback(void); + +void timer_init(uint16_t, uint8_t); +void timer_set_callback(TimerCallback *); + +#endif