2 * 4-bit driver module for Hitachi HD44780 based LCD displays.
3 * The LCD is operated in it's 4 bit-mode to be connected to a single
6 * Copyright (c) 2000, 1999, 1995 Benjamin Tse <blt@Comports.com>
7 * 2001 Joris Robijn <joris@robijn.net>
8 * 1999 Andrew McMeikan <andrewm@engineer.com>
9 * 1998 Richard Rognlie <rrognlie@gamerz.net>
10 * 1997 Matthias Prinke <m.prinke@trashcan.mcnet.de>
12 * The connections are:
19 * D5 (7) RW (5) (LCD3 - 6) (optional - pull all LCD RW low)
21 * D7 (9) EN2 (LCD2 - 6) (optional)
24 * D5 (7) backlight (optional, see documentation)
26 * Extended interface (including LCD3 above)
33 * Keypad connection (optional):
34 * Some diodes and resistors are needed, see further documentation.
52 * Added support for up to 7 LCDs Jan 2000, Benjamin Tse
53 * Created modular driver Dec 1999, Benjamin Tse <blt@Comports.com>
55 * Based on Matthias Prinke's <m.prinke@trashcan.mcnet.de> lcd4.c module
56 * in lcdstat. This was in turn based on Benjamin Tse's lcdtime package
57 * which uses the LCD controller in its 8-bit mode.
59 * This file is released under the GNU General Public License. Refer to the
60 * COPYING file distributed with this package.
63 #include "hd44780-4bit.h"
64 #include "hd44780-low.h"
72 // Generally, any function that accesses the LCD control lines needs to be
73 // implemented separately for each HW design. This is typically (but not
78 void lcdstat_HD44780_senddata(PrivateData
*p
, unsigned char displayID
, unsigned char flags
, unsigned char ch
);
79 void lcdstat_HD44780_backlight(PrivateData
*p
, unsigned char state
);
80 unsigned char lcdstat_HD44780_readkeypad(PrivateData
*p
, unsigned int YData
);
88 // note that the above bits are all meant for the data port of LPT
90 static const unsigned char EnMask
[] = { EN1
, EN2
, EN3
, STRB
, LF
, INIT
, SEL
};
92 #define ALLEXT (STRB|LF|INIT|SEL)
93 // The above bits are on the control port of LPT
95 // initialisation function
97 hd_init_4bit(Driver
*drvthis
)
99 PrivateData
*p
= (PrivateData
*) drvthis
->private_data
;
100 HD44780_functions
*hd44780_functions
= p
->hd44780_functions
;
102 int enableLines
= EN1
| EN2
| EN3
;
104 // Reserve the port registers
105 port_access_multiple(p
->port
,3);
107 hd44780_functions
->senddata
= lcdstat_HD44780_senddata
;
108 hd44780_functions
->backlight
= lcdstat_HD44780_backlight
;
109 hd44780_functions
->readkeypad
= lcdstat_HD44780_readkeypad
;
111 // powerup the lcd now
112 port_out(p
->port
+ 2, 0 ^ OUTMASK
);
113 port_out(p
->port
, 0x03);
114 if (p
->delayBus
) hd44780_functions
->uPause(p
, 1);
116 /* We'll now send 0x03 a coulpe of times,
117 * which is in fact (FUNCSET | IF_8BIT) >> 4 */
118 port_out(p
->port
, enableLines
| 0x03);
119 port_out(p
->port
+ 2, ALLEXT
^ OUTMASK
);
120 if (p
->delayBus
) hd44780_functions
->uPause(p
, 1);
121 port_out(p
->port
, 0x03);
122 port_out(p
->port
+ 2, 0 ^ OUTMASK
);
123 hd44780_functions
->uPause(p
, 15000);
125 port_out(p
->port
, enableLines
| 0x03);
126 port_out(p
->port
+ 2, ALLEXT
^ OUTMASK
);
127 if (p
->delayBus
) hd44780_functions
->uPause(p
, 1);
128 port_out(p
->port
, 0x03);
129 port_out(p
->port
+ 2, 0 ^ OUTMASK
);
130 hd44780_functions
->uPause(p
, 5000);
132 port_out(p
->port
, enableLines
| 0x03);
133 port_out(p
->port
+ 2, ALLEXT
^ OUTMASK
);
134 if (p
->delayBus
) hd44780_functions
->uPause(p
, 1);
135 port_out(p
->port
, 0x03);
136 port_out(p
->port
+ 2, 0 ^ OUTMASK
);
137 hd44780_functions
->uPause(p
, 100);
139 port_out(p
->port
, enableLines
| 0x03);
140 port_out(p
->port
+ 2, ALLEXT
^ OUTMASK
);
141 if (p
->delayBus
) hd44780_functions
->uPause(p
, 1);
142 port_out(p
->port
, 0x03);
143 port_out(p
->port
+ 2, 0 ^ OUTMASK
);
144 hd44780_functions
->uPause(p
, 100);
146 // now in 8-bit mode... set 4-bit mode
147 port_out(p
->port
, 0x02);
148 if (p
->delayBus
) hd44780_functions
->uPause(p
, 1);
150 port_out(p
->port
, enableLines
| 0x02);
151 port_out(p
->port
+ 2, ALLEXT
^ OUTMASK
);
152 if (p
->delayBus
) hd44780_functions
->uPause(p
, 1);
153 port_out(p
->port
, 0x02);
154 port_out(p
->port
+ 2, 0 ^ OUTMASK
);
155 hd44780_functions
->uPause(p
, 100);
157 // Set up two-line, small character (5x8) mode
158 hd44780_functions
->senddata(p
, 0, RS_INSTR
, FUNCSET
| IF_4BIT
| TWOLINE
| SMALLCHAR
);
159 hd44780_functions
->uPause(p
, 40);
161 common_init(p
, IF_4BIT
);
163 if (p
->have_keypad
) {
164 // Remember which input lines are stuck
165 p
->stuckinputs
= lcdstat_HD44780_readkeypad(p
, 0);
171 // lcdstat_HD44780_senddata
173 lcdstat_HD44780_senddata(PrivateData
*p
, unsigned char displayID
, unsigned char flags
, unsigned char ch
)
175 unsigned char enableLines
= 0, portControl
= 0;
176 unsigned char h
= (ch
>> 4) & 0x0f; // high and low nibbles
177 unsigned char l
= ch
& 0x0f;
179 if (flags
== RS_INSTR
)
181 else //if (flags == RS_DATA)
184 portControl
|= p
->backlight_bit
;
186 if (displayID
<= 3) {
187 if (displayID
== 0) {
188 enableLines
= EnMask
[0] | EnMask
[1] | EnMask
[2];
190 enableLines
= EnMask
[displayID
- 1];
193 port_out(p
->port
, portControl
| h
);
194 if (p
->delayBus
) p
->hd44780_functions
->uPause(p
, 1);
195 port_out(p
->port
, enableLines
| portControl
| h
);
196 if (p
->delayBus
) p
->hd44780_functions
->uPause(p
, 1);
197 port_out(p
->port
, portControl
| h
);
199 port_out(p
->port
, portControl
| l
);
200 if (p
->delayBus
) p
->hd44780_functions
->uPause(p
, 1);
201 port_out(p
->port
, enableLines
| portControl
| l
);
202 if (p
->delayBus
) p
->hd44780_functions
->uPause(p
, 1);
203 port_out(p
->port
, portControl
| l
);
206 if (p
->numDisplays
> 3) {
207 if (displayID
== 0) {
208 enableLines
= ALLEXT
;
210 enableLines
= EnMask
[(displayID
- 1)];
213 port_out(p
->port
, portControl
| h
);
214 if (p
->delayBus
) p
->hd44780_functions
->uPause(p
, 1);
215 port_out(p
->port
+ 2, enableLines
^ OUTMASK
);
216 if (p
->delayBus
) p
->hd44780_functions
->uPause(p
, 1);
217 port_out(p
->port
+ 2, 0 ^ OUTMASK
);
219 port_out(p
->port
, portControl
| l
);
220 if (p
->delayBus
) p
->hd44780_functions
->uPause(p
, 1);
221 port_out(p
->port
+ 2, enableLines
^ OUTMASK
);
222 if (p
->delayBus
) p
->hd44780_functions
->uPause(p
, 1);
223 port_out(p
->port
+ 2, 0 ^ OUTMASK
);
227 void lcdstat_HD44780_backlight(PrivateData
*p
, unsigned char state
)
229 p
->backlight_bit
= ((!p
->have_backlight
||state
)?0:BL
);
231 port_out(p
->port
, p
->backlight_bit
);
234 unsigned char lcdstat_HD44780_readkeypad(PrivateData
*p
, unsigned int YData
)
236 unsigned char readval
;
238 // 10 bits output or 6 bits if >=3 displays
239 // Convert the positive logic to the negative logic on the LPT port
240 port_out(p
->port
, ~YData
& 0x003F);
241 if (p
->numDisplays
<=3) {
242 // Can't combine >3 displays with >6 keypad output lines
243 port_out(p
->port
+ 2, (((~YData
& 0x03C0) << 6)) ^ OUTMASK
);
245 if (p
->delayBus
) p
->hd44780_functions
->uPause(p
, 1);
248 readval
= ~ port_in(p
->port
+ 1) ^ INMASK
;
250 // Put port back into idle state for backlight
251 port_out(p
->port
, p
->backlight_bit
);
253 // And convert value back (MSB first).
254 return (((readval
& FAULT
) / FAULT
<<4) | /* pin 15 */
255 ((readval
& SELIN
) / SELIN
<<3) | /* pin 13 */
256 ((readval
& PAPEREND
) / PAPEREND
<<2) | /* pin 12 */
257 ((readval
& BUSY
) / BUSY
<<1) | /* pin 11 */
258 ((readval
& ACK
) / ACK
)) & ~p
->stuckinputs
; /* pin 10 */