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