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