From 238cd5e5bd9c6fa86ae85f5eb7a8ce00158f2f7c Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 27 Oct 2010 16:14:17 +0000 Subject: [PATCH] =?utf8?q?Support=20multiple=20timers=20(currently=200=20a?= =?utf8?q?nd=201)=20Provide=20functions=20for=20setting=20timers=20in=20?= =?utf8?q?=C2=B5s=20as=20well=20as=20Hz=20Rewrite=20interrupt=20callbacks?= =?utf8?q?=20as=20macros=20that=20directly=20create=20the=20ISR=20Add=20an?= =?utf8?q?=20async=20mode=20for=20LCD=20Fix=20DDRB=20in=20s88w-t?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- firmware/Makefile | 5 +- firmware/delay.h | 52 +++++++++++++ firmware/eeprom.c | 7 ++ firmware/eeprom.h | 7 ++ firmware/lcd.c | 184 +++++++++++++++++++++++++++++++++++++++------- firmware/lcd.h | 8 +- firmware/s88w-r.c | 32 ++++---- firmware/s88w-t.c | 22 +++++- firmware/serial.c | 23 ++---- firmware/serial.h | 17 ++++- firmware/timer.c | 125 ++++++++++++++++++++++--------- firmware/timer.h | 20 ++++- 12 files changed, 398 insertions(+), 104 deletions(-) create mode 100644 firmware/delay.h diff --git a/firmware/Makefile b/firmware/Makefile index c17d991..3f4f4c6 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -3,7 +3,8 @@ MCU := atmega328p CLOCK := 16000000 CC := avr-gcc -CFLAGS := -Wall -Os -ffunction-sections -fdata-sections -mmcu=$(MCU) -DF_CPU=$(CLOCK) +FEATURES := LCD_SHIFTREG LCD_ASYNC +CFLAGS := -Wall -Os -ffunction-sections -fdata-sections -mmcu=$(MCU) -DF_CPU=$(CLOCK) $(patsubst %,-D%,$(FEATURES)) LDFLAGS := -Os -Wl,--gc-sections -mmcu=$(MCU) AVRDUDE := avrdude OBJCOPY := avr-objcopy @@ -42,4 +43,4 @@ clean: $(RM) *.o s88w-t.elf: serial.o timer.o eeprom.o -s88w-r.elf: serial.o lcd.o +s88w-r.elf: serial.o lcd.o timer.o diff --git a/firmware/delay.h b/firmware/delay.h new file mode 100644 index 0000000..1798cec --- /dev/null +++ b/firmware/delay.h @@ -0,0 +1,52 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#ifndef DELAY_H_ +#define DELAY_H_ + +static inline void __attribute__((always_inline)) delay_loop8(uint8_t count) +{ + __asm__ volatile ( + "1: dec %0" "\n\t" + "brne 1b" + : "=r" (count) + : "0" (count) + ); +} + +static inline void __attribute__((always_inline)) delay_loop16(uint16_t count) +{ + __asm__ volatile ( + "1: sbiw %0, 1" "\n\t" + "brne 1b" + : "=r" (count) + : "0" (count) + ); +} + +static inline void __attribute__((always_inline)) delay_us(uint16_t us) +{ + uint16_t clocks = F_CPU/1000000*us; + if(clocks<768) + delay_loop8(clocks/3); + else + delay_loop16(clocks/4); +} + +static inline void __attribute__((always_inline)) delay_ms(uint16_t ms) +{ + if(ms<0x40000/(F_CPU/1000)) + delay_loop16(F_CPU/1000*ms/4); + else + { + uint16_t i = ms*10; + while(--i) + delay_loop16(F_CPU/40000); + } +} + +#endif diff --git a/firmware/eeprom.c b/firmware/eeprom.c index 9027ed0..b9010f0 100644 --- a/firmware/eeprom.c +++ b/firmware/eeprom.c @@ -1,3 +1,10 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + #include #include "eeprom.h" diff --git a/firmware/eeprom.h b/firmware/eeprom.h index 903ce96..97527d6 100644 --- a/firmware/eeprom.h +++ b/firmware/eeprom.h @@ -1,3 +1,10 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + #ifndef EEPROM_H_ #define EEPROM_H_ diff --git a/firmware/lcd.c b/firmware/lcd.c index 8c722d8..6ba00d5 100644 --- a/firmware/lcd.c +++ b/firmware/lcd.c @@ -1,27 +1,70 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL + +ATMega pinout (with LCD_SHIFTREG): +B0 - HD44780 RS +B1 - HD44780 clock +B2 - shift reg data +B3 - shift reg clock + +ATMega pinout (without LCD_SHIFTREG): +D4 - HD44780 RS +D5 - HD44780 clock +D6 - HD44780 data 0 +D7 - HD44780 data 1 +B0 - HD44780 data 2 +B1 - HD44780 data 3 +B2 - HD44780 data 4 +B3 - HD44780 data 5 +B4 - HD44780 data 6 +B5 - HD44780 data 7 + +In both cases, HD44780 R/W needs to be connected to ground. +*/ #include -#include +#include "delay.h" +#include "lcd.h" +#include "timer.h" + +#ifdef LCD_SHIFTREG +#define CLOCK_PORT PORTB +#define CLOCK_BIT 0x02 +#define REGSEL_PORT PORTB +#define REGSEL_BIT 0x01 +#else +#define CLOCK_PORT PORTD +#define CLOCK_BIT 0x20 +#define REGSEL_PORT PORTD +#define REGSEL_BIT 0x10 +#endif -#define USE_SHIFTREG 1 +#ifndef LCD_BUFSIZE +#define LCD_BUFSIZE 32 +#endif #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); +#ifdef LCD_ASYNC +static volatile uint8_t lcd_busy = 0; +static uint8_t lcd_buffer[LCD_BUFSIZE]; +static volatile uint8_t lcd_buf_head = 0; +static volatile uint8_t lcd_buf_tail = 0; +#endif -void lcd_clock(void) +static void lcd_clock(void) { - _delay_us(1); - PORTB |= 0x02; - _delay_us(1); - PORTB &= ~0x02; + delay_us(1); + CLOCK_PORT |= CLOCK_BIT; + delay_us(1); + CLOCK_PORT &= ~CLOCK_BIT; } -void lcd_set_data(uint8_t d) +static void lcd_set_data(uint8_t d) { -#ifdef USE_SHIFTREG +#ifdef LCD_SHIFTREG uint8_t i; for(i=0; i<8; ++i) { @@ -37,32 +80,100 @@ void lcd_set_data(uint8_t d) #endif } -void lcd_command(uint8_t c) +static void lcd_command(uint8_t c) { - PORTB &= ~0x01; + REGSEL_PORT &= ~REGSEL_BIT; lcd_set_data(c); lcd_clock(); } +static void lcd_delay(uint16_t us) +{ +#ifdef LCD_ASYNC + lcd_busy = 1; + timer_start_us(0, us); +#else + delay_us(us); +#endif +} + +#ifdef LCD_ASYNC +static void lcd_wait(void) +{ + while(lcd_busy) ; +} + +static void lcd_buffer_push(uint8_t byte) +{ + while((lcd_buf_head+1)%sizeof(lcd_buffer)==lcd_buf_tail) ; + lcd_buffer[lcd_buf_head] = byte; + lcd_buf_head = (lcd_buf_head+1)%sizeof(lcd_buffer); +} + +static uint8_t lcd_buffer_pop(void) +{ + uint8_t byte = lcd_buffer[lcd_buf_tail]; + lcd_buf_tail = (lcd_buf_tail+1)%sizeof(lcd_buffer); + return byte; +} + +static void lcd_timer(void) +{ + if(lcd_busy) + { + if(lcd_buf_head!=lcd_buf_tail) + { + uint8_t byte = lcd_buffer_pop(); + if(byte==0xFF) + { + REGSEL_PORT |= REGSEL_BIT; + byte = lcd_buffer_pop(); + } + else if(byte>=0x80) + REGSEL_PORT &= ~REGSEL_BIT; + else + REGSEL_PORT |= REGSEL_BIT; + lcd_set_data(byte); + lcd_clock(); + lcd_delay(50); + } + else + { + lcd_busy = 0; + timer_stop(0); + } + } +} + +TIMER_SET_CALLBACK(0, lcd_timer) +#endif + void lcd_init(void) { +#ifdef LCD_SHIFTREG DDRB |= 0x0F; PORTB &= ~0x0F; - _delay_ms(20); - lcd_command(0x38); - _delay_us(50); -} +#else + DDRD |= 0xF0; + PORTD &= ~0xF0; + DDRB |= 0x3F; + PORTB &= ~0x3F; +#endif -void lcd_on(void) -{ + delay_ms(20); + lcd_command(0x38); + delay_us(50); lcd_command(0x0C); - _delay_us(50); + delay_us(50); } void lcd_clear(void) { +#ifdef LCD_ASYNC + lcd_wait(); +#endif lcd_command(0x01); - _delay_us(1500); + lcd_delay(1700); } void lcd_gotoxy(uint8_t x, uint8_t y) @@ -70,14 +181,33 @@ void lcd_gotoxy(uint8_t x, uint8_t y) uint8_t a = x+10*y; if(y&1) a += 54; + +#ifdef LCD_ASYNC + if(lcd_busy) + { + lcd_buffer_push(a|0x80); + return; + } +#endif + lcd_command(a|0x80); - _delay_us(50); + lcd_delay(50); } void lcd_write(uint8_t c) { - PORTB |= 0x01; +#ifdef LCD_ASYNC + if(lcd_busy) + { + if(c>=0x80) + lcd_buffer_push(0xFF); + lcd_buffer_push(c); + return; + } +#endif + + REGSEL_PORT |= REGSEL_BIT; lcd_set_data(c); lcd_clock(); - _delay_us(50); + lcd_delay(50); } diff --git a/firmware/lcd.h b/firmware/lcd.h index 16d64d5..2d414dc 100644 --- a/firmware/lcd.h +++ b/firmware/lcd.h @@ -1,8 +1,14 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + #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); diff --git a/firmware/s88w-r.c b/firmware/s88w-r.c index f64445d..dbd1ef1 100644 --- a/firmware/s88w-r.c +++ b/firmware/s88w-r.c @@ -27,6 +27,7 @@ D5 - S88 RESET #include #include "lcd.h" #include "serial.h" +#include "delay.h" #define DATA_OUT PORTD2 #define CLOCK PIND3 @@ -51,20 +52,22 @@ int main() uint8_t bits = 0; uint8_t n_bits = 8; uint8_t offset = 0; + uint8_t i; - DDRD = 0x06; // 00000110 - PIND = 0xC0; // 11000000 - DDRB = 0x20; // 00100000 - PINB = 0x1F; // 00011111 + DDRD = 0x06; // 00000110 + PORTD = 0xC0; // 11000000 + DDRB = 0x20; // 00100000 + PORTB = 0x1F; // 00011111 serial_init(9600); - serial_set_callback(receive); lcd_init(); - lcd_on(); - lcd_clear(); sei(); + lcd_clear(); + for(i=0; i<20; ++i) + lcd_write('0'); + while(1) { uint8_t d_pins; @@ -140,13 +143,11 @@ void receive(uint8_t c) input[j/2] = (input[j/2]&~(0xF<>4)); - lcd_write(hexdigit(input[i])); + lcd_gotoxy(19-offset-nibbles, 0); + for(i=0; i<=nibbles; ++i) + lcd_write(rx_buf[3+i]); + log_pos |= 0x80; } } rx_fill = 0xFF; @@ -164,10 +165,13 @@ void receive(uint8_t c) ++log_pos; if(log_pos>=60) log_pos = 0; - lcd_gotoxy(log_pos%20, 1+log_pos/20); + if(log_pos%20==0) + lcd_gotoxy(log_pos%20, 1+log_pos/20); lcd_write(255); } +SERIAL_SET_CALLBACK(receive) + uint8_t hexdigit(uint8_t n) { n &= 0xF; diff --git a/firmware/s88w-t.c b/firmware/s88w-t.c index e394d60..70c646c 100644 --- a/firmware/s88w-t.c +++ b/firmware/s88w-t.c @@ -17,9 +17,21 @@ D6 - input 5 D7 - input 6 B0 - input 7 B1 - input 8 +B2 - input 9 +B3 - input 10 +B4 - input 11 +B5 - input 12 Inputs are pulled high by internal pull-up resistors. Connect to GND to activate. + +The module can be configured by sending a string of form ":Shhh." over the +serial port, where hhh is a hex number. Lower two bits indicate the number of +inputs (00=4, 01=8, 10=12, 11=16) and upper ten bits indicate offset of lowest +input in multiples of 4 bits. At the moment there are no provisions for +having 16 physical inputs. + +Example: ":S016." would configure the module to have 12 inputs at offset 20. */ #include @@ -52,13 +64,11 @@ int main() DDRD = 0x02; // 00000010 PORTD = 0xFC; // 11111100 - DDRB = 0x20; // 00000000 + DDRB = 0x00; // 00000000 PORTB = 0x3F; // 00111111 serial_init(9600); - serial_set_callback(receive); - timer_init(1, 2); - timer_set_callback(send_state); + timer_start_hz(1, 1, 2); sei(); @@ -123,6 +133,8 @@ void receive(uint8_t c) } } +SERIAL_SET_CALLBACK(receive) + void send_state(void) { uint8_t i; @@ -135,6 +147,8 @@ void send_state(void) serial_write('.'); } +TIMER_SET_CALLBACK(1, send_state) + uint8_t hexdigit(uint8_t n) { n &= 0xF; diff --git a/firmware/serial.c b/firmware/serial.c index b7219ae..be3b13a 100644 --- a/firmware/serial.c +++ b/firmware/serial.c @@ -1,11 +1,15 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + #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; @@ -15,13 +19,7 @@ void serial_init(uint16_t baud) 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); + UCSR0B = BIT(RXEN0) | BIT(TXEN0) | BIT(RXCIE0); } void serial_write(uint8_t c) @@ -29,8 +27,3 @@ void serial_write(uint8_t c) while(!(UCSR0A&(1< + +#define SERIAL_SET_CALLBACK(f) \ + ISR(USART_RX_vect) \ + { \ + char c = UDR0; \ + f(c); \ + } 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 index 043dff8..13db1e8 100644 --- a/firmware/timer.c +++ b/firmware/timer.c @@ -1,55 +1,108 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + #include -#include #include "timer.h" #define BIT(n) (1<<(n)) -static TimerCallback *timer_callback; - -void timer_init(uint16_t freq_p, uint8_t freq_q) +static void timer_start(uint8_t num, uint32_t period) { - 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) + if(num==0) { - cs = BIT(CS12); - period /= 256; + if(period<0x100) + { + cs = BIT(CS00); + } + else if(period<0x800) + { + cs = BIT(CS01); + period /= 8; + } + else if(period<0x4000) + { + cs = BIT(CS01) | BIT(CS00); + period /= 64; + } + else if(period<0x10000) + { + cs = BIT(CS02); + period /= 256; + } + else + { + cs = BIT(CS02) | BIT(CS00); + period /= 1024; + if(period>0xFF) + period = 0xFF; + } + TCCR0A = BIT(WGM01); + TCCR0B = cs; + OCR0A = period; + TIMSK0 = BIT(OCIE0A); } - else + if(num==1) { - cs = BIT(CS12) | BIT(CS10); - period /= 1024; - if(period>0xFFFF) - period = 0xFFFF; + 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; + TIMSK1 = BIT(OCIE1A); } - TCCR1A = 0; - TCCR1B = BIT(WGM12) | cs; - OCR1AH = period>>8; - OCR1AL = period; } -void timer_set_callback(TimerCallback *cb) +void timer_start_hz(uint8_t num, uint32_t freq_p, uint8_t freq_q) +{ + timer_start(num, F_CPU*freq_q/freq_p); +} + +void timer_start_us(uint8_t num, uint32_t us) { - timer_callback = cb; - TIMSK1 |= BIT(OCIE1A); + timer_start(num, F_CPU/1000000*us); } -ISR(TIMER1_COMPA_vect) +void timer_stop(uint8_t num) { - timer_callback(); + if(num==0) + { + TCCR0B = 0; + TIMSK0 = 0; + } + else if(num==1) + { + TCCR1B = 0; + TIMSK1 = 0; + } } diff --git a/firmware/timer.h b/firmware/timer.h index fa2cd86..2fbda8e 100644 --- a/firmware/timer.h +++ b/firmware/timer.h @@ -1,9 +1,23 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + #ifndef TIMER_H_ #define TIMER_H_ -typedef void TimerCallback(void); +#include + +#define TIMER_SET_CALLBACK(n, f) \ + ISR(TIMER ## n ## _COMPA_vect) \ + { \ + f(); \ + } -void timer_init(uint16_t, uint8_t); -void timer_set_callback(TimerCallback *); +void timer_start_hz(uint8_t, uint32_t, uint8_t); +void timer_start_us(uint8_t, uint32_t); +void timer_stop(uint8_t); #endif -- 2.45.2