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