1 /* 2 * Control LCD module hung off parallel port using the 3 * ppi 'geek port' interface. 4 * 5 * $Id$ 6 */ 7 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <ctype.h> 12 #include <fcntl.h> 13 #include <unistd.h> 14 #include <err.h> 15 #include <sysexits.h> 16 17 /* XXX should be in <machine/> */ 18 #include "ppbconf.h" 19 #include "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 void 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")) != EOF) { 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 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 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 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 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 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 359 hd44780_finish(void) 360 { 361 close(hd_fd); 362 } 363 364 static void 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 434 hd44780_putc(int c) 435 { 436 hd44780_output(HD_DATA, c); 437 usleep(40); 438 } 439 440