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.
374 lines
9.5 KiB
C
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, ®, 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;
|
|
}
|