# $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
--- /dev/null
+#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;
+}
--- /dev/null
+#ifndef EEPROM_H_
+#define EEPROM_H_
+
+void eeprom_write(uint16_t, uint8_t);
+uint8_t eeprom_read(uint16_t);
+
+#endif
--- /dev/null
+#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);
+}
--- /dev/null
+#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
#include <avr/io.h>
#include <avr/interrupt.h>
+#include "lcd.h"
+#include "serial.h"
#define DATA_OUT PORTD2
#define CLOCK PIND3
#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();
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);
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)
#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);
input &= valid;
input |= state&~valid;
+ input &= (1<<(nibbles*4))-1;
if(input!=state)
{
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;
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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();
+}
--- /dev/null
+#ifndef TIMER_H_
+#define TIMER_H_
+
+typedef void TimerCallback(void);
+
+void timer_init(uint16_t, uint8_t);
+void timer_set_callback(TimerCallback *);
+
+#endif