]> git.tdb.fi Git - model-railway-devices.git/blob - firmware/lcd.c
f4e0387aeccdb5f148c0bbc7b89be943b9d29f17
[model-railway-devices.git] / firmware / 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         uint8_t i;
64         for(i=0; i<8; ++i)
65         {
66                 PORTB = (PORTB&~0x04)|((d&1)<<2);
67                 PORTB |= 0x08;
68                 NOP();
69                 PORTB &= ~0x08;
70                 d >>= 1;
71         }
72 #else
73         PORTD = (PORTD&0x3F) | (d<<6);
74         PORTB = (PORTB&0xC0) | (d>>2);
75 #endif
76 }
77
78 static void lcd_command(uint8_t c)
79 {
80         REGSEL_PORT &= ~REGSEL_BIT;
81         lcd_set_data(c);
82         lcd_clock();
83 }
84
85 static void lcd_delay(uint16_t us)
86 {
87 #ifdef LCD_ASYNC
88         lcd_busy = 1;
89         timer_start_us(0, us);
90 #else
91         delay_us(us);
92 #endif
93 }
94
95 #ifdef LCD_ASYNC
96 static void lcd_wait(void)
97 {
98         while(lcd_busy) ;
99 }
100
101 static void lcd_buffer_push(uint8_t byte)
102 {
103         while((lcd_buf_head+1)%sizeof(lcd_buffer)==lcd_buf_tail) ;
104         lcd_buffer[lcd_buf_head] = byte;
105         lcd_buf_head = (lcd_buf_head+1)%sizeof(lcd_buffer);
106 }
107
108 static uint8_t lcd_buffer_pop(void)
109 {
110         uint8_t byte = lcd_buffer[lcd_buf_tail];
111         lcd_buf_tail = (lcd_buf_tail+1)%sizeof(lcd_buffer);
112         return byte;
113 }
114
115 static void lcd_timer(void)
116 {
117         if(lcd_busy)
118         {
119                 if(lcd_buf_head!=lcd_buf_tail)
120                 {
121                         uint8_t byte = lcd_buffer_pop();
122                         if(byte==0xFF)
123                         {
124                                 REGSEL_PORT |= REGSEL_BIT;
125                                 byte = lcd_buffer_pop();
126                         }
127                         else if(byte>=0x80)
128                                 REGSEL_PORT &= ~REGSEL_BIT;
129                         else
130                                 REGSEL_PORT |= REGSEL_BIT;
131                         lcd_set_data(byte);
132                         lcd_clock();
133                         lcd_delay(50);
134                 }
135                 else
136                 {
137                         lcd_busy = 0;
138                         timer_stop(0);
139                 }
140         }
141 }
142
143 TIMER_SET_CALLBACK(0, lcd_timer)
144 #endif
145
146 void lcd_init(void)
147 {
148 #ifdef LCD_SHIFTREG
149         DDRB |= 0x0F;
150         PORTB &= ~0x0F;
151 #else
152         DDRD |= 0xF0;
153         PORTD &= ~0xF0;
154         DDRB |= 0x3F;
155         PORTB &= ~0x3F;
156 #endif
157
158         delay_ms(20);
159         lcd_command(0x38);
160         delay_us(50);
161         lcd_command(0x0C);
162         delay_us(50);
163 }
164
165 void lcd_clear(void)
166 {
167 #ifdef LCD_ASYNC
168         lcd_wait();
169 #endif
170         lcd_command(0x01);
171         lcd_delay(1700);
172 }
173
174 void lcd_gotoxy(uint8_t x, uint8_t y)
175 {
176         uint8_t a = x+10*y;
177         if(y&1)
178                 a += 54;
179
180 #ifdef LCD_ASYNC
181         if(lcd_busy)
182         {
183                 lcd_buffer_push(a|0x80);
184                 return;
185         }
186 #endif
187
188         lcd_command(a|0x80);
189         lcd_delay(50);
190 }
191
192 void lcd_write(uint8_t c)
193 {
194 #ifdef LCD_ASYNC
195         if(lcd_busy)
196         {
197                 if(c>=0x80)
198                         lcd_buffer_push(0xFF);
199                 lcd_buffer_push(c);
200                 return;
201         }
202 #endif
203
204         REGSEL_PORT |= REGSEL_BIT;
205         lcd_set_data(c);
206         lcd_clock();
207         lcd_delay(50);
208 }