X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=common%2Flcd.c;fp=common%2Flcd.c;h=f4e0387aeccdb5f148c0bbc7b89be943b9d29f17;hb=9c37d18b9c70fdb70dfec453398c4649e9e57586;hp=0000000000000000000000000000000000000000;hpb=49b6b6ad84ec47b4f9eb9ef131975cc5b72372a2;p=model-railway-devices.git diff --git a/common/lcd.c b/common/lcd.c new file mode 100644 index 0000000..f4e0387 --- /dev/null +++ b/common/lcd.c @@ -0,0 +1,208 @@ +/* +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 "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 + +#ifndef LCD_BUFSIZE +#define LCD_BUFSIZE 32 +#endif + +#define NOP() __asm__("nop") + +#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 + +static void lcd_clock(void) +{ + delay_us(1); + CLOCK_PORT |= CLOCK_BIT; + delay_us(1); + CLOCK_PORT &= ~CLOCK_BIT; +} + +static void lcd_set_data(uint8_t d) +{ +#ifdef LCD_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 +} + +static void lcd_command(uint8_t c) +{ + 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; +#else + DDRD |= 0xF0; + PORTD &= ~0xF0; + DDRB |= 0x3F; + PORTB &= ~0x3F; +#endif + + delay_ms(20); + lcd_command(0x38); + delay_us(50); + lcd_command(0x0C); + delay_us(50); +} + +void lcd_clear(void) +{ +#ifdef LCD_ASYNC + lcd_wait(); +#endif + lcd_command(0x01); + lcd_delay(1700); +} + +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); + lcd_delay(50); +} + +void lcd_write(uint8_t c) +{ +#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(); + lcd_delay(50); +}