Oops... forgot to add PCF8574 LCD Backpack files before doing the commit.
This commit is contained in:
parent
ac86a8b1fc
commit
51504a032e
1403
drivers/lcd/pcf8574_lcd_backpack.c
Normal file
1403
drivers/lcd/pcf8574_lcd_backpack.c
Normal file
File diff suppressed because it is too large
Load Diff
245
drivers/lcd/pcf8574_lcd_backpack_readme.txt
Normal file
245
drivers/lcd/pcf8574_lcd_backpack_readme.txt
Normal 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
|
176
include/nuttx/lcd/pcf8574_lcd_backpack.h
Normal file
176
include/nuttx/lcd/pcf8574_lcd_backpack.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user