1 /*
2 * Control LCD module hung off parallel port using the
3 * ppi 'geek port' interface.
4 *
5 * $FreeBSD: src/share/examples/ppi/ppilcd.c,v 1.2.2.1 2003/01/05 19:45:29 semenu Exp $
6 * $DragonFly: src/share/examples/ppi/ppilcd.c,v 1.3 2008/07/10 18:29:51 swildner Exp $
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <err.h>
16 #include <sysexits.h>
17
18 #include <dev/ppbus/ppbconf.h>
19 #include <dev/ppbus/ppi.h>
20
21 #define debug(lev, fmt, args...) if (debuglevel >= lev) fprintf(stderr, fmt "\n" , ## args);
22
23 static void usage(void);
24 static char *progname;
25
26 #define DEFAULT_DEVICE "/dev/ppi0"
27
28 /* Driver functions */
29 static void hd44780_prepare(char *devname, char *options);
30 static void hd44780_finish(void);
31 static void hd44780_command(int cmd);
32 static void hd44780_putc(int c);
33
34 /*
35 * Commands
36 * Note that unrecognised command escapes are passed through with
37 * the command value set to the ASCII value of the escaped character.
38 */
39 #define CMD_RESET 0
40 #define CMD_BKSP 1
41 #define CMD_CLR 2
42 #define CMD_NL 3
43 #define CMD_CR 4
44 #define CMD_HOME 5
45
46 #define MAX_DRVOPT 10 /* maximum driver-specific options */
47
48 struct lcd_driver
49 {
50 char *l_code;
51 char *l_name;
52 char *l_options[MAX_DRVOPT];
53 void (* l_prepare)(char *name, char *options);
54 void (* l_finish)(void);
55 void (* l_command)(int cmd);
56 void (* l_putc)(int c);
57 };
58
59 static struct lcd_driver lcd_drivertab[] = {
60 {
61 "hd44780",
62 "Hitachi HD44780 and compatibles",
63 {
64 "Reset options:",
65 " 1 1-line display (default 2)",
66 " B Cursor blink enable",
67 " C Cursor enable",
68 " F Large font select",
69 NULL
70 },
71 hd44780_prepare,
72 hd44780_finish,
73 hd44780_command,
74 hd44780_putc
75 },
76 {
77 NULL,
78 NULL,
79 {
80 NULL
81 },
82 NULL,
83 NULL
84 }
85 };
86
87 static void do_char(struct lcd_driver *driver, char ch);
88
89 int debuglevel = 0;
90 int vflag = 0;
91
92 int
main(int argc,char * argv[])93 main(int argc, char *argv[])
94 {
95 extern char *optarg;
96 extern int optind;
97 struct lcd_driver *driver = &lcd_drivertab[0];
98 char *drivertype, *cp;
99 char *devname = DEFAULT_DEVICE;
100 char *drvopts = NULL;
101 int ch, i;
102
103 if ((progname = strrchr(argv[0], '/'))) {
104 progname++;
105 } else {
106 progname = argv[0];
107 }
108
109 drivertype = getenv("LCD_TYPE");
110
111 while ((ch = getopt(argc, argv, "Dd:f:o:v")) != -1) {
112 switch(ch) {
113 case 'D':
114 debuglevel++;
115 break;
116 case 'd':
117 drivertype = optarg;
118 break;
119 case 'f':
120 devname = optarg;
121 break;
122 case 'o':
123 drvopts = optarg;
124 break;
125 case 'v':
126 vflag = 1;
127 break;
128 default:
129 usage();
130 }
131 }
132 argc -= optind;
133 argv += optind;
134
135 /* If an LCD type was specified, look it up */
136 if (drivertype != NULL) {
137 driver = NULL;
138 for (i = 0; lcd_drivertab[i].l_code != NULL; i++) {
139 if (!strcmp(drivertype, lcd_drivertab[i].l_code)) {
140 driver = &lcd_drivertab[i];
141 break;
142 }
143 }
144 if (driver == NULL) {
145 warnx("LCD driver '%s' not known", drivertype);
146 usage();
147 }
148 }
149 debug(1, "Driver selected for %s", driver->l_name);
150 driver->l_prepare(devname, drvopts);
151 atexit(driver->l_finish);
152
153 if (argc > 0) {
154 debug(2, "reading input from %d argument%s", argc, (argc > 1) ? "s" : "");
155 for (i = 0; i < argc; i++)
156 for (cp = argv[i]; *cp; cp++)
157 do_char(driver, *cp);
158 } else {
159 debug(2, "reading input from stdin");
160 setvbuf(stdin, NULL, _IONBF, 0);
161 while ((ch = fgetc(stdin)) != EOF)
162 do_char(driver, (char)ch);
163 }
164 exit(EX_OK);
165 }
166
167 static void
usage(void)168 usage(void)
169 {
170 int i, j;
171
172 fprintf(stderr, "usage: %s [-v] [-d drivername] [-f device] [-o options] [args...]\n", progname);
173 fprintf(stderr, " -D Increase debugging\n");
174 fprintf(stderr, " -f Specify device, default is '%s'\n", DEFAULT_DEVICE);
175 fprintf(stderr, " -d Specify driver, one of:\n");
176 for (i = 0; lcd_drivertab[i].l_code != NULL; i++) {
177 fprintf(stderr, " %-10s (%s)%s\n",
178 lcd_drivertab[i].l_code, lcd_drivertab[i].l_name, (i == 0) ? " *default*" : "");
179 if (lcd_drivertab[i].l_options[0] != NULL) {
180
181 for (j = 0; lcd_drivertab[i].l_options[j] != NULL; j++)
182 fprintf(stderr, " %s\n", lcd_drivertab[i].l_options[j]);
183 }
184 }
185 fprintf(stderr, " -o Specify driver option string\n");
186 fprintf(stderr, " args Message strings. Embedded escapes supported:\n");
187 fprintf(stderr, " \\b Backspace\n");
188 fprintf(stderr, " \\f Clear display, home cursor\n");
189 fprintf(stderr, " \\n Newline\n");
190 fprintf(stderr, " \\r Carriage return\n");
191 fprintf(stderr, " \\R Reset display\n");
192 fprintf(stderr, " \\v Home cursor\n");
193 fprintf(stderr, " \\\\ Literal \\\n");
194 fprintf(stderr, " If args not supplied, strings are read from standard input\n");
195 exit(EX_USAGE);
196 }
197
198 static void
do_char(struct lcd_driver * driver,char ch)199 do_char(struct lcd_driver *driver, char ch)
200 {
201 static int esc = 0;
202
203 if (esc) {
204 switch(ch) {
205 case 'b':
206 driver->l_command(CMD_BKSP);
207 break;
208 case 'f':
209 driver->l_command(CMD_CLR);
210 break;
211 case 'n':
212 driver->l_command(CMD_NL);
213 break;
214 case 'r':
215 driver->l_command(CMD_CR);
216 break;
217 case 'R':
218 driver->l_command(CMD_RESET);
219 break;
220 case 'v':
221 driver->l_command(CMD_HOME);
222 break;
223 case '\\':
224 driver->l_putc('\\');
225 break;
226 default:
227 driver->l_command(ch);
228 break;
229 }
230 esc = 0;
231 } else {
232 if (ch == '\\') {
233 esc = 1;
234 } else {
235 if (vflag || isprint(ch))
236 driver->l_putc(ch);
237 }
238 }
239 }
240
241
242 /******************************************************************************
243 * Driver for the Hitachi HD44780. This is probably *the* most common driver
244 * to be found on one- and two-line alphanumeric LCDs.
245 *
246 * This driver assumes the following connections :
247 *
248 * Parallel Port LCD Module
249 * --------------------------------
250 * Strobe (1) Enable (6)
251 * Data (2-9) Data (7-14)
252 * Select(13) RS (4)
253 * Auto Feed (14) R/W (5)
254 *
255 * In addition, power must be supplied to the module, normally with
256 * a circuit similar to this:
257 *
258 * VCC (+5V) O------o-------o--------O Module pin 2
259 * | | +
260 * / ---
261 * \ --- 1uF
262 * / | -
263 * \ <-----o--------O Module pin 3
264 * /
265 * \
266 * |
267 * GND O------o----------------O Module pin 1
268 *
269 * The ground line should also be connected to the parallel port, on
270 * one of the ground pins (eg. pin 25).
271 *
272 * Note that the pinning on some LCD modules has the odd and even pins
273 * arranged as though reversed; check carefully before conecting a module
274 * as it is possible to toast the HD44780 if the power is reversed.
275 */
276
277 static int hd_fd;
278 static u_int8_t hd_cbits;
279 static int hd_lines = 2;
280 static int hd_blink = 0;
281 static int hd_cursor = 0;
282 static int hd_font = 0;
283
284 #define HD_COMMAND SELECTIN
285 #define HD_DATA 0
286 #define HD_READ 0
287 #define HD_WRITE AUTOFEED
288
289 #define HD_BF 0x80 /* internal busy flag */
290 #define HD_ADDRMASK 0x7f /* DDRAM address mask */
291
292 #define hd_sctrl(v) {u_int8_t _val; _val = hd_cbits | v; ioctl(hd_fd, PPISCTRL, &_val);}
293 #define hd_sdata(v) {u_int8_t _val; _val = v; ioctl(hd_fd, PPISDATA, &_val);}
294 #define hd_gdata(v) ioctl(hd_fd, PPIGDATA, &v)
295
296 static void
hd44780_output(int type,int data)297 hd44780_output(int type, int data)
298 {
299 debug(3, "%s -> 0x%02x", (type == HD_COMMAND) ? "cmd " : "data", data);
300 hd_sctrl(type | HD_WRITE | STROBE); /* set direction, address */
301 hd_sctrl(type | HD_WRITE); /* raise E */
302 hd_sdata((u_int8_t) data); /* drive data */
303 hd_sctrl(type | HD_WRITE | STROBE); /* lower E */
304 }
305
306 static int
hd44780_input(int type)307 hd44780_input(int type)
308 {
309 u_int8_t val;
310
311 hd_sctrl(type | HD_READ | STROBE); /* set direction, address */
312 hd_sctrl(type | HD_READ); /* raise E */
313 hd_gdata(val); /* read data */
314 hd_sctrl(type | HD_READ | STROBE); /* lower E */
315
316 debug(3, "0x%02x -> %s", val, (type == HD_COMMAND) ? "cmd " : "data");
317 return(val);
318 }
319
320 static void
hd44780_prepare(char * devname,char * options)321 hd44780_prepare(char *devname, char *options)
322 {
323 char *cp = options;
324
325 if ((hd_fd = open(devname, O_RDWR, 0)) == -1)
326 err(EX_OSFILE, "can't open '%s'", devname);
327
328 /* parse options */
329 while (cp && *cp) {
330 switch (*cp++) {
331 case '1':
332 hd_lines = 1;
333 break;
334 case 'B':
335 hd_blink = 1;
336 break;
337 case 'C':
338 hd_cursor = 1;
339 break;
340 case 'F':
341 hd_font = 1;
342 break;
343 default:
344 errx(EX_USAGE, "hd44780: unknown option code '%c'", *(cp-1));
345 }
346 }
347
348 /* Put LCD in idle state */
349 if (ioctl(hd_fd, PPIGCTRL, &hd_cbits)) /* save other control bits */
350 err(EX_IOERR, "ioctl PPIGCTRL failed (not a ppi device?)");
351 hd_cbits &= ~(STROBE | SELECTIN | AUTOFEED); /* set strobe, RS, R/W low */
352 debug(2, "static control bits 0x%x", hd_cbits);
353 hd_sctrl(STROBE);
354 hd_sdata(0);
355
356 }
357
358 static void
hd44780_finish(void)359 hd44780_finish(void)
360 {
361 close(hd_fd);
362 }
363
364 static void
hd44780_command(int cmd)365 hd44780_command(int cmd)
366 {
367 u_int8_t val;
368
369 switch (cmd) {
370 case CMD_RESET: /* full manual reset and reconfigure as per datasheet */
371 debug(1, "hd44780: reset to %d lines, %s font,%s%s cursor",
372 hd_lines, hd_font ? "5x10" : "5x7", hd_cursor ? "" : " no", hd_blink ? " blinking" : "");
373 val = 0x30;
374 if (hd_lines == 2)
375 val |= 0x08;
376 if (hd_font)
377 val |= 0x04;
378 hd44780_output(HD_COMMAND, val);
379 usleep(10000);
380 hd44780_output(HD_COMMAND, val);
381 usleep(1000);
382 hd44780_output(HD_COMMAND, val);
383 usleep(1000);
384 val = 0x08; /* display off */
385 hd44780_output(HD_COMMAND, val);
386 usleep(1000);
387 val |= 0x04; /* display on */
388 if (hd_cursor)
389 val |= 0x02;
390 if (hd_blink)
391 val |= 0x01;
392 hd44780_output(HD_COMMAND, val);
393 usleep(1000);
394 hd44780_output(HD_COMMAND, 0x06); /* shift cursor by increment */
395 usleep(1000);
396 /* FALLTHROUGH */
397
398 case CMD_CLR:
399 hd44780_output(HD_COMMAND, 0x01);
400 usleep(2000);
401 break;
402
403 case CMD_BKSP:
404 hd44780_output(HD_DATA, 0x10); /* shift cursor left one */
405 break;
406
407 case CMD_NL:
408 if (hd_lines == 2)
409 hd44780_output(HD_COMMAND, 0xc0); /* beginning of second line */
410 break;
411
412 case CMD_CR:
413 /* XXX will not work in 4-line mode, or where readback fails */
414 val = hd44780_input(HD_COMMAND) & 0x3f; /* mask character position, save line pos */
415 hd44780_output(HD_COMMAND, 0x80 | val);
416 break;
417
418 case CMD_HOME:
419 hd44780_output(HD_COMMAND, 0x02);
420 usleep(2000);
421 break;
422
423 default:
424 if (isprint(cmd)) {
425 warnx("unknown command %c", cmd);
426 } else {
427 warnx("unknown command 0x%x", cmd);
428 }
429 }
430 usleep(40);
431 }
432
433 static void
hd44780_putc(int c)434 hd44780_putc(int c)
435 {
436 hd44780_output(HD_DATA, c);
437 usleep(40);
438 }
439
440