Oops... forgot to add PCF8574 LCD Backpack files before doing the commit.

This commit is contained in:
Gregory Nutt 2016-05-25 08:47:21 -06:00
parent ac86a8b1fc
commit 51504a032e
3 changed files with 1824 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,245 @@
pcf8574 lcd backpack - readme.txt
20160524a, ziggurat29
Abstract
========
This describes the use of the pcf8574_lcd_backpack.h, .c driver module for NuttX.
Contents
========
o Summary for Those Who Don't Like to Read
o Introduction
o Usage
- Specifying the I2C Address
- Specifying the LCD Display Format
- Specifying Unknown/New Backpacks
o Special Features
- Codec
- Ioctl
o Troubleshooting
Summary for Those Who Don't Like to Read
========================================
To use, in your board_app_initialize(),
1) instantiate an I2C bus:
FAR struct i2c_master_s* i2c = stm32l4_i2cbus_initialize(1);
2) set the configuration for the particular make of board, and LCD format:
struct pcf8574_lcd_backpack_config_s cfg = LCD_I2C_BACKPACK_CFG_MJKDZ;
cfg.rows = 2;
cfg.cols = 16;
3) instantiate the device on the I2C bus previously created:
ret = pcf8574_lcd_backpack_register("/dev/slcd0", i2c, &cfg);
Introduction
============
The character LCD modules based on the HD44780 (and compatible ST7706U, KS0066U,
SED1278, etc.) drivers have been around for many decades and are quite popular.
One challenge is that they require a large number of GPIO (11 in 8-bit mode, 7
in 4-bit mode, and an additional line if you control the backlight).
To address this, several folks have created daughter boards for the LCD module
which present a two-wire I2C interface. Generally, folks call these interface
boards an 'lcd backpack'. A large class of them (and in particular, the very
inexpensive ones found on ebay, q.v. google "ebay i2c lcd backpack"; they're
usually about $USD 1), use the same design: a PCF8574 I2C IO expander.
Variations occur in mapping GPIO line to LCD pins, but otherwise the
expectation is that you control the LCD at a low-level tweaking the lines
("byte-banging"?)
My original motivation for producing this was to simply serve as a test device
for some I2C driver work I was doing, but it occurred to me that it may be
useful to others, given the popularity of the 'lcd backpack', so I cleaned up
the code and made it general to support all the variations on the market, and
also to adopt the NuttX notion of a 'segment lcd codec', which is used to
transport escape sequences (for doing things like clearing the display, turning
on/off the cursor, etc), and also the standard ioctls.
I believe it should support all "lcd backpack"s on the market (because you can
specify the particular wiring), and all HD44780-based LCD modules in 1-line,
2-line, and 4-line configurations (except 4x40 -- this is not supported by
the hardware).
This module should be cpu-architecture-neutral, and work with any standard I2C
bus object. At the time of this writing it has been tested only with the
STM32L4 chip and with the 'MJKDZ' backpack board with a 16x2 lcd module.
Usage
=====
The driver is contained in the files pcf8574_lcd_backpack.h and
pcf8574_lcd_backpack.c; you can include these in your build in whatever manner
you choose (e.g. copy them into your board's src directory, and reference them
in the Makefile).
As with other I2C devices, you first instantiate the I2C bus, and then
instantiate the driver on that bus. When instantiating the driver, you also
provide a configuration 'descriptor' that specified board wiring and LCD
format parameters. You can explicitly specify any wiring configuration, and
some known popular boards are already #defined for your convenience.
E.g.:
#include <nuttx/i2c/i2c_master.h>
#include "pcf8574_lcd_backpack.h"
#define MJKDZ_I2C_PORTNO 1
#define MJKDZ_DEVICE_NAME "/dev/lcd0"
FAR struct i2c_master_s* g_i2cMJKDZ = NULL;
....
g_i2cMJKDZ = stm32l4_i2cbus_initialize(MJKDZ_I2C_PORTNO);
....
struct pcf8574_lcd_backpack_config_s cfg = LCD_I2C_BACKPACK_CFG_MJKDZ;
cfg.rows = 2;
cfg.cols = 16;
ret = pcf8574_lcd_backpack_register(MJKDZ_DEVICE_NAME, g_i2cMJKDZ, &cfg);
If all the above executes successfully, you should wind up with a character
device node "/dev/lcd0". Applications can open that node and write() to it,
and the shell can emit data to it (e.g. 'echo Hi, there! > /dev/lcd0').
That is the basic configuration. Some additional configuration points are
worth noting.
Specifying the I2C Address
--------------------------
The 'struct pcf8574_lcd_backpack_config_s' shown above is initialized using
the convenience macro LCD_I2C_BACKPACK_CFG_MJKDZ. Those convenience macros
use the default I2C address for the board, however many of the boards allow
altering the address (by jumpers, or removing pullups). You need to specify
the correct address for your board's physical configuration. You can do that
via
cfg.addr = 0x23;
Specifying the LCD Display Format
---------------------------------
The LCD modules cannot 'self-describe' their physical format, so it must be
explicitly provided to the driver. The correct format is important for
computing screen coordinate addresses and for scrolling and line wrap.
In the example above, the screen format is specifying by setting the
fields in the configuration descriptor:
cfg.rows = 2;
cfg.cols = 16;
The lcd backpack can accomodate all known 1-line and 2-line displays, and
4-line displays up to 4 x 32. Explicitly, the 4 x 40 /cannot/ be supported
because it has an important hardware difference (it is actually two 4x20
controllers, and the LCD backpack does not have the wiring for the
second controller's 'E' line). This is a hardware limitation of the
lcd backpack, rather than the driver.
Specifying Unknown/New Backpacks
--------------------------------
The descriptor initializer macros in the form LCD_I2C_BACKPACK_CFG_xxx
located near the top of pcf8574_lcd_backpack.h are provided for convenience.
However, their use is not required, and it can be useful to initialize the
descriptor with explicit values, say, for custom or unknown boards.
The format of this descriptor is conscientiously chosen to be semantically
similar to an equivalent initialization mechanism popular in the Arduino
community used in their LCD support libraries. It specifies:
* I2C address
* pin mapping for data lines
* pin mapping for control lines
* pin mapping for backlight control line
* polarity sense of backlight control line
and we add to that
* (row, column) size of display
(the Arduino libraries specify display size at a different point in code)
You should be able to readily port a functional Arduino project by cutting-
and-pasting the sequence of numbers that are the pin defs for the lcd
backpack you are using.
Special Features
================
Codec
-----
The driver supports the NuttX 'segment lcd codec', which facilitates the
encoding of control functions into the write() stream. These can be used
to clear the display, move the cursor, etc. For details, q.v.
nuttx/lcd/slcd_codec.h
Ioctl
-----
The driver supports the NuttX ioctl definitions for segment lcd. Q.v.
nuttx/lcd/slcd_ioctl.h
Additionally, the ioctl SLCDIOC_CREATECHAR is provided to allow the
creation of custom characters.
The HD44780 devices generally support the creation of 8 custom
characters, which map to code points 0-7. The characters are 5x8
pixels (with the expectation that the last row is left blank, to
accommodate the underscore cursor, though this is not strictly a
requirement).
The SLCDIOC_CREATECHAR ioctl takes a parameter, which is a struct
consisting of the character index being programmed (0-7) and the
8-byte bitmap of the character image. The bitmap is constructed
with each byte representing a row, from top row to bottom row.
Each row is imaged left to right, MSB to LSB, right-justified (i.e.,
bit 4 is leftmost, bit 0 is rightmost, and bits 7-5 are unused).
You may reference these characters simply by including them in
the data you write() to the device, e.g.
write(fd, "\x01,\x02Hi, there!\n", 13);
Example of programming a character image:
static const struct slcd_createchar_s custom_char =
{ 4, { 0x04, 0x0e, 0x15, 0x04, 0x04, 0x04, 0x04, 0x00 } }; /* up arrow */
ret = ioctl(fd, SLCDIOC_CREATECHAR, (unsigned long)custom_char);
Now character '\x04' will display as an 'up arrow'.
Note, you might consider avoiding the use of code point 0x00 unless
absolutely needed, because the embedded nul character can cause
problems. The driver, and write() apis are binary, and unaffected,
but things like printf() and puts() assume C-style strings, and are
affected.
Troubleshooting
===============
* Check your I2C address. turn on debugging output so you can see
bus timeouts that suggest a non-responsive slave.
* Check your board wiring and configuration specification. Buzz
out the lines if you have to.
* Con't forget to check the 'contrast' potentiometer. The voltage
at the central wiper should be approximately 0.29 V. The useful
range of voltages at this pin is very narrow, and outside that
range there will be nothing visible on the display, so most of the
turn range of the pot is non-useful. Much of human life has been
wasted in the rediscovery of this farcically idiotic

View File

@ -0,0 +1,176 @@
/****************************************************************************
* include/nuttx/lcd/pcf8574_lcd_backpack.h
*
* Copyright (C) 2016 Gregory Nutt. All rights reserved.
* Author: dev@ziggurat29.com
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#ifndef __INCLUDE_NUTTX_LCD_PCF8574_LCD_BACKPACK_H
#define __INCLUDE_NUTTX_LCD_PCF8574_LCD_BACKPACK_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <stdint.h>
#include <stdbool.h>
#include <nuttx/i2c/i2c_master.h>
#include <sys/ioctl.h>
#include <nuttx/lcd/slcd_ioctl.h>
#include <nuttx/lcd/slcd_codec.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configurations of some well-known boards. You may still have to modify the
* address if was changed from the default. You will also need to specify the
* geometry of your attached LCD display. You can support:
* 1x8, 1x12, 1x16, 2x8, 2x12, 2x16, 2x20, 2x24, 2x40, 4x16, 4x20
* Pretty much anything on the market except 4x40, which really consists of two
* separate 2x40 controllers, and the I2C backpack doesn't support those due
* to the second 'E' line being needed.
* Consider these 'informative'.
* XXX Note, actual testing has been done on LCD_I2C_BACKPACK_CFG_MJKDZ only,
* the others come from online research.
*/
/* board marked 'mjkdz' */
#define LCD_I2C_BACKPACK_CFG_MJKDZ {0x20,4,5,6,0,1,2,3,7,false,0,0}
/* YwRobot/DFRobot/SainSmart */
#define LCD_I2C_BACKPACK_CFG_SAINSMART {0x27,2,1,0,4,5,6,7,3,true,0,0}
/* Robot Arduino LCM1602/2004 */
#define LCD_I2C_BACKPACK_CFG_ROBOT {0x27,2,1,0,4,5,6,7,3,false,0,0}
/* I2CLCDextraIO board modded for backlight (pnp transistor) */
#define LCD_I2C_BACKPACK_CFG_I2CIO_PNP {0x38,6,5,4,0,1,2,3,7,false,0,0}
/* I2CLCDextraIO board modded for backlight (npn transistor) */
#define LCD_I2C_BACKPACK_CFG_I2CIO_NPN {0x38,6,5,4,0,1,2,3,7,true,0,0}
/* SLCDIOC_CREATECHAR: Create a custom character pattern
*
* argument: pointer to slcd_createchar_s structure (defined below)
*/
#define SLCDIOC_CREATECHAR _SLCDIOC(0x80)
/****************************************************************************
* Public Types
****************************************************************************/
/* Used to specify the pin wiring for this particular module */
struct pcf8574_lcd_backpack_config_s
{
uint8_t addr; /* I2C address; 'unshifted' (i.e. disregarding the LSB R/W bit)
* these can vary widely depending on board pullups, whether it
* uses a PCF8574-T or -AT, etc. Many default to either 0x20
* or 0x27, and some default to 0x38 or 0x3f. Check with seller.
*/
uint8_t en; /* gpio bit for LCD EN */
uint8_t rw; /* gpio bit for LCD RW */
uint8_t rs; /* gpio bit for LCD RS */
uint8_t d4; /* gpio bit for LCD D4 */
uint8_t d5; /* gpio bit for LCD D5 */
uint8_t d6; /* gpio bit for LCD D6 */
uint8_t d7; /* gpio bit for LCD D7 */
uint8_t bl; /* gpio bit for backlight control */
bool bl_active_high; /* is the backlight control active high? */
uint8_t rows; /* screen geometry, rows, 1, 2 or 4 */
uint8_t cols; /* screen geometry, cols, 8, 12, 16, 20, 24, 40 */
};
/* Used with the SLCDIOC_CREATECHAR ioctl call */
struct slcd_createchar_s
{
uint8_t idx; /* Custom character index; 0-7. Note; you'll probably
* want to avoid code point 0 unless you really need it,
* because embedded nul in a C string can cause surprises.
*/
uint8_t bmp[8]; /* Custom character bitmap. The bitmap is structured as
* a 5x8 bitmap. '1' = pixel on, msb-lsb left-to-right,
* and right-justified (i.e. only bits 4-0 are used).
* Each byte represents 1 row. By convention, you are
* expected to leave the last row all 0's, because it is
* used by the cursor, but this is not strictly required.
*/
};
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: pcf8574_lcd_backpack_register
*
* Description:
* Register a character driver that is a I2C LCD 'backpack' based on the
* PCF8574 I2C IO expander. It allows operation of the ever-popular HD44780
* based LCDs via I2C instead of parallel (saving a bunch of gpio lines).
*
* There are a multitude of these available from various sources (e.g. ebay).
* They typically vary by gpio-to-lcd pin mapping, and I2C addresss, but
* otherwise are functionally identical.
*
* The characters presented for codes depend on the masked rom of the
* particular LCD device attached, but the most common are:
* 'A00', which present 0x20-0x7f, and 0xa0-0xff', similar to JIS X 0201
* 'A02', which present 0x10-0xff, and include various european symbols
* In both cases, codes 0x00-0x07 map to the CGRAM characters, which can be
* loaded via ioctl SLCDIOC_CREATECHAR (q.v.).
*
* This driver supports the SLCD codec for various escape sequences; q.v.
* nuttx/lcd/slcd_codec.h for details. This driver supports the SLCD ioctl
* interface for various extended commands; q.v. nuttx/lcd/slcd_ioctl.h for
* details. This driver supports an additional ioctl for defining custom
* characters; see above for details.
*
* Parameters:
* devpath - path to device node; arbitrary, but typically '/dev/lcd0' or such
* i2c - the low-level i2c bus onto which to bind
* cfg - the board-specific configuration
*
****************************************************************************/
int pcf8574_lcd_backpack_register(FAR const char *devpath,
FAR struct i2c_master_s *i2c,
FAR struct pcf8574_lcd_backpack_config_s *cfg);
#endif /* __INCLUDE_NUTTX_LCD_PCF8574_LCD_BACKPACK_H */