Patch adds support for the Adafruit PiPlate

Jonathan Brogdon <[email protected]> Mon, 11 Feb 2013 15:32:25 -0600
Newsgroups gmane.comp.sysutils.lcdproc
Message-ID <CANAYU8Y-RCCi+ktF9oMd6x5S21Pp-eE6xBUozgzK89Md5-f=nA@mail.gmail.com>
Never saw this on the mailing list, or a response.  Re-sending.

Jon

---------- Forwarded message ----------
From: Jonathan Brogdon <[email protected]>
Date: Sat, Feb 9, 2013 at 1:26 AM
Subject: [PATCH] Add support for the Adafruit PiPlate
To: [email protected]


For your consideration, I have attached a v0.5.6 patch which adds a driver
for the Adafruit PiPlate (http://adafruit.com/products/1109).  The PiPlate
includes a HD44780 LCD and 5 button keys which is accessed via a MCP23017
I2C IO expander.

The driver was adapted from the HD44780-i2c driver, which uses the PCF8574
I2C IO expander.

Jon Brogdon

_______________________________________________
LCDproc mailing list
[email protected]
http://lists.omnipotent.net/mailman/listinfo/lcdproc
lcdproc-piplate.patch (application/octet-stream, 14.8 KB)
diff -urN ./lcdproc/acinclude.m4 ./lcdproc-piplate/acinclude.m4
--- ./lcdproc/acinclude.m4	2013-01-27 05:44:43.000000000 -0600
+++ ./lcdproc-piplate/acinclude.m4	2013-02-08 23:14:50.000000000 -0600
@@ -227,6 +227,7 @@
 			fi
 			if test "$x_ac_have_i2c" = yes; then
 				HD44780_DRIVERS="$HD44780_DRIVERS hd44780-hd44780-i2c.o"
+				HD44780_DRIVERS="$HD44780_DRIVERS hd44780-hd44780-i2c-piplate.o"
 			fi
 dnl			The hd4470-rpi driver only works on a Raspberry Pi,
 dnl			which is an ARM platform. Require people to compile on
diff -urN ./lcdproc/server/drivers/hd44780-drivers.h ./lcdproc-piplate/server/drivers/hd44780-drivers.h
--- ./lcdproc/server/drivers/hd44780-drivers.h	2013-01-27 05:44:43.000000000 -0600
+++ ./lcdproc-piplate/server/drivers/hd44780-drivers.h	2013-02-08 23:23:16.000000000 -0600
@@ -31,6 +31,7 @@
 #endif
 #ifdef HAVE_I2C
 # include "hd44780-i2c.h"
+# include "hd44780-i2c-piplate.h"
 #endif
 #ifdef WITH_ETHLCD
 # include "hd44780-ethlcd.h"
@@ -80,6 +81,7 @@
 	/* I2C connection types */
 #ifdef HAVE_I2C
 	{ "i2c",           HD44780_CT_I2C,           IF_TYPE_I2C,     hd_init_i2c       },
+        { "i2c-piplate",   HD44780_CT_I2C_PIPLATE,   IF_TYPE_I2C,     hd_init_i2c_piplate },
 #endif
 	/* TCP socket connection types */
 #ifdef WITH_ETHLCD
diff -urN ./lcdproc/server/drivers/hd44780-i2c-piplate.c ./lcdproc-piplate/server/drivers/hd44780-i2c-piplate.c
--- ./lcdproc/server/drivers/hd44780-i2c-piplate.c	1969-12-31 18:00:00.000000000 -0600
+++ ./lcdproc-piplate/server/drivers/hd44780-i2c-piplate.c	2013-02-09 00:22:12.000000000 -0600
@@ -0,0 +1,412 @@
+/** \file server/drivers/hd44780-i2c-piplate.c
+ * \c i2c (via MCP23017) connection type of \c hd44780 driver for
+ * Hitachi HD44780 based LCD displays connected to the
+ * MCP23017 I2C IO Explander (Adafruit PiPlate LCD).
+ *
+ * This driver implements the 
+ * The LCD is operated in its 4 bit-mode to be connected to the 8 bit-port
+ * of a single MCP23017 i2c I/O expander that is accessed by the server via
+ * the i2c bus.
+ */
+
+/* Copyright (c)  2013 Jonathan Brogdon <[email protected]>
+ *                2005 Matthias Goebl <[email protected]>
+ *                2000, 1999, 1995 Benjamin Tse <[email protected]>
+ *                2001 Joris Robijn <[email protected]>
+ *                1999 Andrew McMeikan <[email protected]>
+ *                1998 Richard Rognlie <[email protected]>
+ *                1997 Matthias Prinke <[email protected]>
+ *
+ * The connections are:
+ * MCP23017	  LCD
+ * GPA8           Blue LCD backlight
+ * GPA7           Green LCD backlight
+ * GPA6           Red LCD backlight
+ * GPA4           Left key
+ * GPA3           Up key
+ * GPA2           Down key
+ * GPA1           Right key
+ * GPA0           Select key
+ * GPB1 	  D4 (11)
+ * GPB2  	  D5 (12)
+ * GPB3 	  D6 (13)
+ * GPB4 	  D7 (14)
+ * GPB5 	  EN (6)
+ * GPB6 	  RW (5)
+ * GPB7 	  RS (4)
+ *
+ * Configuration:
+ * device=/dev/i2c-1   # the device file of the i2c bus
+ * port=0x20   # the i2c address of the i2c port expander
+ *
+ * Based mostly on the hd44780-i2c module, see there for a complete history.
+ *
+ * This file is released under the GNU General Public License. Refer to the
+ * COPYING file distributed with this package.
+ */
+#include "hd44780-i2c-piplate.h"
+#include "hd44780-low.h"
+
+#include "report.h"
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/i2c-dev.h>
+
+#define DEFAULT_DEVICE		"/dev/i2c-0"
+
+void i2c_piplate_HD44780_senddata(PrivateData *p,
+				  unsigned char displayID,
+				  unsigned char flags,
+				  unsigned char ch);
+void i2c_piplate_HD44780_backlight(PrivateData *p, unsigned char state);
+unsigned char i2c_piplate_HD44780_scankeypad(PrivateData *p);
+void i2c_piplate_HD44780_close(PrivateData *p);
+
+/* MCP23017 registers */
+#define MCP23017_IODIRA 0x00
+#define MCP23017_IODIRB 0x01
+#define MCP23017_GPPUA  0x0C
+#define MCP23017_GPPUB  0x0D
+#define MCP23017_GPIOA  0x12
+#define MCP23017_GPIOB  0x13
+#define MCP23017_OLATA  0x14
+#define MCP23017_OLATB  0x15
+
+/* GPIOA BITS */
+#define B_BIT     (1 << 8)
+#define G_BIT     (1 << 7)
+#define R_BIT     (1 << 6)
+#define L_KEY_BIT (1 << 4)
+#define U_KEY_BIT (1 << 3)
+#define D_KEY_BIT (1 << 2)
+#define R_KEY_BIT (1 << 1)
+#define S_KEY_BIT (1 << 0)
+
+/* GPIOB BITS */
+#define RS_BIT (1 << 7)
+#define RW_BIT (1 << 6)
+#define EN_BIT (1 << 5)
+#define D0_BIT (1 << 4)
+#define D1_BIT (1 << 3)
+#define D2_BIT (1 << 2)
+#define D3_BIT (1 << 1)
+
+#define DATA_BIT_START_OFFSET 1
+
+/* Reverses the bits in a 4 bit word */
+#define SWIZZLE_4BITS(x) ((((x) & 0x01) << 3) | (((x) & 0x02) << 1) | (((x) & 0x04) >> 1) | (((x) & 0x08) >> 3))
+
+/* Swizzle and offset bits correctly for the PiPlate wiring */
+#define MAP_TO_DATA_PINS(x) (SWIZZLE_4BITS(x) << DATA_BIT_START_OFFSET)
+
+#define I2C_ADDR_MASK 0x7f
+#define I2C_PCAX_MASK 0x80
+
+/**
+ * Read one of the MCP23017 registers
+ * p        Pointer to driver private data.
+ * reg      Address of the register to read.
+ * val      Pointer to the read buffer.
+ * 0        Success.
+ * -1       Error.
+ */
+static int
+i2c_read_reg(PrivateData *p, unsigned char reg, unsigned char *val)
+{
+  /* Set the address to be read */
+  if (write(p->fd,&reg,1) != 1) {
+    return -1;
+  }
+
+  /* Read the value */
+  if (read(p->fd,val,1) != 1) {
+    return -1;
+  }
+
+  return 0;
+}
+
+/**
+ * Write one of the MCP23017 registers
+ * p        Pointer to driver private data.
+ * reg      Address of the register to write.
+ * val      Value to be written.
+ * 0        Success.
+ * -1       Error.
+ */
+static int
+i2c_write_reg(PrivateData *p, unsigned char reg, unsigned char val)
+{
+  unsigned char buf[2];
+
+  buf[0] = reg;
+  buf[1] = val;
+  if (write(p->fd,buf,sizeof(buf)) != sizeof(buf)) {
+    return -1;
+  }
+  return 0;
+}
+
+/**
+ * Low level write to the LCD 4-bit interface.
+ * p        Driver private data.
+ * val      Value to be written.
+ * rs       Register/Data select.
+ * 0        Success.
+ * -1       Error.
+ */
+static int
+i2c_write_4bits(PrivateData *p, unsigned char val, int rs)
+{
+  unsigned char nibble[2];
+  unsigned char write_val;
+  int nibble_idx = 0;
+
+  /* Split the val byte into nibbles */
+  nibble[0] = (val >> 4);
+  nibble[1] = (val & 0x0f);
+
+  for (nibble_idx=0;nibble_idx < 2;nibble_idx++) {
+    /* Adjust for 4-bit data pins */
+    write_val = MAP_TO_DATA_PINS(nibble[nibble_idx]);
+    
+    /* Set the RS bit */
+    if (rs) {
+      write_val |= RS_BIT;
+    }
+    else {
+      write_val &= ~RS_BIT;
+    }
+
+    /* Set enable bit */
+    i2c_write_reg(p,MCP23017_GPIOB,write_val | EN_BIT);
+
+    usleep(1);
+
+    /* Clear enable bit */
+    i2c_write_reg(p,MCP23017_GPIOB,write_val & ~EN_BIT);
+
+  } /* for each nibble */
+
+  return 0;
+}
+
+/**
+ * Initialize the driver.
+ * \param drvthis  Pointer to driver structure.
+ * \retval 0       Success.
+ * \retval -1      Error.
+ */
+int
+hd_init_i2c_piplate(Driver *drvthis)
+{
+  PrivateData *p = (PrivateData*) drvthis->private_data;
+  HD44780_functions *hd44780_functions = p->hd44780_functions;
+  
+  char device[256] = DEFAULT_DEVICE;
+  
+  /* READ CONFIG FILE */
+  
+  /* Get serial device to use */
+  strncpy(device,
+	  drvthis->config_get_string(drvthis->name,
+				     "Device",
+				     0,
+				     DEFAULT_DEVICE),
+	  sizeof(device));
+  device[sizeof(device)-1] = '\0';
+  report(RPT_INFO,
+	 "HD44780: I2C piplate: Using device '%s' and address %u for a MCP23017",
+	 device,
+	 p->port & I2C_ADDR_MASK);
+  
+  /* Open the I2C device */
+  p->fd = open(device, O_RDWR);
+  if (p->fd < 0) {
+    report(RPT_ERR,
+	   "HD44780: I2C piplate: open i2c device '%s' failed: %s",
+	   device,
+	   strerror(errno));
+    return(-1);
+  }
+  
+  /* Set I2C address */
+  if (ioctl(p->fd,I2C_SLAVE, p->port & I2C_ADDR_MASK) < 0) {
+    report(RPT_ERR,
+	   "HD44780: I2C piplate: set address to '%i': %s",
+	   p->port & I2C_ADDR_MASK,
+	   strerror(errno));
+    return(-1);
+  }
+  
+  /* IODIRA - keys as input, all other as output */
+  i2c_write_reg(p, MCP23017_IODIRA,
+	(unsigned char) (L_KEY_BIT | U_KEY_BIT | D_KEY_BIT | R_KEY_BIT | S_KEY_BIT)
+		& ~(B_BIT | G_BIT | R_BIT));
+  
+  /* IODIRB - LCD control/data bua signals as outputs */
+  i2c_write_reg(p, MCP23017_IODIRB,
+	(unsigned char) ~(RS_BIT | RW_BIT | EN_BIT | D0_BIT | D1_BIT | D2_BIT | D3_BIT));
+  
+  /* GPPUA - Pullups for key switch inputs */
+  i2c_write_reg(p, MCP23017_GPPUA,
+	(unsigned char) (L_KEY_BIT | U_KEY_BIT | D_KEY_BIT | R_KEY_BIT | S_KEY_BIT)
+		& ~(B_BIT | G_BIT | R_BIT));
+  
+  /* GPPUB - Pullups disabed */
+  i2c_write_reg(p, MCP23017_GPPUB, (unsigned char) 0x00);
+  
+  /* Set driver functions */
+  hd44780_functions->senddata   = i2c_piplate_HD44780_senddata;
+  hd44780_functions->backlight  = i2c_piplate_HD44780_backlight;
+  hd44780_functions->scankeypad = i2c_piplate_HD44780_scankeypad;
+  hd44780_functions->close      = i2c_piplate_HD44780_close;
+
+  /* Init the LCD */
+  i2c_write_4bits(p,0x33,0);
+  if (p->delayBus)
+    hd44780_functions->uPause(p, 1);
+
+  i2c_write_4bits(p,0x32,0);
+  if (p->delayBus)
+    hd44780_functions->uPause(p, 1);
+
+  i2c_write_4bits(p,0x28,0);
+  if (p->delayBus)
+    hd44780_functions->uPause(p, 1);
+
+  i2c_write_4bits(p,0x0C,0);
+  if (p->delayBus)
+    hd44780_functions->uPause(p, 1);
+
+  i2c_write_4bits(p,0x06,0);
+  if (p->delayBus)
+    hd44780_functions->uPause(p, 1);
+
+  /* Set up two-line, small character (5x8) mode */
+  hd44780_functions->senddata(p,
+			      0,
+			      RS_INSTR, FUNCSET | IF_4BIT | TWOLINE | SMALLCHAR);
+  hd44780_functions->uPause(p, 40);
+
+  common_init(p, IF_4BIT);
+
+  report(RPT_INFO,"HD44780: I2C piplate: initialized!");
+  
+  return 0;
+}
+
+/**
+ * Close the device.
+ * \param p          Pointer to driver's private data structure.
+ */
+void
+i2c_piplate_HD44780_close(PrivateData *p) {
+  if (p->fd >= 0) {
+    close(p->fd);
+  }
+}
+
+
+/**
+ * Send data or commands to the display.
+ * \param p          Pointer to driver's private data structure.
+ * \param displayID  ID of the display (or 0 for all) to send data to.
+ * \param flags      Defines whether to end a command or data.
+ * \param ch         The value to send.
+ */
+void
+i2c_piplate_HD44780_senddata(PrivateData *p,
+			     unsigned char displayID,
+			     unsigned char flags,
+			     unsigned char ch)
+{
+  if (flags == RS_INSTR)
+    i2c_write_4bits(p,ch,0);
+  else
+    i2c_write_4bits(p,ch,1);
+  
+  if ((p->backlight_bit) && (p->hd44780_functions->backlight)) {
+    p->hd44780_functions->backlight(p,1);
+  }
+  else {
+    p->hd44780_functions->backlight(p,1);
+  }
+}
+
+/**
+ * Turn display backlight on or off.
+ * \param p      Pointer to driver's private data structure.
+ * \param state  New backlight status.
+ */
+void
+i2c_piplate_HD44780_backlight(PrivateData *p, unsigned char state)
+{
+  unsigned char gpioa=0;
+
+  /* Set the backlit bit */
+  p->backlight_bit = (p->have_backlight ? 0xff : 0) & (state ? 1 : 0);
+
+  /* Read the RGB bits */
+  if (i2c_read_reg(p, MCP23017_GPIOA, &gpioa)) {
+    return;
+  }
+
+  /* Set or clear the RGB bits */
+  if (p->backlight_bit) {
+    gpioa &= ~(R_BIT | G_BIT | B_BIT);
+  }
+  else {
+    gpioa |= (R_BIT | G_BIT | B_BIT);
+  }
+
+  /* Write the RGB bits */
+  i2c_write_reg(p, MCP23017_GPIOA,gpioa);
+}
+
+/**
+ * Scan the LCD keys
+ * \param p      Pointer to driver's private data structure.
+ * \retval key   Key number
+ */
+unsigned char
+i2c_piplate_HD44780_scankeypad(PrivateData *p)
+{
+  unsigned char gpioa=0;
+
+  /* Read the key bits */
+  if (i2c_read_reg(p,MCP23017_GPIOA,&gpioa)) {
+    return 0;
+  }
+
+  /* Select key */
+  if (0==(gpioa & S_KEY_BIT)) {
+    return 1;
+  }
+
+  /* Up key */
+  if (0==(gpioa & U_KEY_BIT)) {
+    return 2;
+  }
+
+  /* Down key */
+  if (0==(gpioa & D_KEY_BIT)) {
+    return 3;
+  }
+
+  /* Left key */
+  if (0==(gpioa & L_KEY_BIT)) {
+    return 4;
+  }
+
+  /* Right key */
+  if (0==(gpioa & R_KEY_BIT)) {
+    return 5;
+  }
+
+  return 0;
+}
diff -urN ./lcdproc/server/drivers/hd44780-i2c-piplate.h ./lcdproc-piplate/server/drivers/hd44780-i2c-piplate.h
--- ./lcdproc/server/drivers/hd44780-i2c-piplate.h	1969-12-31 18:00:00.000000000 -0600
+++ ./lcdproc-piplate/server/drivers/hd44780-i2c-piplate.h	2013-02-08 23:34:35.000000000 -0600
@@ -0,0 +1,9 @@
+#ifndef HD_I2C_PP_H
+#define HD_I2C_PP_H
+
+#include "lcd.h"					  /* for Driver */
+
+/* initialise this particular driver */
+int hd_init_i2c_piplate(Driver *drvthis);
+
+#endif
diff -urN ./lcdproc/server/drivers/hd44780-low.h ./lcdproc-piplate/server/drivers/hd44780-low.h
--- ./lcdproc/server/drivers/hd44780-low.h	2013-01-27 05:44:43.000000000 -0600
+++ ./lcdproc-piplate/server/drivers/hd44780-low.h	2013-02-08 23:24:29.000000000 -0600
@@ -54,6 +54,7 @@
 #define HD44780_CT_USBTINY		20
 #define HD44780_CT_USB4ALL		21
 #define HD44780_CT_RASPBERRYPI		22
+#define HD44780_CT_I2C_PIPLATE          23
 /**@}*/
 
 /** \name Symbolic names for interface types
diff -urN ./lcdproc/server/drivers/Makefile.am ./lcdproc-piplate/server/drivers/Makefile.am
--- ./lcdproc/server/drivers/Makefile.am	2013-01-27 05:44:43.000000000 -0600
+++ ./lcdproc-piplate/server/drivers/Makefile.am	2013-02-08 23:18:06.000000000 -0600
@@ -95,7 +95,7 @@
 glcdlib_SOURCES =    lcd.h lcd_lib.h glcdlib.h glcdlib.c report.h
 glk_SOURCES =        lcd.h glk.c glk.h glkproto.c glkproto.h report.h
 hd44780_SOURCES =    lcd.h lcd_lib.h hd44780.h hd44780.c hd44780-drivers.h hd44780-low.h hd44780-charmap.h report.h adv_bignum.h
-EXTRA_hd44780_SOURCES = hd44780-4bit.c hd44780-4bit.h hd44780-ext8bit.c hd44780-ext8bit.h lcd_sem.c lcd_sem.h hd44780-serialLpt.c hd44780-serialLpt.h hd44780-serial.c hd44780-serial.h hd44780-winamp.c hd44780-winamp.h hd44780-bwct-usb.c hd44780-bwct-usb.h hd44780-lcd2usb.c hd44780-lcd2usb.h hd44780-usbtiny.c hd44780-usbtiny.h hd44780-lis2.c hd44780-lis2.h hd44780-i2c.c hd44780-i2c.h hd44780-ftdi.c hd44780-ftdi.h hd44780-ethlcd.c hd44780-ethlcd.h hd44780-uss720.c hd44780-uss720.h hd44780-usblcd.c hd44780-usblcd.h hd44780-usb4all.c hd44780-usb4all.h port.h hd44780-rpi.c hd44780-rpi.h lpt-port.h timing.h
+EXTRA_hd44780_SOURCES = hd44780-4bit.c hd44780-4bit.h hd44780-ext8bit.c hd44780-ext8bit.h lcd_sem.c lcd_sem.h hd44780-serialLpt.c hd44780-serialLpt.h hd44780-serial.c hd44780-serial.h hd44780-winamp.c hd44780-winamp.h hd44780-bwct-usb.c hd44780-bwct-usb.h hd44780-lcd2usb.c hd44780-lcd2usb.h hd44780-usbtiny.c hd44780-usbtiny.h hd44780-lis2.c hd44780-lis2.h hd44780-i2c.c hd44780-i2c.h hd44780-ftdi.c hd44780-ftdi.h hd44780-ethlcd.c hd44780-ethlcd.h hd44780-uss720.c hd44780-uss720.h hd44780-usblcd.c hd44780-usblcd.h hd44780-usb4all.c hd44780-usb4all.h hd44780-i2c-piplate.c hd44780-i2c-piplate.h port.h hd44780-rpi.c hd44780-rpi.h lpt-port.h timing.h
 i2500vfd_SOURCES =   lcd.h i2500vfd.c i2500vfd.h glcd_font5x8.h report.h
 icp_a106_SOURCES =   lcd.h lcd_lib.h icp_a106.c icp_a106.h report.h
 imon_SOURCES =       lcd.h lcd_lib.h hd44780-charmap.h imon.h imon.c report.h adv_bignum.h