Base: LCDproc 0.5.2
[lcdproc-de200c.git] / server / drivers / hd44780-4bit.c
blob2be29cdd54aba12eb47a0b8537f3314550e51d60
1 /*
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
4 * 8 bit-port.
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:
13 * printer port LCD
14 * D0 (2) D4 (11)
15 * D1 (3) D5 (12)
16 * D2 (4) D6 (13)
17 * D3 (5) D7 (14)
18 * D4 (6) RS (4)
19 * D5 (7) RW (5) (LCD3 - 6) (optional - pull all LCD RW low)
20 * D6 (8) EN (6)
21 * D7 (9) EN2 (LCD2 - 6) (optional)
23 * Backlight
24 * D5 (7) backlight (optional, see documentation)
26 * Extended interface (including LCD3 above)
27 * D5 (7) EN3
28 * STR (1) EN4
29 * LF (14) EN5
30 * INIT (16) EN6
31 * SEL (17) EN7
33 * Keypad connection (optional):
34 * Some diodes and resistors are needed, see further documentation.
35 * printer port keypad
36 * D0 (2) Y0
37 * D1 (3) Y1
38 * D2 (4) Y2
39 * D3 (5) Y3
40 * D4 (6) Y4
41 * D5 (7) Y5
42 * nSTRB (1) Y6
43 * nLF (14) Y7
44 * INIT (16) Y8
45 * nSEL (17) Y9
46 * nACK (10) X0
47 * BUSY (11) X1
48 * PAPEREND (12 X2
49 * SELIN (13) X3
50 * nFAULT (15) X4
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"
65 #include "lpt-port.h"
67 #include "port.h"
68 #include <stdio.h>
69 #include <string.h>
70 #include <errno.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
74 // restricted to):
75 // HD44780_senddata
76 // HD44780_readkeypad
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);
82 #define RS 0x10
83 #define RW 0x20
84 #define EN1 0x40
85 #define EN2 0x80
86 #define EN3 0x20
87 #define BL 0x20
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
96 int
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);
168 return 0;
171 // lcdstat_HD44780_senddata
172 void
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)
180 portControl = 0;
181 else //if (flags == RS_DATA)
182 portControl = RS;
184 portControl |= p->backlight_bit;
186 if (displayID <= 3) {
187 if (displayID == 0) {
188 enableLines = EnMask[0] | EnMask[1] | EnMask[2];
189 } else {
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;
209 } else {
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);
247 // Read inputs
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 */