4643fcdfd8
improve timing parameters in init sequences for better display compatibility correct (row,col) -> address calculation; affects 4-line displays update comments in header and readme
253 lines
9.4 KiB
Plaintext
253 lines
9.4 KiB
Plaintext
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
|
|
you absolutely need it, 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.
|
|
* Remember to set the (ros,cols) geometry in pcf8574_lcd_backpack_config_s
|
|
before registration of the driver, since this cannot be determined
|
|
programmatically.
|
|
* If the driver registration step seems to 'hang' it could be the I2C
|
|
driver performing retries due to no response from the LCD backpack. Check
|
|
the address. Turning on debug output for I2C can help make this visible.
|
|
* Don't forget to check the 'contrast' potentiometer. The voltage at the
|
|
central wiper should be approximately 0.3 V - 2.4 V, but the actual value
|
|
is is dependent on the physics of the attached LCD module. The useful
|
|
range of voltages at this pin for any given LCD is quite 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. It's less 'contrast' and
|
|
more 'LCD segment drive bias'.
|