]> git.tdb.fi Git - model-railway-devices.git/commitdiff
Add some utility functions for interfacing with hardware
authorMikko Rasa <tdb@tdb.fi>
Sat, 25 Sep 2010 08:46:20 +0000 (08:46 +0000)
committerMikko Rasa <tdb@tdb.fi>
Sat, 25 Sep 2010 08:46:20 +0000 (08:46 +0000)
Improve makefile to support uploading to Arduino Mini
Expand s88w protocol to support multiple transmitters
Add lcd output to s88w-r for debugging

firmware/Makefile
firmware/eeprom.c [new file with mode: 0644]
firmware/eeprom.h [new file with mode: 0644]
firmware/lcd.c [new file with mode: 0644]
firmware/lcd.h [new file with mode: 0644]
firmware/s88w-r.c
firmware/s88w-t.c
firmware/serial.c [new file with mode: 0644]
firmware/serial.h [new file with mode: 0644]
firmware/timer.c [new file with mode: 0644]
firmware/timer.h [new file with mode: 0644]

index 68d6f4c922be4ec5b8546e6c4a5cdca7e0339729..c17d9919ecf938b8bf4608768ae5dbeddcf7fc3a 100644 (file)
@@ -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 (file)
index 0000000..9027ed0
--- /dev/null
@@ -0,0 +1,29 @@
+#include <avr/io.h>
+#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 (file)
index 0000000..903ce96
--- /dev/null
@@ -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 (file)
index 0000000..8c722d8
--- /dev/null
@@ -0,0 +1,83 @@
+#include <avr/io.h>
+#include <util/delay.h>
+
+#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 (file)
index 0000000..16d64d5
--- /dev/null
@@ -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
index dac0cfcaf85948eeebd70f2eaebae030d9fe3ac6..f64445d81c5a33f12b8f82ef1cae2ea21c7fba5d 100644 (file)
@@ -25,6 +25,8 @@ D5 - S88 RESET
 
 #include <avr/io.h>
 #include <avr/interrupt.h>
+#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<<shift)) | (bits<<shift);
+                                       latch[j/2] = input[j/2];
+                               }
+                       }
+
+                       lcd_gotoxy(0, 0);
+                       for(i=10; i--;)
+                       {
+                               lcd_write(hexdigit(input[i]>>4));
+                               lcd_write(hexdigit(input[i]));
+                       }
                }
+               rx_fill = 0xFF;
+       }
+       else
+       {
+               if(rx_fill<sizeof(rx_buf))
+                       rx_buf[rx_fill++] = c;
+               else
+                       rx_fill = 0xFF;
        }
+
+       lcd_gotoxy(log_pos%20, 1+log_pos/20);
+       lcd_write(c);
+       ++log_pos;
+       if(log_pos>=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)
index e88a1f509eb6fc24aec4c4a8e05e83bce6a4fd7e..e394d603032fe07b14047f554abc633afb451c7a 100644 (file)
@@ -24,48 +24,55 @@ activate.
 
 #include <avr/io.h>
 #include <avr/interrupt.h>
+#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<sizeof(rx_buf))
+                       rx_buf[rx_fill++] = c;
+               else
+                       rx_fill = 0xFF;
+       }
 }
 
 void send_state(void)
 {
-       write_serial(':');
-       write_serial(hexdigit(state>>4));
-       write_serial(hexdigit(state&0xF));
-}
-
-void write_serial(uint8_t c)
-{
-       while(!(UCSR0A&(1<<UDRE0))) ;
-       UDR0 = c;
+       uint8_t i;
+       serial_write(':');
+       serial_write(hexdigit(offset>>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 (file)
index 0000000..b7219ae
--- /dev/null
@@ -0,0 +1,36 @@
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#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<<UDRE0))) ;
+       UDR0 = c;
+}
+
+ISR(USART_RX_vect)
+{
+       serial_callback(UDR0);
+}
diff --git a/firmware/serial.h b/firmware/serial.h
new file mode 100644 (file)
index 0000000..e62b0a9
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef SERIAL_H_
+#define SERIAL_H_
+
+typedef void SerialCallback(uint8_t);
+
+void serial_init(uint16_t);
+void serial_set_callback(SerialCallback *);
+void serial_write(uint8_t);
+
+#endif
diff --git a/firmware/timer.c b/firmware/timer.c
new file mode 100644 (file)
index 0000000..043dff8
--- /dev/null
@@ -0,0 +1,55 @@
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#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 (file)
index 0000000..fa2cd86
--- /dev/null
@@ -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