summaryrefslogtreecommitdiffstats
path: root/share/examples/ppi
diff options
context:
space:
mode:
authormsmith <msmith@FreeBSD.org>1998-01-02 12:10:56 +0000
committermsmith <msmith@FreeBSD.org>1998-01-02 12:10:56 +0000
commit7f5886c9f1c4c3c25b84a8d50501bf4ce34e55f2 (patch)
tree8b871c35a78bc829a60b9f729aa80e187bf66748 /share/examples/ppi
parent7f112297213c52e8e5f43a23a60f87e4da8af335 (diff)
downloadFreeBSD-src-7f5886c9f1c4c3c25b84a8d50501bf4ce34e55f2.zip
FreeBSD-src-7f5886c9f1c4c3c25b84a8d50501bf4ce34e55f2.tar.gz
Sample program using the ppi(4) interface to control LCD modules.
Currently supports the (very common) Hitachi HD44780, but can be easily modified to add other chipsets.
Diffstat (limited to 'share/examples/ppi')
-rw-r--r--share/examples/ppi/Makefile8
-rw-r--r--share/examples/ppi/ppilcd.c439
2 files changed, 447 insertions, 0 deletions
diff --git a/share/examples/ppi/Makefile b/share/examples/ppi/Makefile
new file mode 100644
index 0000000..c77260c
--- /dev/null
+++ b/share/examples/ppi/Makefile
@@ -0,0 +1,8 @@
+# Makefile to build LCD control software for ppi(4) interface.
+#
+# $Id$
+
+PROG= ppilcd
+NOMAN= YES
+
+.include <bsd.prog.mk>
diff --git a/share/examples/ppi/ppilcd.c b/share/examples/ppi/ppilcd.c
new file mode 100644
index 0000000..4ad7e2a
--- /dev/null
+++ b/share/examples/ppi/ppilcd.c
@@ -0,0 +1,439 @@
+/*
+ * Control LCD module hung off parallel port using the
+ * ppi 'geek port' interface.
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <err.h>
+#include <sysexits.h>
+
+/* XXX should be in <machine/> */
+#include "ppbconf.h"
+#include "ppi.h"
+
+#define debug(lev, fmt, args...) if (debuglevel >= lev) fprintf(stderr, fmt "\n" , ## args);
+
+static void usage(void);
+static char *progname;
+
+#define DEFAULT_DEVICE "/dev/ppi0"
+
+/* Driver functions */
+static void hd44780_prepare(char *devname, char *options);
+static void hd44780_finish(void);
+static void hd44780_command(int cmd);
+static void hd44780_putc(int c);
+
+/*
+ * Commands
+ * Note that unrecognised command escapes are passed through with
+ * the command value set to the ASCII value of the escaped character.
+ */
+#define CMD_RESET 0
+#define CMD_BKSP 1
+#define CMD_CLR 2
+#define CMD_NL 3
+#define CMD_CR 4
+#define CMD_HOME 5
+
+#define MAX_DRVOPT 10 /* maximum driver-specific options */
+
+struct lcd_driver
+{
+ char *l_code;
+ char *l_name;
+ char *l_options[MAX_DRVOPT];
+ void (* l_prepare)(char *name, char *options);
+ void (* l_finish)(void);
+ void (* l_command)(int cmd);
+ void (* l_putc)(int c);
+};
+
+static struct lcd_driver lcd_drivertab[] = {
+ {
+ "hd44780",
+ "Hitachi HD44780 and compatibles",
+ {
+ "Reset options:",
+ " 1 1-line display (default 2)",
+ " B Cursor blink enable",
+ " C Cursor enable",
+ " F Large font select",
+ NULL
+ },
+ hd44780_prepare,
+ hd44780_finish,
+ hd44780_command,
+ hd44780_putc
+ },
+ {
+ NULL,
+ NULL,
+ {
+ NULL
+ },
+ NULL,
+ NULL
+ }
+};
+
+static void do_char(struct lcd_driver *driver, char ch);
+
+int debuglevel = 0;
+int vflag = 0;
+
+void
+main(int argc, char *argv[])
+{
+ extern char *optarg;
+ extern int optind;
+ struct lcd_driver *driver = &lcd_drivertab[0];
+ char *drivertype, *cp;
+ char *devname = DEFAULT_DEVICE;
+ char *drvopts = NULL;
+ int ch, i;
+
+ if ((progname = strrchr(argv[0], '/'))) {
+ progname++;
+ } else {
+ progname = argv[0];
+ }
+
+ drivertype = getenv("LCD_TYPE");
+
+ while ((ch = getopt(argc, argv, "Dd:f:o:v")) != EOF) {
+ switch(ch) {
+ case 'D':
+ debuglevel++;
+ break;
+ case 'd':
+ drivertype = optarg;
+ break;
+ case 'f':
+ devname = optarg;
+ break;
+ case 'o':
+ drvopts = optarg;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* If an LCD type was specified, look it up */
+ if (drivertype != NULL) {
+ driver = NULL;
+ for (i = 0; lcd_drivertab[i].l_code != NULL; i++) {
+ if (!strcmp(drivertype, lcd_drivertab[i].l_code)) {
+ driver = &lcd_drivertab[i];
+ break;
+ }
+ }
+ if (driver == NULL) {
+ warnx("LCD driver '%s' not known", drivertype);
+ usage();
+ }
+ }
+ debug(1, "Driver selected for %s", driver->l_name);
+ driver->l_prepare(devname, drvopts);
+ atexit(driver->l_finish);
+
+ if (argc > 0) {
+ debug(2, "reading input from %d argument%s", argc, (argc > 1) ? "s" : "");
+ for (i = 0; i < argc; i++)
+ for (cp = argv[i]; *cp; cp++)
+ do_char(driver, *cp);
+ } else {
+ debug(2, "reading input from stdin");
+ setvbuf(stdin, NULL, _IONBF, 0);
+ while ((ch = fgetc(stdin)) != EOF)
+ do_char(driver, (char)ch);
+ }
+ exit(EX_OK);
+}
+
+static void
+usage(void)
+{
+ int i, j;
+
+ fprintf(stderr, "usage: %s [-v] [-d drivername] [-f device] [-o options] [args...]\n", progname);
+ fprintf(stderr, " -D Increase debugging\n");
+ fprintf(stderr, " -f Specify device, default is '%s'\n", DEFAULT_DEVICE);
+ fprintf(stderr, " -d Specify driver, one of:\n");
+ for (i = 0; lcd_drivertab[i].l_code != NULL; i++) {
+ fprintf(stderr, " %-10s (%s)%s\n",
+ lcd_drivertab[i].l_code, lcd_drivertab[i].l_name, (i == 0) ? " *default*" : "");
+ if (lcd_drivertab[i].l_options[0] != NULL) {
+
+ for (j = 0; lcd_drivertab[i].l_options[j] != NULL; j++)
+ fprintf(stderr, " %s\n", lcd_drivertab[i].l_options[j]);
+ }
+ }
+ fprintf(stderr, " -o Specify driver option string\n");
+ fprintf(stderr, " args Message strings. Embedded escapes supported:\n");
+ fprintf(stderr, " \\b Backspace\n");
+ fprintf(stderr, " \\f Clear display, home cursor\n");
+ fprintf(stderr, " \\n Newline\n");
+ fprintf(stderr, " \\r Carriage return\n");
+ fprintf(stderr, " \\R Reset display\n");
+ fprintf(stderr, " \\v Home cursor\n");
+ fprintf(stderr, " \\\\ Literal \\\n");
+ fprintf(stderr, " If args not supplied, strings are read from standard input\n");
+ exit(EX_USAGE);
+}
+
+static void
+do_char(struct lcd_driver *driver, char ch)
+{
+ static int esc = 0;
+
+ if (esc) {
+ switch(ch) {
+ case 'b':
+ driver->l_command(CMD_BKSP);
+ break;
+ case 'f':
+ driver->l_command(CMD_CLR);
+ break;
+ case 'n':
+ driver->l_command(CMD_NL);
+ break;
+ case 'r':
+ driver->l_command(CMD_CR);
+ break;
+ case 'R':
+ driver->l_command(CMD_RESET);
+ break;
+ case 'v':
+ driver->l_command(CMD_HOME);
+ break;
+ case '\\':
+ driver->l_putc('\\');
+ break;
+ default:
+ driver->l_command(ch);
+ break;
+ }
+ esc = 0;
+ } else {
+ if (ch == '\\') {
+ esc = 1;
+ } else {
+ if (vflag || isprint(ch))
+ driver->l_putc(ch);
+ }
+ }
+}
+
+
+/******************************************************************************
+ * Driver for the Hitachi HD44780. This is probably *the* most common driver
+ * to be found on one- and two-line alphanumeric LCDs.
+ *
+ * This driver assumes the following connections :
+ *
+ * Parallel Port LCD Module
+ * --------------------------------
+ * Strobe (1) Enable (6)
+ * Data (2-9) Data (7-14)
+ * Select(13) RS (4)
+ * Auto Feed (14) R/W (5)
+ *
+ * In addition, power must be supplied to the module, normally with
+ * a circuit similar to this:
+ *
+ * VCC (+5V) O------o-------o--------O Module pin 2
+ * | | +
+ * / ---
+ * \ --- 1uF
+ * / | -
+ * \ <-----o--------O Module pin 3
+ * /
+ * \
+ * |
+ * GND O------o----------------O Module pin 1
+ *
+ * The ground line should also be connected to the parallel port, on
+ * one of the ground pins (eg. pin 25).
+ *
+ * Note that the pinning on some LCD modules has the odd and even pins
+ * arranged as though reversed; check carefully before conecting a module
+ * as it is possible to toast the HD44780 if the power is reversed.
+ */
+
+static int hd_fd;
+static u_int8_t hd_cbits;
+static int hd_lines = 2;
+static int hd_blink = 0;
+static int hd_cursor = 0;
+static int hd_font = 0;
+
+#define HD_COMMAND SELECTIN
+#define HD_DATA 0
+#define HD_READ 0
+#define HD_WRITE AUTOFEED
+
+#define HD_BF 0x80 /* internal busy flag */
+#define HD_ADDRMASK 0x7f /* DDRAM address mask */
+
+#define hd_sctrl(v) {u_int8_t _val; _val = hd_cbits | v; ioctl(hd_fd, PPISCTRL, &_val);}
+#define hd_sdata(v) {u_int8_t _val; _val = v; ioctl(hd_fd, PPISDATA, &_val);}
+#define hd_gdata(v) ioctl(hd_fd, PPIGDATA, &v)
+
+static void
+hd44780_output(int type, int data)
+{
+ debug(3, "%s -> 0x%02x", (type == HD_COMMAND) ? "cmd " : "data", data);
+ hd_sctrl(type | HD_WRITE | STROBE); /* set direction, address */
+ hd_sctrl(type | HD_WRITE); /* raise E */
+ hd_sdata((u_int8_t) data); /* drive data */
+ hd_sctrl(type | HD_WRITE | STROBE); /* lower E */
+}
+
+static int
+hd44780_input(int type)
+{
+ u_int8_t val;
+
+ hd_sctrl(type | HD_READ | STROBE); /* set direction, address */
+ hd_sctrl(type | HD_READ); /* raise E */
+ hd_gdata(val); /* read data */
+ hd_sctrl(type | HD_READ | STROBE); /* lower E */
+
+ debug(3, "0x%02x -> %s", val, (type == HD_COMMAND) ? "cmd " : "data");
+ return(val);
+}
+
+static void
+hd44780_prepare(char *devname, char *options)
+{
+ char *cp = options;
+
+ if ((hd_fd = open(devname, O_RDWR, 0)) == -1)
+ err(EX_OSFILE, "can't open '%s'", devname);
+
+ /* parse options */
+ while (cp && *cp) {
+ switch (*cp++) {
+ case '1':
+ hd_lines = 1;
+ break;
+ case 'B':
+ hd_blink = 1;
+ break;
+ case 'C':
+ hd_cursor = 1;
+ break;
+ case 'F':
+ hd_font = 1;
+ break;
+ default:
+ errx(EX_USAGE, "hd44780: unknown option code '%c'", *(cp-1));
+ }
+ }
+
+ /* Put LCD in idle state */
+ if (ioctl(hd_fd, PPIGCTRL, &hd_cbits)) /* save other control bits */
+ err(EX_IOERR, "ioctl PPIGCTRL failed (not a ppi device?)");
+ hd_cbits &= ~(STROBE | SELECTIN | AUTOFEED); /* set strobe, RS, R/W low */
+ debug(2, "static control bits 0x%x", hd_cbits);
+ hd_sctrl(STROBE);
+ hd_sdata(0);
+
+}
+
+static void
+hd44780_finish(void)
+{
+ close(hd_fd);
+}
+
+static void
+hd44780_command(int cmd)
+{
+ u_int8_t val;
+
+ switch (cmd) {
+ case CMD_RESET: /* full manual reset and reconfigure as per datasheet */
+ debug(1, "hd44780: reset to %d lines, %s font,%s%s cursor",
+ hd_lines, hd_font ? "5x10" : "5x7", hd_cursor ? "" : " no", hd_blink ? " blinking" : "");
+ val = 0x30;
+ if (hd_lines == 2)
+ val |= 0x08;
+ if (hd_font)
+ val |= 0x04;
+ hd44780_output(HD_COMMAND, val);
+ usleep(10000);
+ hd44780_output(HD_COMMAND, val);
+ usleep(1000);
+ hd44780_output(HD_COMMAND, val);
+ usleep(1000);
+ val = 0x08; /* display off */
+ hd44780_output(HD_COMMAND, val);
+ usleep(1000);
+ val |= 0x04; /* display on */
+ if (hd_cursor)
+ val |= 0x02;
+ if (hd_blink)
+ val |= 0x01;
+ hd44780_output(HD_COMMAND, val);
+ usleep(1000);
+ hd44780_output(HD_COMMAND, 0x06); /* shift cursor by increment */
+ usleep(1000);
+ /* FALLTHROUGH */
+
+ case CMD_CLR:
+ hd44780_output(HD_COMMAND, 0x01);
+ usleep(2000);
+ break;
+
+ case CMD_BKSP:
+ hd44780_output(HD_DATA, 0x10); /* shift cursor left one */
+ break;
+
+ case CMD_NL:
+ if (hd_lines == 2)
+ hd44780_output(HD_COMMAND, 0xc0); /* beginning of second line */
+ break;
+
+ case CMD_CR:
+ /* XXX will not work in 4-line mode, or where readback fails */
+ val = hd44780_input(HD_COMMAND) & 0x3f; /* mask character position, save line pos */
+ hd44780_output(HD_COMMAND, 0x80 | val);
+ break;
+
+ case CMD_HOME:
+ hd44780_output(HD_COMMAND, 0x02);
+ usleep(2000);
+ break;
+
+ default:
+ if (isprint(cmd)) {
+ warnx("unknown command %c", cmd);
+ } else {
+ warnx("unknown command 0x%x", cmd);
+ }
+ }
+ usleep(40);
+}
+
+static void
+hd44780_putc(int c)
+{
+ hd44780_output(HD_DATA, c);
+ usleep(40);
+}
+
OpenPOWER on IntegriCloud