Files
lcdproc/server/drivers/hd44780-piplate.c
T
mmdolze 27bb3801b2 Add new connection type 'piplate' to the hd44780 driver (by J. Brogdon).
In the documentation replace the list of the connection types for hd44780
driver with the table also used in the configuration options description
(use only a single place to list and link everyting). While there, remove the
assembly code for lcdserializer.
2013-04-07 11:50:29 +00:00

374 lines
9.5 KiB
C

/** \file server/drivers/hd44780-piplate.c
*
* \c I2C (via MCP23017) connection type of \c hd44780 driver for Hitachi
* HD44780 based LCD displays connected to the MCP23017 I2C I/O expander
* (Adafruit Pi Plate, see http://www.adafruit.com/products/1109).
*
* The LCD is operated in its 4 bit-mode to be connected to the 8 bit-port
* of a single MCP23017 that is accessed by the server via the I2C bus.
*/
/*-
* Copyright (c) 2013 Jonathan Brogdon <jlbrogdon@gmail.com>
*
* This file is released under the GNU General Public License. Refer to the
* COPYING file distributed with this package.
*
* Based mostly on the hd44780-i2c module, see there for a complete history.
*/
/*-
* The connections are:
* MCP23017 LCD
* GPA7 Green LCD backlight
* GPA6 Red LCD backlight
* GPA4 Left key
* GPA3 Up key
* GPA2 Down key
* GPA1 Right key
* GPA0 Select key
* GPB7 RS (4)
* GPB6 RW (5)
* GPB5 EN (6)
* GPB4 D7 (14)
* GPB3 D6 (13)
* GPB2 D5 (12)
* GPB1 D4 (11)
* GPB0 Blue LCD backlight
*
* Configuration:
* device=/dev/i2c-1 # the device file of the i2c bus
* port=0x20 # the i2c address of the i2c port expander
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "hd44780-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>
#ifdef HAVE_DEV_IICBUS_IIC_H
# include <strings.h>
# include <dev/iicbus/iic.h>
#else /* HAVE_LINUX_I2C_DEV_H */
# include <linux/i2c-dev.h>
#endif
#define DEFAULT_DEVICE "/dev/i2c-1"
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
/* GPIOA BITS */
#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 B_BIT (1 << 0)
/* 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 DATA_BIT_START_OFFSET 1
#define MAP_TO_DATA_PINS(x) (SWIZZLE_4BITS(x) << DATA_BIT_START_OFFSET)
#define I2C_ADDR_MASK 0x7f
/**
* Read one of the MCP23017 registers
* \param p Pointer to driver private data.
* \param reg Address of the register to read.
* \param val Pointer to the read buffer.
* \retval 0 Success.
* \retval -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
* \param p Pointer to driver private data.
* \param reg Address of the register to write.
* \param val Value to be written.
* \retval 0 Success.
* \retval -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;
}
/**
* 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;
#ifdef HAVE_DEV_IICBUS_IIC_H
struct iiccmd cmd;
bzero(&cmd, sizeof(cmd));
#endif
/* 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: piplate: Using device '%s' and address 0x%02X 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: piplate: open i2c device '%s' failed: %s",
device, strerror(errno));
return -1;
}
/* Set I2C address */
#ifdef HAVE_DEV_IICBUS_IIC_H
cmd.slave = (p->port & I2C_ADDR_MASK) << 1;
cmd.last = 0;
cmd.count = 0;
if (ioctl(p->fd, I2CRSTCARD, &cmd) < 0) {
report(RPT_ERR, "HD44780: piplate: reset bus failed: %s", strerror(errno));
return -1;
}
if (ioctl(p->fd, I2CSTART, &cmd) < 0) {
report(RPT_ERR, "HD44780: piplate: set address to 0x%02X: %s",
p->port & I2C_ADDR_MASK, strerror(errno));
return -1;
}
#else /* HAVE_LINUX_I2C_DEV_H */
if (ioctl(p->fd, I2C_SLAVE, p->port & I2C_ADDR_MASK) < 0) {
report(RPT_ERR, "HD44780: piplate: set address to 0x%02X: %s",
p->port & I2C_ADDR_MASK, strerror(errno));
return -1;
}
#endif
/* 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) & ~(G_BIT | R_BIT));
/* IODIRB - LCD control/data bus 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 | B_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) & ~(G_BIT | R_BIT));
/* GPPUB - Pullups disabled */
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;
/* Initialize the LCD */
hd44780_functions->senddata(p, 0, RS_INSTR, 0x33);
hd44780_functions->uPause(p, 1);
hd44780_functions->senddata(p, 0, RS_INSTR, 0x32);
hd44780_functions->uPause(p, 1);
common_init(p, IF_4BIT);
report(RPT_INFO, "HD44780: 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)
{
unsigned char nibble[2];
unsigned char write_val = 0;
int nibble_idx = 0;
/* Split the val byte into nibbles */
nibble[0] = (ch >> 4);
nibble[1] = (ch & 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 (flags == RS_INSTR)
write_val &= ~RS_BIT;
else
write_val |= RS_BIT;
/* Set the blue backlight bit if backlight is on */
if (p->backlightstate == BACKLIGHT_OFF)
write_val |= B_BIT;
/* Set enable bit */
i2c_write_reg(p, MCP23017_GPIOB, write_val | EN_BIT);
p->hd44780_functions->uPause(p, 1);
/* Clear enable bit */
i2c_write_reg(p, MCP23017_GPIOB, write_val & ~EN_BIT);
} /* for each nibble */
p->hd44780_functions->uPause(p, 1);
}
/**
* Turn display backlight on or off. For the Pi Plate this means a read /
* modify / write cycle is required, as the bits are shared with other signals
* on the same byte.
*
* \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;
unsigned char gpiob = 0;
/* Read the RG bits */
if (i2c_read_reg(p, MCP23017_GPIOA, &gpioa) != 0) {
return;
}
/* Read the B bit */
if (i2c_read_reg(p, MCP23017_GPIOB, &gpiob) != 0) {
return;
}
/* Set or clear the RGB bits. Backlight is ON if the bits are CLEAR */
if (state == BACKLIGHT_ON) {
gpioa &= ~(R_BIT | G_BIT);
gpiob &= ~(B_BIT);
}
else {
gpioa |= (R_BIT | G_BIT);
gpiob |= (B_BIT);
}
/* Write the RGB bits */
i2c_write_reg(p, MCP23017_GPIOA, gpioa);
i2c_write_reg(p, MCP23017_GPIOB, gpiob);
}
/**
* 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) != 0)
return 0;
/* Select key */
if ((gpioa & S_KEY_BIT) == 0)
return 1;
/* Up key */
if ((gpioa & U_KEY_BIT) == 0)
return 2;
/* Down key */
if ((gpioa & D_KEY_BIT) == 0)
return 3;
/* Left key */
if ((gpioa & L_KEY_BIT) == 0)
return 4;
/* Right key */
if ((gpioa & R_KEY_BIT) == 0)
return 5;
return 0;
}