]> git.tdb.fi Git - model-railway-devices.git/blobdiff - firmware/lcd.c
Support multiple timers (currently 0 and 1)
[model-railway-devices.git] / firmware / lcd.c
index 8c722d8d29e7e9150d83c76ee9eb63f04e22fb2a..6ba00d54a913c01ba6f2aac24a05f708a40c2a7e 100644 (file)
@@ -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 <avr/io.h>
-#include <util/delay.h>
+#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);
 }