/** \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 * * 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 #include #include #include #include #include #ifdef HAVE_DEV_IICBUS_IIC_H # include # include #else /* HAVE_LINUX_I2C_DEV_H */ # include #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; }