]> git.tdb.fi Git - model-railway-devices.git/blob - firmware/lcd.c
6ba00d54a913c01ba6f2aac24a05f708a40c2a7e
[model-railway-devices.git] / firmware / lcd.c
1 /* $Id$
2
3 This file is part of the MSP Märklin suite
4 Copyright © 2010  Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6
7 ATMega pinout (with LCD_SHIFTREG):
8 B0 - HD44780 RS
9 B1 - HD44780 clock
10 B2 - shift reg data
11 B3 - shift reg clock
12
13 ATMega pinout (without LCD_SHIFTREG):
14 D4 - HD44780 RS
15 D5 - HD44780 clock
16 D6 - HD44780 data 0
17 D7 - HD44780 data 1
18 B0 - HD44780 data 2
19 B1 - HD44780 data 3
20 B2 - HD44780 data 4
21 B3 - HD44780 data 5
22 B4 - HD44780 data 6
23 B5 - HD44780 data 7
24
25 In both cases, HD44780 R/W needs to be connected to ground.
26 */
27 #include <avr/io.h>
28 #include "delay.h"
29 #include "lcd.h"
30 #include "timer.h"
31
32 #ifdef LCD_SHIFTREG
33 #define CLOCK_PORT PORTB
34 #define CLOCK_BIT 0x02
35 #define REGSEL_PORT PORTB
36 #define REGSEL_BIT 0x01
37 #else
38 #define CLOCK_PORT PORTD
39 #define CLOCK_BIT 0x20
40 #define REGSEL_PORT PORTD
41 #define REGSEL_BIT 0x10
42 #endif
43
44 #ifndef LCD_BUFSIZE
45 #define LCD_BUFSIZE 32
46 #endif
47
48 #define NOP() __asm__("nop")
49
50 #ifdef LCD_ASYNC
51 static volatile uint8_t lcd_busy = 0;
52 static uint8_t lcd_buffer[LCD_BUFSIZE];
53 static volatile uint8_t lcd_buf_head = 0;
54 static volatile uint8_t lcd_buf_tail = 0;
55 #endif
56
57 static void lcd_clock(void)
58 {
59         delay_us(1);
60         CLOCK_PORT |= CLOCK_BIT;
61         delay_us(1);
62         CLOCK_PORT &= ~CLOCK_BIT;
63 }
64
65 static void lcd_set_data(uint8_t d)
66 {
67 #ifdef LCD_SHIFTREG
68         uint8_t i;
69         for(i=0; i<8; ++i)
70         {
71                 PORTB = (PORTB&~0x04)|((d&1)<<2);
72                 PORTB |= 0x08;
73                 NOP();
74                 PORTB &= ~0x08;
75                 d >>= 1;
76         }
77 #else
78         PORTD = (PORTD&0x3F) | (d<<6);
79         PORTB = (PORTB&0xC0) | (d>>2);
80 #endif
81 }
82
83 static void lcd_command(uint8_t c)
84 {
85         REGSEL_PORT &= ~REGSEL_BIT;
86         lcd_set_data(c);
87         lcd_clock();
88 }
89
90 static void lcd_delay(uint16_t us)
91 {
92 #ifdef LCD_ASYNC
93         lcd_busy = 1;
94         timer_start_us(0, us);
95 #else
96         delay_us(us);
97 #endif
98 }
99
100 #ifdef LCD_ASYNC
101 static void lcd_wait(void)
102 {
103         while(lcd_busy) ;
104 }
105
106 static void lcd_buffer_push(uint8_t byte)
107 {
108         while((lcd_buf_head+1)%sizeof(lcd_buffer)==lcd_buf_tail) ;
109         lcd_buffer[lcd_buf_head] = byte;
110         lcd_buf_head = (lcd_buf_head+1)%sizeof(lcd_buffer);
111 }
112
113 static uint8_t lcd_buffer_pop(void)
114 {
115         uint8_t byte = lcd_buffer[lcd_buf_tail];
116         lcd_buf_tail = (lcd_buf_tail+1)%sizeof(lcd_buffer);
117         return byte;
118 }
119
120 static void lcd_timer(void)
121 {
122         if(lcd_busy)
123         {
124                 if(lcd_buf_head!=lcd_buf_tail)
125                 {
126                         uint8_t byte = lcd_buffer_pop();
127                         if(byte==0xFF)
128                         {
129                                 REGSEL_PORT |= REGSEL_BIT;
130                                 byte = lcd_buffer_pop();
131                         }
132                         else if(byte>=0x80)
133                                 REGSEL_PORT &= ~REGSEL_BIT;
134                         else
135                                 REGSEL_PORT |= REGSEL_BIT;
136                         lcd_set_data(byte);
137                         lcd_clock();
138                         lcd_delay(50);
139                 }
140                 else
141                 {
142                         lcd_busy = 0;
143                         timer_stop(0);
144                 }
145         }
146 }
147
148 TIMER_SET_CALLBACK(0, lcd_timer)
149 #endif
150
151 void lcd_init(void)
152 {
153 #ifdef LCD_SHIFTREG
154         DDRB |= 0x0F;
155         PORTB &= ~0x0F;
156 #else
157         DDRD |= 0xF0;
158         PORTD &= ~0xF0;
159         DDRB |= 0x3F;
160         PORTB &= ~0x3F;
161 #endif
162
163         delay_ms(20);
164         lcd_command(0x38);
165         delay_us(50);
166         lcd_command(0x0C);
167         delay_us(50);
168 }
169
170 void lcd_clear(void)
171 {
172 #ifdef LCD_ASYNC
173         lcd_wait();
174 #endif
175         lcd_command(0x01);
176         lcd_delay(1700);
177 }
178
179 void lcd_gotoxy(uint8_t x, uint8_t y)
180 {
181         uint8_t a = x+10*y;
182         if(y&1)
183                 a += 54;
184
185 #ifdef LCD_ASYNC
186         if(lcd_busy)
187         {
188                 lcd_buffer_push(a|0x80);
189                 return;
190         }
191 #endif
192
193         lcd_command(a|0x80);
194         lcd_delay(50);
195 }
196
197 void lcd_write(uint8_t c)
198 {
199 #ifdef LCD_ASYNC
200         if(lcd_busy)
201         {
202                 if(c>=0x80)
203                         lcd_buffer_push(0xFF);
204                 lcd_buffer_push(c);
205                 return;
206         }
207 #endif
208
209         REGSEL_PORT |= REGSEL_BIT;
210         lcd_set_data(c);
211         lcd_clock();
212         lcd_delay(50);
213 }