first commit

This commit is contained in:
Jérôme Delacotte
2025-03-06 11:15:32 +01:00
commit 7b30d6e298
5276 changed files with 2108927 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
hd44780ioclass
==============
This directory contains the source code for each of the i/o classes that are
included in the hd44780 library package.
Each i/o class has its own .h file.
#### The library currenly comes with the following i/o subclasses:
* `hd44780_HC1627_I2C` control LCD with native i2c interface (Tsingtek Display HC1627)
* `hd44780_I2Cexp` control LCD using i2c i/o exapander backpack (PCF8574 or MCP23008)
* `hd44780_I2Clcd` control LCD with native i2c interface (PCF2116, PCF2119x, etc...)
* `hd44780_NTCU165ECPB` control Noritake CU165ECBP-T2J LCD display over SPI
* `hd44780_NTCU20025ECPB_pinIO` control Noritake CU20025ECPB using direct Arduino pin connections
* `hd44780_NTCUUserial` control Noritake CU-U Series VFD display in serial mode
* `hd44780_pinIO` control LCD using direct Arduino Pin connections
See each header file for further details.

View File

@@ -0,0 +1,223 @@
// vi:ts=4
// ---------------------------------------------------------------------------
// hd44780_HC1627_I2C.h - hd44780_HC1627_I2C i/o subclass for hd44780 library
// Copyright (c) 2020 Bill Perry
//
// The API functionality provided by this library class is compatible
// with the API functionality of the Arduino LiquidCrystal library.
//
// ---------------------------------------------------------------------------
//
// This file is part of the hd44780 library
//
// hd44780_HC1627_I2C is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 3 of the License.
//
// hd44780_HC1627_I2C is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with hd44780_HC1627_I2C. If not, see <http://www.gnu.org/licenses/>.
//
// ---------------------------------------------------------------------------
//
// It implements all the hd44780 library i/o methods to control an LCD based
// on the Tsingtek Display Co HC1627 LCD devices in i2c mode which have
// a native I2C interface rather than use a simple I2C i/o expander chip
// such as a PCF8574 or MCP23008
// This device uses two i2c address:
// - base address for RS clear
// - one higher for RS set
// The communication interface is very simple:
// 8 bit data is sent using the base address
// command/status use the base address +1
//
// Here are examples of such a device:
// HC1627-B-LWH-I2C-30
// HC1627_I2C-SYH-I2C-30
// http://dvtecnologia.com.br/imagens/HC1627_I2C-SYH-I2C-30_Eng%2CA.pdf
// This device has 2 address pins to configure the base address.
//
// The configurable base address can be:
// 0x38, 0x3A, 0x3C, 0x3E
// It overlaps with the addresses for the PCF8574A and PCF2116/PCF2119x
// NOTE:
// This device needs pullups on the bus signals.
// While it will usually work on an AVR platform without external pullups,
// it doesn't always work.
//
// The I2C can only control the LCD and does not have the capability
// to control the backlight so the backlight will always remain on.
//
// 2020.06.26 bperrybap - initial creation (hd44780_IIClcd)
//
// @author Bill Perry - bperrybap@opensource.billsworld.billandterrie.com
//
// ---------------------------------------------------------------------------
#ifndef hd44780_HC1627_I2C_h
#define hd44780_HC1627_I2C_h
#if (ARDUINO < 101) && !defined(MPIDE)
#error hd44780_HC1627_I2C i/o class requires Arduino 1.0.1 or later
#endif
class hd44780_HC1627_I2C : public hd44780
{
public:
// ====================
// === constructors ===
// ====================
hd44780_HC1627_I2C(uint8_t i2c_addr=0) : _Addr(i2c_addr) {} // zero addres means auto locate
private:
// ====================
// === private data ===
// ====================
uint8_t _Addr; // I2C base Address of the LCD
// ==================================================
// === hd44780 i/o subclass virtual i/o functions ===
// ==================================================
//
// Note:
// It is not possible to control the backlight nor
// is it possible to read from the device.
// so iosetbacklight() and ioread()
// will not be defined and will use the defaults in
// hd44780
// ioinit() - Returns non zero if initialization failed.
int ioinit()
{
int status;
/*
* First, initialize the i2c (Wire) library.
* This really shouldn't be here
* because Wire.begin() should only be called once, but
* unfortunately, there is no way to know if anybody
* else has called this.
* I believe that it is unreasonable to require the the user
* sketch code to do it, because all that should change between
* interfaces should be the constructor
* So we go ahead and call it here.
*/
Wire.begin();
/*
* If i2c address was not specified go try to locate device
*/
if(!_Addr)
{
if( !(_Addr = LocateDevice())) // failed to find device
{
return(hd44780::RV_ENXIO);
}
}
/*
* Check to see if the device is responding
*/
Wire.beginTransmission(_Addr);
if( (status = Wire.endTransmission()) )
{
if(status == 1)
status = hd44780::RV_EMSGSIZE;
else if(status == 2)
status = hd44780::RV_ENXIO;
else
status = hd44780::RV_EIO;
}
/*
* this device only runs in 8 bit mode
*/
_displayfunction = HD44780_8BITMODE;
return ( status );
}
//
// iowrite(type, value) - send either a command or data byte to lcd
// returns zero on success, non zero on failure
int iowrite(hd44780::iotype type, uint8_t value)
{
uint8_t addr;
/*
* No need to look for 4 bit commands as all bits are already in
* proper upper nibble and unsued bits are zero.
* This allows the special 4 bit commands to be sent "As is" when
* using an 8 bit interface - which is what this i2c LCD uses.
*/
if(type == hd44780::HD44780_IOdata)
{
addr = _Addr + 1; // RS bit is set by using 1 higher address
}
else
{
addr = _Addr; // RS is clear when using base address
}
/*
* ensure that previous LCD instruction finished.
* There is a 25us offset since there will be at least 1 byte
* (the i2c address) transmitted over i2c before the next LCD instruction
* information could get to the LCD.
* At 400Khz (max rate supported by the LCD) 8 bits plus start and stop
* bits is 25us.
* So there is at least 25us of time overhead in the physical interface.
*/
waitReady(-25);
/*
* Send the next LCD instruction
*/
Wire.beginTransmission(addr);
Wire.write(value); // send data/cmd
if(Wire.endTransmission())
return(hd44780::RV_EIO);
else
return(hd44780::RV_ENOERR);
}
// ================================
// === internal class functions ===
// ================================
// LocateDevice() - Locate I2C LCD device
uint8_t LocateDevice()
{
uint8_t error, address;
// Search for 4 base address pairs
for(address = 0x38; address <= 0x3E; address += 2 )
{
Wire.beginTransmission(address);
error = Wire.endTransmission();
// chipkit i2c screws up if you do a beginTransmission() too quickly
// after an endTransmission()
// below 20us will cause it to fail,
// we use delay(1) here to accomdate that as well as ensure that
// systems with watchdog timers like ESP8266 won't have issues.
delay(1);
if(error == 0) // if no error we found something
{
return(address);
}
}
return(0); // could not locate device address
}
}; // end of class definition
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,271 @@
// vi:ts=4
// ---------------------------------------------------------------------------
// hd44780_I2Clcd.h - hd44780_I2Clcd i/o subclass for hd44780 library
// Copyright (c) 2013-2020 Bill Perry
//
// The API functionality provided by this library class is compatible
// with the API functionality of the Arduino LiquidCrystal library.
//
// ---------------------------------------------------------------------------
//
// This file is part of the hd44780 library
//
// hd44780_I2Clcd is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 3 of the License.
//
// hd44780_I2Clcd is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with hd44780_I2Clcd. If not, see <http://www.gnu.org/licenses/>.
//
// ---------------------------------------------------------------------------
//
// It implements all the hd44780 library i/o methods to control an LCD based
// on the Hitachi HD44780 and compatible chipsets which have
// a native I2C interface rather than use a simple I2C i/o expander chip
// such as a PCF8574 or MCP23008
//
// Here is a example of such a device:
// https://seeeddoc.github.io/Grove-LCD_RGB_Backlight/res/JHD1214Y_YG_1.0.pdf
// This device uses address 0x3E and incorrectly responds to read requests
// It overlaps with the addresses for the PCF8574A
// It will lock up module and/or the AVR i2c h/w and require a power cycle to reset.
// NOTE:
// This device needs pullups on the bus signals.
// While it will usually work on an AVR platform without external pullups,
// it doesn't always work.
//
// PCF2119x chips use this protocol and slave addresses are 0x3A and 0x3B
// This is defined in the allocated i2c address table Group 7
// It overlaps with the addresses for the PCF8574A
// This device has extended function set commands that are not directly
// supported by the library but can be used through the command() API
//
// PCF2116 chips use this protocol and slave addresses are 0x3A and 0x3B
// It overlaps with the addresses for the PCF8574A
//
// Raystar RC1602B5-LLH-JWV is jumper settable to 0x3c, 0x3d, 0x3e, 0x3f
// and appears to only be able to be written to.
// It overlaps with the addresses for the PCF8574A
//
// Known addresses for this type of device:
// 0x3a PCF2116/PCF2119x
// 0x3b PCF2116/PCF2119x
// 0x3c Raystar
// 0x3d Raystar
// 0x3e Raystar
// 0x3f Raystar
//
// The interface consists of sending 2 bytes over I2C for each hd44780
// data/cmd.
// The first byte is a control byte which sets the RS signal
// The second byte is the data.
//
// The I2C can only control the LCD and does not have the capability
// to control the backlight so the backlight will always remain on.
//
// Attempting to read from some of these devices will lockup the AVR Wire
// library.
//
// 2018.08.06 bperrybap - removed TinyWireM work around (TinyWireM was fixed)
// 2017.05.12 bperrybap - now requires IDE 1.0.1 or newer
// This is to work around TinyWireM library bugs
// 2016.12.26 bperrybap - added auto i2c address location
// 2016.09.08 bperrybap - changed param order of iowrite() to match ioread()
// 2016.08.06 bperrybap - changed iosend() to iowrite()
// 2016.07.27 bperrybap - added return status for iosend()
// 2017.07.21 bperrybap - merged all class code into header
// 2017.07.20 bperrybap - merged into hd44780 library
// 2016.06.09 bperrybap - changed name to hd44780_I2Clcd
// 2016.06.08 bperrybap - removed pre IDE 1.0 support
// 2016.06.03 bperrybap - added smart execution delays
// 2014.02.15 bperrybap - changed to use hd44780 base class library
// 2013.06.01 bperrybap - initial creation (hd44780_IIClcd)
//
// @author Bill Perry - bperrybap@opensource.billsworld.billandterrie.com
//
// ---------------------------------------------------------------------------
#ifndef hd44780_I2Clcd_h
#define hd44780_I2Clcd_h
// A bug in TinyWireM is that requestFrom() returns incorrect status
// so its return status can't be used. Instead the code will check the return
// from Wire.read() which will return -1 if there no data was transfered.
#if (ARDUINO < 101) && !defined(MPIDE)
#error hd44780_I2Clcd i/o class requires Arduino 1.0.1 or later
#endif
class hd44780_I2Clcd : public hd44780
{
public:
// ====================
// === constructors ===
// ====================
hd44780_I2Clcd(uint8_t i2c_addr=0) : _Addr(i2c_addr) {} // zero addres means auto locate
private:
// ====================
// === private data ===
// ====================
// Control byte bit values
// RS signal in HD44780 interface
// set for data writes, clear for cmds
// note: some datasheets call this A0 bit
static const uint8_t I2Clcd_RS = (1 << 6);
// set if another control byte following data
// clear if last control byte
static const uint8_t I2Clcd_CO = (1 << 7);
uint8_t _Addr; // I2C Address of the LCD
// ==================================================
// === hd44780 i/o subclass virtual i/o functions ===
// ==================================================
//
// Note:
// It is not possible to control the backlight nor
// is it possible to read from the device.
// so iosetbacklight() and ioread()
// will not be defined and will use the defaults in
// hd44780
// ioinit() - Returns non zero if initialization failed.
int ioinit()
{
int status;
/*
* First, initialize the i2c (Wire) library.
* This really shouldn't be here
* because Wire.begin() should only be called once, but
* unfortunately, there is no way to know if anybody
* else has called this.
* I believe that it is unreasonable to require the the user
* sketch code to do it, because all that should change between
* interfaces should be the constructor
* So we go ahead and call it here.
*/
Wire.begin();
/*
* If i2c address was not specified go try to locate device
*/
if(!_Addr)
{
if( !(_Addr = LocateDevice())) // failed to find device
{
return(hd44780::RV_ENXIO);
}
}
/*
* Check to see if the device is responding
*/
Wire.beginTransmission(_Addr);
if( (status = Wire.endTransmission()) )
{
if(status == 1)
status = hd44780::RV_EMSGSIZE;
else if(status == 2)
status = hd44780::RV_ENXIO;
else
status = hd44780::RV_EIO;
}
/*
* this device only runs in 8 bit mode
*/
_displayfunction = HD44780_8BITMODE;
return ( status );
}
//
// iowrite(type, value) - send either a command or data byte to lcd
// returns zero on success, non zero on failure
int iowrite(hd44780::iotype type, uint8_t value)
{
uint8_t ctlbyte;
/*
* No need to look for 4 bit commands as all bits are already in
* proper upper nibble and unsued bits are zero.
* This allows the special 4 bit commands to be sent "As is" when
* using an 8 bit interface - which is what this i2c LCD uses.
*/
if(type == hd44780::HD44780_IOdata)
{
ctlbyte = I2Clcd_RS; // control byte with RS and no continue
}
else
{
ctlbyte = 0; // control byte with no RS and no continue
}
/*
* ensure that previous LCD instruction finished.
* There is a 25us offset since there will be at least 1 byte
* (the i2c address) transmitted over i2c before the next LCD instruction
* information could get to the LCD.
* At 400Khz (max rate supported by the LCD) 8 bits plus start and stop
* bits is 25us.
* So there is at least 25us of time overhead in the physical interface.
*/
waitReady(-25);
/*
* Send the next LCD instruction
*/
Wire.beginTransmission(_Addr);
Wire.write(ctlbyte); // send control byte
Wire.write(value); // send data/cmd
if(Wire.endTransmission())
return(hd44780::RV_EIO);
else
return(hd44780::RV_ENOERR);
}
// ================================
// === internal class functions ===
// ================================
// LocateDevice() - Locate I2C LCD device
uint8_t LocateDevice()
{
uint8_t error, address;
// Search for 6 addresses
for(address = 0x3a; address <= 0x3f; address++ )
{
Wire.beginTransmission(address);
error = Wire.endTransmission();
// chipkit i2c screws up if you do a beginTransmission() too quickly
// after an endTransmission()
// below 20us will cause it to fail,
// we use delay(1) here to accomdate that as well as ensure that
// systems with watchdog timers like ESP8266 won't have issues.
delay(1);
if(error == 0) // if no error we found something
{
return(address);
}
}
return(0); // could not locate device address
}
}; // end of class definition
#endif

View File

@@ -0,0 +1,447 @@
// vi:ts=4
// ---------------------------------------------------------------------------
// hd44780_NTCU165ECPB.h - hd44780_NTCU165ECPB i/o subclass for hd44780 library
// Copyright (c) 2016-2020 Bill Perry
//
// The API functionality provided by this library class is compatible
// with the API functionality of the Arduino LiquidCrystal library.
//
// ---------------------------------------------------------------------------
//
// This file is part of the hd44780 library
//
// hd44780_NTCU165ECPB is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 3 of the License.
//
// hd44780_NTCU165ECPB is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with hd44780_NTCU165ECPB. If not, see <http://www.gnu.org/licenses/>.
//
// ---------------------------------------------------------------------------
//
// It implements hd44780 library subclass i/o methods to control a
// Noritake CU165ECBP-T2J display.
// The Noritake CU165ECBP-T2J display is a 16x1 line VFD display.
// It is not a hd44780 display. So while this device is supported by hd44780,
// it will not be capable of supporting all the hd44780 capabilities.
// The subclass will do its best to emulatate hd44780 functions and map
// them to equivalent functionality when possible but certain capabilities
// will not be possible.
//
// Datasheet can be found here:
// http://www.blog.zapro.dk/wp-content/CU165.pdf
// Full datasheet/release notes:
// http://www.blog.zapro.dk/wp-content/CU165ECPB_T2J_specification.pdf
//
// The device uses SPI for communication.
// Pinout:
// 1 Vcc
// 2 GND
// 3 /CS (SS)
// 4 CLK (SCK)
// 5 DATA (MOSI)
//
// ---------------------------------------------------------------------------
// history
//
// 2017.12.23 bperrybap - added support LCD API 1.0 init()
// 2016.08.22 bperrybap - initial creation
//
// @author Bill Perry - bperrybap@opensource.billsworld.billandterrie.com
//
// ---------------------------------------------------------------------------
#ifndef hd44780_NTCU165ECPB_h
#define hd44780_NTCU165ECPB_h
class hd44780_NTCU165ECPB : public hd44780
{
public:
// ====================
// === constructors ===
// ====================
// no parameters: use h/w spi signals
// Note: This doesn't work for Leonardo since SS is not available and only drives an LED
hd44780_NTCU165ECPB() : hd44780(16,1), _cs(SS), _clk(SCK), _data(MOSI) { }
// supply alternate chip/slave select
// will use h/w spi with alternate chip select
hd44780_NTCU165ECPB(int cs) : hd44780(16,1), _cs(cs), _clk(SCK), _data(MOSI) { }
// supply pins for chip select, clock, and data
hd44780_NTCU165ECPB(int cs, int clock, int data) : hd44780(16,1), _cs(cs), _clk(clock), _data(data) { }
private:
// ====================
// === private data ===
// ====================
// Arduino Pin information
const uint8_t _cs; // arduino pint for slave select (chip select)
const uint8_t _clk; // arduino pin for clock
const uint8_t _data; // arduino pin for master out slave in (data)
// other internal variables
uint8_t _cgmode; // set when in cgramaddr mode
uint8_t _udf[5]; // udf buffer area
uint8_t _cgindx; // index into cgdata (will be 0 to 7)
uint8_t _cgchar; // custom character (0 to 7, this board goes to f)
// device commands
static const uint8_t CMD_SETDISPLAYLEN = 0x00; // lower 3 bits sets total digits/cols
// to display
// 0 is 9 and 7 means all 16 digits/cols
static const uint8_t CMD_SETBRIGHTNESS = 0x08; // lower 3 bits sets brigtness
// 0 is 1/16 and 7 is 14/16 max
static const uint8_t CMD_SETDIGSCAN160 = 0xF6; // set digit scan time to 160us
static const uint8_t CMD_SETDIGSCAN320 = 0xF7; // set digit scan time to 320us
static const uint8_t CMD_SETDIGPOINTER = 0xE0; // set column, lower 4 bits sets column
static const uint8_t CMD_NOAUTOINCREMENT = 0xF0; // no auto advance to next column
static const uint8_t CMD_AUTOINCREMENT = 0xF1; // auto advance to next column
static const uint8_t CMD_DISPLAYOFF = 0xF0; // all digits/dots off
static const uint8_t CMD_DISPLAYON = 0xF1; // normal display mode
static const uint8_t CMD_DISPLAYALLON = 0xF3; // all digits, all dots on
static const uint8_t CMD_STOREUDF = 0xFC; // begin user defined font (custom char)
// There are 16 custom chars, they are
// accessed/printed using 0x90-0x9F
// they are programmed using locations 0-F
// Each character glyph is 5 bytes.
// Each byte is vertical 7 pixels with the
// MSB at the top and the LSB dropped.
// The first byte is the left most column of
// the glyph.
// full command data is 7 bytes:
// 0xFC,<location (0-f)> 5 bytes of data
// start of custom user defined font (UDF) characters
// There are 16 UDF characters that start here
static const uint8_t UDF_BASE = 0x90;
// ==================================================
// === hd44780 i/o subclass virtual i/o functions ===
// ==================================================
//
// Note:
// It is not possible to read from the device.
// so ioread(), and & iostatus()
// will not be defined and will use the defaults in
// hd44780 which will return an apropriate error status
// ioinit() - Returns non zero if initialization failed.
int ioinit()
{
int status = 0;
// setup SPI signals
digitalWrite(_cs, HIGH);
pinMode(_cs, OUTPUT);
pinMode(_clk, OUTPUT);
digitalWrite(_clk, LOW);
pinMode(_data, OUTPUT);
// check to see if SPI has beginTransaction() and endTransaction()
// this was added in IDE 1.06 and so if using an IDE older than that,
// H/W spi will not be used
// This requires that <SPI.h> be included prior to this header
#if (defined(SPI_HAS_TRANSACTION) && !defined(ARDUINO_ARCH_ESP32)) || \
(defined(ARDUINO_ARCH_ESP32) && defined(_SPI_H_INCLUDED) && defined(SPI_HAS_TRANSACTION))
// check to see if h/w spi can be used
if((_data == MOSI) && (_clk == SCK))
SPI.begin();
#endif
// maybe look at cols to see how many should be turned on?
devicewrite(CMD_SETDISPLAYLEN|0x7); // show all 16 characters
devicewrite(CMD_SETDIGSCAN320); // set digit scan time to 320us
devicewrite(CMD_AUTOINCREMENT); // set auto advance to next column on writes
// for now turn on display and "backlight"
// clear and set to "home" address position
devicewrite(CMD_DISPLAYON);
devicewrite(CMD_SETBRIGHTNESS | 0x7); // max brightness
device_clear();
return ( status );
}
//
// iowrite() - send either a command or data byte to lcd
// returns zero on success, non zero on failure
//
// Things are a bit tricky here since this is not a hd44780 device.
// The code must look at the commands and emulate them or drop them
//
int iowrite(hd44780::iotype type, uint8_t value)
{
/*
* waitReady() is not called as this module does not need any execution time
* delays even when using a 4mHz h/w clock.
*/
/*
* 4 bit commands are part of low level hd44780 initalization.
* since this is not a hd44780 display, just throw them away
* and say they "worked"
*/
if(type == hd44780::HD44780_IOcmd4bit)
return(0);
if(type == hd44780::HD44780_IOcmd)
{
return(hd44780cmd(value));
}
if(_cgmode)
{
// in cgram mode so have to process custom character data.
// hd44780 font data is stored horizontally in 8 bytes
// with each byte being 1 row of dots
// with upper 3 bits of each byte ignored.
// first byte of the 8 bytes is upper row of dots.
// Noritake UDF format is vertical columns of 5 bytes with
// the LSB of each byte ignored as it only has 5x7 dots.
// the first byte is the right most column of 7 dots of the font.
//
// Because of this mismatch, the bits & bytes have to be flipped
// around and stored in the 5 UDF bytes until the full 8 hd44780 font
// bytes come in. Once all 8 hd44780 font bytes are in,
// the 5 byte UDF font definition can be pushed to the device.
for(int i = 0; i < 5; i++)
{
if( (value & (1<< i)))
{
_udf[4-i] |= (1 << (7- _cgindx));
}
else
{
_udf[4-i] &= ~(1 << (7-_cgindx));
}
}
_cgindx++;
if(_cgindx > 7) // is this the last of the 8 bytes?
{
// 0xFC,<location (0-f)> 5 bytes of data
devicewrite(CMD_STOREUDF);
devicewrite(_cgchar);
for(int i = 0; i < 5; i++)
{
devicewrite(_udf[i]);
}
// now send it again for the 8-f version
// to emulate the hd44780 custom chars
// that duplicate 0-7 at 8-f
devicewrite(CMD_STOREUDF);
devicewrite(_cgchar+8);
for(int i = 0; i < 5; i++)
{
devicewrite(_udf[i]);
}
// note, cgmode is still in effect.
// the UDF/font byte index is reset back to 0,
// and the cgchar location is bumped.
// this is to allow for the case when the code may
// be attempting to program more than a single character at once.
_cgindx = 0;
_cgchar++;
_cgchar &= 0xf;
}
return(0);
}
// upper 32 bytes are for commands so drop chars that are in the 0xE0 to 0xFF range
if(value >= 0xE0)
{
return(0);
}
// check for hd44780 custom font characters,
// The hd44780 character code must be mapped to the UDF characters
// which start at a different location.
if(value < 16)
{
value += UDF_BASE;
}
devicewrite(value);
return(0);
}
// iosetBacklight() - set backlight brightness
// Since this display does not use a backlight but can control pixel intensity
// use the backlight brightness to set the pixel intensity.
//
// Note:
// The dimmer value on the CU165 is 3 bit value
// other than the 000 value it looks like the value is mapped to a 4 bit
// value by adding a 0 msb.
// 000 is 1/16
// 001 is 2/16
// 010 is 4/16
// 011 is 6/16
// 100 is 8/16
// 101 is 10/16
// 110 is 12/16
// 111 is 14/16
//
// Becauswe of this, an API dimvalue of 0 does not turn off the display pixels
// so the code must check for that and explicitly turn off the display if the
// API dimvalue is zero.
int iosetBacklight(uint8_t dimvalue)
{
if(dimvalue)
{
// the dim value is 0-255 so devide by 32 to get a value 0-7
devicewrite(CMD_SETBRIGHTNESS | (dimvalue/32));
devicewrite(CMD_DISPLAYON);
}
else
{
devicewrite(CMD_DISPLAYOFF);
}
return(RV_ENOERR);
}
// ================================
// === internal class functions ===
// ================================
// devicwrite() - send a byte to the device
void devicewrite(uint8_t value)
{
// use h/w spi if we can
// note: checking for SPI support (SPI.h included) is messy since
// ESP32 core always defines SPI_HAS_TRANSACTION even when SPI.h is not included
#if (defined(SPI_HAS_TRANSACTION) && !defined(ARDUINO_ARCH_ESP32)) || \
(defined(ARDUINO_ARCH_ESP32) && defined(_SPI_H_INCLUDED) && defined(SPI_HAS_TRANSACTION))
if((_data == MOSI) && (_clk == SCK))
{
// NOTE: spec says 2Mhz is max, 4Mhz seems to work.
SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
digitalWrite(_cs,LOW); // select device
SPI.transfer(value);
delayMicroseconds(1); // must delay at least 130ns before de-selecting
digitalWrite(_cs,HIGH); // deselect device
SPI.endTransaction();
}
else
#endif
{
digitalWrite(_cs,LOW); // select device
myshiftOut(_data, _clk, MSBFIRST, value);
delayMicroseconds(1); // must delay at least 130ns before de-selecting
digitalWrite(_cs,HIGH); // deselect device
}
}
// can't use standard shiftOut() on esp32 because it is too fast for display
//
void myshiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
uint8_t i;
for (i = 0; i < 8; i++) {
if (bitOrder == LSBFIRST) {
digitalWrite(dataPin, val & 1);
val >>= 1;
} else {
digitalWrite(dataPin, (val & 128) != 0);
val <<= 1;
}
// insert delay on super fast processors to slow down clock
// and allow a bit of time for slew rate rise when using 3v processor with 5v logic
#if (F_CPU > 40000000)
delayMicroseconds(1); // min pulse width is 230ns
#endif
digitalWrite(clockPin, HIGH);
#if (F_CPU > 40000000) // insert delay on super fast processors to slow down clock
delayMicroseconds(1); // min pulse width is 230ns
#endif
digitalWrite(clockPin, LOW);
}
}
// hd44780cmd() - emulate hd44780 commands best we can
// for now, silently drop unsupported commands
int hd44780cmd(uint8_t value)
{
// detect the hd44780 command
if(value & HD44780_SETDDRAMADDR)
{
_cgmode = 0; // back to ddram mode
devicewrite(CMD_SETDIGPOINTER | (value & 0xf));
}
else if(value & HD44780_SETCGRAMADDR)
{
_cgmode = 1; // enter cgram mode
_cgindx = 0;
_cgchar = (value >> 3) & 0x7; // for now limmit to 8 custom like hd44780
}
else if(value & HD44780_FUNCTIONSET)
{
// not supported
}
else if(value & HD44780_CURDISPSHIFT)
{
// not supported
}
else if(value & HD44780_DISPLAYCONTROL)
{
if(value & HD44780_DISPLAYON)
devicewrite(CMD_DISPLAYON);
else
devicewrite(CMD_DISPLAYOFF);
}
else if(value & HD44780_ENTRYMODESET)
{
// not supported
}
else if(value & HD44780_RETURNHOME)
{
_cgmode = 0; // back to ddram mode
devicewrite(CMD_SETDIGPOINTER);
}
else if(value & HD44780_CLEARDISPLAY)
{
_cgmode = 0; // back to ddram mode
device_clear();
}
return(0);
}
void device_clear()
{
devicewrite(CMD_SETDIGPOINTER);
for(uint8_t col = 0; col < 16; col++)
{
devicewrite(' ');
}
devicewrite(CMD_SETDIGPOINTER);
}
}; // end of class definition
#endif

View File

@@ -0,0 +1,402 @@
// vi:ts=4
// ---------------------------------------------------------------------------
// hd44780_NTCU20025ECPB_pinIO.h -
// hd44780_NTCU20025ECPB_pinIO i/o subclass for hd44780 library
// Copyright (c) 2019-2020 Bill Perry
// ---------------------------------------------------------------------------
//
// This file is part of the hd44780 library
//
// hd44780_NTCU20025ECPB_pinIO is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 3 of the License.
//
// hd44780_NTCU20025ECPB_pinIO is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with hd44780_NTCU20025ECPB_pinIO.
// If not, see <http://www.gnu.org/licenses/>.
//
// ---------------------------------------------------------------------------
//
// It implements hd44780 library subclass i/o methods to control Noritake
// CU-U series displays like the Noritake CU20025ECPB-U1J or CU20025ECPB-W1J
// The Noritake CU20025ECPB display is a 20x2 line VFD display.
// It is not a hd44780 display but is nearly fully compatible with a hd44780
// display. The only real difference is the backlight control.
// So while this device is supported by the hd44780 library,
// the subclass will do its best to emulatate hd44780 functions and map
// them to equivalent functionality when possible.
//
// ---------------------------------------------------------------------------
// Note:
// The CU20025 device is very compatible with a standard hd44780 display.
// The only difference is that it provides dimming control of the pixels
// whereas a standard hd44780 display does not have dimming control, but
// often has a backlight that is controlled with h/w outside of the hd44780
// chipset.
//
// Datasheets:
//
// Full Data sheet: (requires registration):
// https://www.noritake-elec.com/user/datasheet?SCID=147&VFDID=116&PSID=5908e690167775b2f6241569268c741e&FMTP=2#
// https://www.noritake-elec.com/products/vfd-display-module/character-display/cu-u-series
// https://www.noritake-elec.com/products/vfd-display-module/lineup?cu-u
//
// Summary Datasheet:
// https://media.digikey.com/pdf/Data%20Sheets/Noritake%20PDFs/CU20025ECPB-U1J.pdf
// http://www.farnell.com/datasheets/49448.pdf
// https://media.digikey.com/pdf/Data%20Sheets/Noritake%20PDFs/CU20025ECPB-W1J.pdf
//
// AppNote:
// http://www.northstardatasheet.com/datasheet/APN1100-01.pdf
// https://www.datasheet.directory/index.php?title=Special:PdfViewer&url=https%3A%2F%2Fdatasheet.iiic.cc%2Fdatasheets-0%2Fnoritake%2FCU20025ECPB-W1J.pdf
//
// CU20025 pinout:
// 1 - LCD gnd
// 2 - VCC (5v)
// 3 - not connected (assuming JP1 pins 1 and 2 are not shorted)
// 4 - RS Register Select (rs)
// 5 - Read/Write
// 6 - Enable (en)
// 7 - Data 0 (db0) ----
// 8 - Data 1 (db1) |-------- Not used in 4 bit mode
// 9 - Data 2 (db2) |
// 10 - Data 3 (db3) ----
// 11 - Data 4 (db4)
// 12 - Data 5 (db5)
// 13 - Data 6 (db6)
// 14 - Data 7 (db7)
//
// ---------------------------------------------------------------------------
//
// The API functionality provided by this library class is compatible
// with the functionality of the Arduino LiquidCrystal library.
//
//
// 2019.11.23 bperrybap - initial creation from hd44780_pinIO i/o class
//
// @author Bill Perry - bperrybap@opensource.billsworld.billandterrie.com
// ---------------------------------------------------------------------------
#ifndef hd44780_NTCU20025ECPB_pinIO_h
#define hd44780_NTCU20025ECPB_pinIO_h
#include <hd44780.h>
// For STUPID versions of gcc that don't hard error on missing header files
#ifndef hd44780_h
#error Missing hd44780.h header file
#endif
#include <pins_arduino.h> // to get PWM detection macros
class hd44780_NTCU20025ECPB_pinIO : public hd44780
{
public:
// ====================
// === constructors ===
// ====================
// 4 bit mode constructor without r/w control
hd44780_NTCU20025ECPB_pinIO(uint8_t rs, uint8_t en,
uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7) :
hd44780(20,2), _rs(rs), _rw(0xff), _en(en),
_d4(d4), _d5(d5), _d6(d6), _d7(d7) { }
// 4 bit mode constructor with r/w control
hd44780_NTCU20025ECPB_pinIO(uint8_t rs, uint8_t rw, uint8_t en,
uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7) :
hd44780(20,2), _rs(rs), _rw(rw), _en(en),
_d4(d4), _d5(d5), _d6(d6), _d7(d7) { }
private:
// ====================
// === private data ===
// ====================
// Arduino Pin information
uint8_t _rs; // hd44780 rs arduino pin
uint8_t _rw; // hd44780 rw arduino pin
uint8_t _en; // hd44780 en arduino pin
uint8_t _d4; // hd44780 d4 arduino pin
uint8_t _d5; // hd44780 d4 arduino pin
uint8_t _d6; // hd44780 d4 arduino pin
uint8_t _d7; // hd44780 d4 arduino pin
// ==================================================
// === hd44780 i/o subclass virtual i/o functions ===
// ==================================================
// ioinit() - initialize the h/w
int ioinit()
{
// initialize Arduino pins used for hd44780 signals
// NOTE:
// _rw and _en pins are set to low even though they should
// default to this from a an initial reset.
// the reason is it is possible initilization is called without
// without resetting the processor. In that case the state of the
// bit in the port register would be unknown, so it is safest to set
// to the desired state (LOW).
pinMode(_rs, OUTPUT);
if(_rw != 0xff)
{
pinMode(_rw, OUTPUT);
// if r/w is used it be will set to be low,
// it will only be turned hi during a read and
// then set back to low. This offers a slight boost
// for writes since normally reads are not done very often
digitalWrite(_rw, LOW);
}
pinMode(_en, OUTPUT);
digitalWrite(_en, LOW);
pinMode(_d4, OUTPUT);
pinMode(_d5, OUTPUT);
pinMode(_d6, OUTPUT);
pinMode(_d7, OUTPUT);
return(hd44780::RV_ENOERR); // all is good
}
// ioread(type) - read a byte from LCD DDRAM
//
// returns:
// success: 8 bit value read
// failure: negative value: reading not supported
//
int ioread(hd44780::iotype type)
{
uint8_t data = 0;
// check if r/w control supported
if(_rw == 0xff)
return(hd44780::RV_ENOTSUP);
waitReady(); // ensure previous instruction finished
// put all the LCD data pins into input mode.
pinMode(_d4, INPUT);
pinMode(_d5, INPUT);
pinMode(_d6, INPUT);
pinMode(_d7, INPUT);
// set RS based on type of read (data or status/cmd)
if(type == hd44780::HD44780_IOdata)
{
digitalWrite(_rs, HIGH); // RS high to access data reg
}
else
{
digitalWrite(_rs, LOW); // RS LOW to access status/cmd reg
}
// r/w HIGH for reading
digitalWrite(_rw, HIGH);
// raise E to allow reading the data.
digitalWrite(_en, HIGH);
// allow for hd44780 tDDR (Data delay time) before reading data
// this could be much shorter but this is portable for all CPUs.
// and it will ensure that hd44780 PWEH timing is honored as well.
delayMicroseconds(1);
// read pins for d4-d7 into upper nibble of byte
if(digitalRead(_d4) == HIGH)
data |= (1 << 4);
if(digitalRead(_d5) == HIGH)
data |= (1 << 5);
if(digitalRead(_d6) == HIGH)
data |= (1 << 6);
if(digitalRead(_d7) == HIGH)
data |= (1 << 7);
// lower E after reading nibble
digitalWrite(_en, LOW);
// allow for hd44780 1/2 of tcycE (Enable cycle time)
// this could be much shorter but this is portable for all CPUs.
delayMicroseconds(1);
// raise E to allow reading the lower nibbly of the byte
digitalWrite(_en, HIGH);
// allow for hd44780 tDDR (Data delay time) before reading data
// this could be shorter but this is portable for all CPUs.
// and it will ensure that hd44780 PWEH timing is honored as well.
delayMicroseconds(1);
// read pins for d4-d7 into upper nibble of byte
if(digitalRead(_d4) == HIGH)
data |= (1 << 0);
if(digitalRead(_d5) == HIGH)
data |= (1 << 1);
if(digitalRead(_d6) == HIGH)
data |= (1 << 2);
if(digitalRead(_d7) == HIGH)
data |= (1 << 3);
// lower E after reading nibble
digitalWrite(_en, LOW);
//
// put all pins back into state for writing to LCD
//
// put all the LCD data pins into input mode.
pinMode(_d4, OUTPUT);
pinMode(_d5, OUTPUT);
pinMode(_d6, OUTPUT);
pinMode(_d7, OUTPUT);
// r/w LOW for Writing
digitalWrite(_rw, LOW);
return(data);
}
// iowrite(type, value) - send either a command or data byte to lcd
// returns zero on success, non zero on failure
int iowrite(hd44780::iotype type, uint8_t value)
{
if(type == hd44780::HD44780_IOdata)
digitalWrite(_rs, HIGH);
else
digitalWrite(_rs, LOW);
// "4 bit commands" are special.
// They are used only during initalization and
// are used to reliably get the LCD and host in nibble sync
// with each other and to get the LCD into 4 bit mode.
// 8 bit host interfaces don't have to worry about this,
// but 4 bit host interfaces must only send the upper nibble.
// note:
// waitReady() is explicitly called as late as possible *after* the Arduino
// pins for the LCD control and data signals are set to allow
// overhead of the digitalWrite() calls to be hidden under execution time.
write4bits(value>>4); // setup uppper nibble d4-d7 lcd pins
waitReady(); // ensure previous instruction finished
pulseEnable(); // send upper nibble to LCD
// send lower nibble if not a 4 bit command
// (sends nibble for both data and "normal" commands)
if (type != hd44780::HD44780_IOcmd4bit )
{
write4bits((value & 0x0F));// setup lower nibble on d4-d7 lcd pins
pulseEnable(); // send lower nibble to LCD
}
return(hd44780::RV_ENOERR); // it never fails
}
// iosetBacklight() - set backlight brightness
// Since this display does not use a backlight but can control pixel intensity
// use the backlight brightness to set the pixel intensity.
//
// Note:
// The brightness control byte on the CU20025 is 2 bit value
// 00 is 100%
// 01 is 75%
// 10 is 50%
// 11 is 25%
// To handle 0% the display will be turned off
//
// dimvalue levels will be mapped as follows:
// 0 - 0% / display off
// 01 - 63 - 25%
// 64 -127 - 50%
// 128-191 - 75%
// 192-255 -100%
int iosetBacklight(uint8_t dimvalue)
{
if(dimvalue)
{
// brightness is set by sending a brightness control byte after a function set
// Data which follows the FunctionSet command is considered as brightness data.
// send a function set,
// this is effectively a NOP, since it will set mode to what it already is
// Data which follows
command(HD44780_FUNCTIONSET | _displayfunction);
// send the brightness control byte to set brightness level
_write(3 - dimvalue / 64);
display(); // turn display on in case it was off
}
else
{
noDisplay(); // turn off display for 0% brightness
}
// can't tell if it really worked with pin i/o, so say it always worked.
return(hd44780::RV_ENOERR); // it never fails
}
// ================================
// === internal class functions ===
// ================================
// write4bits() - set the 4 hd44780 data lines
void write4bits(uint8_t value)
{
// write the bits on the LCD data lines but don't send the data
if(value & 1)
digitalWrite(_d4, HIGH);
else
digitalWrite(_d4, LOW);
if(value & 2)
digitalWrite(_d5, HIGH);
else
digitalWrite(_d5, LOW);
if(value & 4)
digitalWrite(_d6, HIGH);
else
digitalWrite(_d6, LOW);
if(value & 8)
digitalWrite(_d7, HIGH);
else
digitalWrite(_d7, LOW);
}
// pulseEnable() - toggle en to send data to hd44780 module
void pulseEnable(void)
{
digitalWrite(_en, HIGH);
#if defined (ARDUINO_ARCH_ESP8266)
// the extra delay here is not for the LCD, it is to allow signal lines time
// to settle when using 3v esp modules with 5v LCDs.
// 3v outputs on 5v inputs is already a bit out of spec and
// without this, the slew rate isn't fast enough to get "reliable"
// signal levels
// while it isn't needed for 3v LCDs, the time penalty isn't much
// to always do it.
delayMicroseconds(2); // enable pulse must be >450ns
#else
delayMicroseconds(1); // enable pulse must be >450ns
#endif
digitalWrite(_en, LOW);
}
}; // end of class definition
#endif

View File

@@ -0,0 +1,293 @@
// vi:ts=4
// ---------------------------------------------------------------------------
// hd44780_NTCUUserial.h - hd44780_NTCUUserial i/o subclass for hd44780 library
// Copyright (c) 2013-2019 Bill Perry
//
// The API functionality provided by this library class is compatible
// with the API functionality of the Arduino LiquidCrystal library.
//
// ---------------------------------------------------------------------------
//
// This file is part of the hd44780 library
//
// hd44780_NTCUUserial is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 3 of the License.
//
// hd44780_NTCUUserial is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with hd44780_NTCUUserial. If not, see <http://www.gnu.org/licenses/>.
//
// ---------------------------------------------------------------------------
//
// It implements all the hd44780 library i/o methods to control a Noritake
// CU-U Series VFD, in serial mode. Some CU-U boards are serial-only, some have
// a jumper that must be set to enable serial mode, and some do not support
// serial communication. See the application note for more information.
//
// This device has extended function set commands that are not directly
// supported by the library but can be used through the command() API
//
// The device uses a kind of 3-wire SPI for communication.
// Pinout:
// 1 VCC
// 2 SI/SO (DATA)
// 3 GND
// 4 STB (CS)
// 5 SCK (CLK)
// 6 NC
//
// The interface consists of sending 2 bytes serially for each hd44780
// data/cmd.
// The first byte is a control byte which sets the RS signal
// The second byte is the data.
//
// The application note for CU-U series boards is here:
// https://www.noritake-elec.com/includes/documents/brochure/CU-U_Application_Note.pdf
// Datasheets for specific boards, code samples, and more can be found here:
// https://www.noritake-elec.com/products/vfd-display-module/character-display/cu-u-series
//
//
// @author Bill Perry - bperrybap@opensource.billsworld.billandterrie.com
//
// ---------------------------------------------------------------------------
#ifndef hd44780_NTCUUserial_h
#define hd44780_NTCUUserial_h
class hd44780_NTCUUserial : public hd44780
{
public:
// ====================
// === constructors ===
// ====================
// no parameters: use h/w spi signals
// Note: This doesn't work for Leonardo since SS is not available and only drives an LED
hd44780_NTCUUserial() : _cs(SS), _clk(SCK), _data(MOSI) { }
// supply alternate chip/slave select
// will use h/w spi with alternate chip select
hd44780_NTCUUserial(int cs) : _cs(cs), _clk(SCK), _data(MOSI) { }
// supply pins for chip select, clock, and data
hd44780_NTCUUserial(uint8_t cs, uint8_t clk, uint8_t data) : _cs(cs), _clk(clk), _data(data) { }
private:
// ====================
// === private data ===
// ====================
// Arduino Pin information
const uint8_t _cs; // arduino pin for chip select
const uint8_t _clk; // arduino pin for clock
const uint8_t _data; // arduino pin for data i/o
// Control byte values
static const uint8_t CUU_startByte = 0xf8;
static const uint8_t CUU_RS = (1 << 1); // RS bit. set for data, clear for command
static const uint8_t CUU_RW = (1 << 2); // RW bit. set for read, clear for write
// ==================================================
// === hd44780 i/o subclass virtual i/o functions ===
// ==================================================
//
// ioinit() - Returns non zero if initialization failed.
int ioinit()
{
// App note says "Wait at least 260 ms after VCC > 4.75VDC"
// See also the delay in hd44780::begin()
// In practice little or no delay at all may work fine
delay(260);
// setup pins
digitalWrite(_cs, HIGH);
digitalWrite(_clk, HIGH);
digitalWrite(_data, HIGH);
pinMode(_cs, OUTPUT);
pinMode(_clk, OUTPUT);
pinMode(_data, OUTPUT);
// check to see if SPI has beginTransaction() and endTransaction()
// this was added in IDE 1.06 and so if using an IDE older than that,
// H/W spi will not be used
// This requires that <SPI.h> be included prior to this header
#if defined(SPI_HAS_TRANSACTION)
// check to see if h/w spi can be used
if((_data == MOSI) && (_clk == SCK))
SPI.begin();
#endif
/*
* Serial interface of this device is always 8 bit
*/
_displayfunction = HD44780_8BITMODE;
return(hd44780::RV_ENOERR); // all is good
}
// ioread(type) - read a byte from VFD DDRAM
//
// returns 8 bit value read
//
int ioread(hd44780::iotype type)
{
uint8_t data = 0;
if(type == hd44780::HD44780_IOdata)
{
data = deviceread(true);
}
else
{
data = deviceread(false);
}
return data;
}
//
// iowrite(type, value) - send either a command or data byte to lcd
//
// returns zero on success, non zero on failure
//
int iowrite(hd44780::iotype type, uint8_t value)
{
if(type == hd44780::HD44780_IOdata)
{
devicewrite(value, true);
}
else
{
devicewrite(value, false);
}
return(hd44780::RV_ENOERR);
}
// iosetBacklight() - set backlight brightness
// Since this display does not use a backlight but can control pixel intensity
// use the backlight brightness to set the pixel intensity.
//
// Note:
// The dimmer value on the CU-U Series is a 2 bit value
// 00 is 100%
// 01 is 75%
// 10 is 50%
// 11 is 25%
//
// Because of this, an API dimvalue of 0 does not turn off the display
// so the code must check for that and explicitly turn off the display if the
// API dimvalue is zero.
int iosetBacklight(uint8_t dimvalue)
{
int status = RV_ENOERR;
if(dimvalue)
{
// Data which follows the FunctionSet command is considered as brightness data.
command(HD44780_FUNCTIONSET | _displayfunction);
// the dim value is 0-255 so divide by 64 to get a value 0-3
if (_write(3 - dimvalue / 64) == 0)
status = RV_EIO;
display();
}
else
{
noDisplay();
}
return(status);
}
// ================================
// === internal class functions ===
// ================================
// deviceread() - send control and read data byte from the device
uint8_t deviceread(bool rs)
{
uint8_t data = CUU_startByte | CUU_RW | (CUU_RS * rs);
#if defined(SPI_HAS_TRANSACTION)
if((_data == MOSI) && (_clk == SCK))
SPI.end();
#endif
digitalWrite(_cs,LOW); // select device
digitalWrite(_clk, LOW);
shiftOut(_data, _clk, MSBFIRST, data);
pinMode(_data, INPUT);
delayMicroseconds(1);
digitalWrite(_clk, LOW);
data = shiftIn(_data, _clk, MSBFIRST);
digitalWrite(_cs,HIGH); // deselect device
pinMode(_data, OUTPUT);
delayMicroseconds(5);
#if defined(SPI_HAS_TRANSACTION)
if((_data == MOSI) && (_clk == SCK))
SPI.begin();
#endif
return data;
}
// devicewrite() - send control and data byte to the device
void devicewrite(uint8_t data, bool rs)
{
uint8_t startByte = CUU_startByte | (CUU_RS * rs);
// use h/w spi if we can
#if defined(SPI_HAS_TRANSACTION)
if((_data == MOSI) && (_clk == SCK))
{
// NOTE: App note says min 500ns clock cycle, so 2Mhz is max, 4Mhz seems to work.
SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE3));
digitalWrite(_cs, LOW); // select device
SPI.transfer(startByte);
SPI.transfer(data);
delayMicroseconds(1); // must delay at least 500ns before de-selecting
digitalWrite(_cs, HIGH); // deselect device
SPI.endTransaction();
}
else
#endif
{
digitalWrite(_cs, LOW); // select device
digitalWrite(_clk, LOW);
shiftOut(_data, _clk, MSBFIRST, startByte);
digitalWrite(_clk, LOW);
shiftOut(_data, _clk, MSBFIRST, data);
delayMicroseconds(1); // must delay at least 500ns before de-selecting
digitalWrite(_cs, HIGH); // deselect device
delayMicroseconds(5);
}
}
}; // end of class definition
#endif

View File

@@ -0,0 +1,538 @@
// vi:ts=4
// ---------------------------------------------------------------------------
// hd44780_pinIO.h - hd44780_pinIO i/o subclass for hd44780 library
// Copyright (c) 2016-2020 Bill Perry
// ---------------------------------------------------------------------------
//
// This file is part of the hd44780 library
//
// hd44780_pinIO is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 3 of the License.
//
// hd44780_pinIO is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with hd44780_pinIO.
// If not, see <http://www.gnu.org/licenses/>.
//
// ---------------------------------------------------------------------------
//
// It implements all the hd44780 library i/o methods to control an LCD based
// on the Hitachi HD44780 and compatible chipsets using direct Arduino
// pin connections.
//
// The API functionality provided by this library class is compatible
// with the functionality of the Arduino LiquidCrystal library.
//
//
// 2020.08.01 bperrybap - removed calls to analogWrite() on ESP32 core (not supported)
// 2019.08.11 bperrybap - fixed bug introduced by broken backlight check tweak
// 2016.12.26 bperrybap - tweak to broken backlight check code
// 2016.11.12 bperrybap - added code to safely handle broken backlight circuits
// 2016.09.08 bperrybap - changed param order of iowrite() to match ioread()
// 2016.08.06 bperrybap - added ioread()
// 2016.08.06 bperrybap - changed iosend() to iowrite()
// 2016.07.27 bperrybap - added return status for iosend()
// 2016.07.21 bperrybap - merged all class code into header
// 2016.07.20 bperrybap - merged into hd44780 library
// 2016.06.03 bperrybap - added smart execution delays
// 2016.04.01 bperrybap - initial creation
//
// @author Bill Perry - bperrybap@opensource.billsworld.billandterrie.com
// ---------------------------------------------------------------------------
#ifndef hd44780_pinIO_h
#define hd44780_pinIO_h
#include <hd44780.h>
// For STUPID versions of gcc that don't hard error on missing header files
#ifndef hd44780_h
#error Missing hd44780.h header file
#endif
#include <pins_arduino.h> // to get PWM detection macros
#define HIGHZ 0xfe // value is not critical but it cannot be the same as LOW or HIGH, or 0xff
class hd44780_pinIO : public hd44780
{
public:
// ====================
// === constructors ===
// ====================
// 4 bit mode constructor without r/w control, without backlight control
hd44780_pinIO(uint8_t rs, uint8_t en,
uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
_rs = rs;
_en = en;
_rw = 0xff; // not used
_d4 = d4;
_d5 = d5;
_d6 = d6;
_d7 = d7;
_bl = 0xff; // not used
_blLevel = 0xff; // not used
}
// 4 bit mode constructor without r/w control, with backlight control
hd44780_pinIO(uint8_t rs, uint8_t en,
uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7,
uint8_t bl, uint8_t blLevel)
{
_rs = rs;
_en = en;
_rw = 0xff; // not used
_d4 = d4;
_d5 = d5;
_d6 = d6;
_d7 = d7;
_bl = bl;
_blLevel = blLevel;
}
// 4 bit mode constructor with r/w control, without backlight control
hd44780_pinIO(uint8_t rs, uint8_t rw, uint8_t en,
uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
_rs = rs;
_en = en;
_rw = rw;
_d4 = d4;
_d5 = d5;
_d6 = d6;
_d7 = d7;
_bl = 0xff; // not used
_blLevel = 0xff; // not used
}
// 4 bit mode constructor with w/2 control, with backlight control
hd44780_pinIO(uint8_t rs, uint8_t rw, uint8_t en,
uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7,
uint8_t bl, uint8_t blLevel)
{
_rs = rs;
_en = en;
_rw = rw;
_d4 = d4;
_d5 = d5;
_d6 = d6;
_d7 = d7;
_bl = bl;
_blLevel = blLevel;
}
private:
// ====================
// === private data ===
// ====================
// Arduino Pin information
uint8_t _rs; // hd44780 rs arduino pin
uint8_t _rw; // hd44780 rw arduino pin
uint8_t _en; // hd44780 en arduino pin
uint8_t _d4; // hd44780 d4 arduino pin
uint8_t _d5; // hd44780 d4 arduino pin
uint8_t _d6; // hd44780 d4 arduino pin
uint8_t _d7; // hd44780 d4 arduino pin
uint8_t _bl; // arduino pin to control backlight
uint8_t _blLevel; // backlight active control level HIGH/LOW
// (HIGHZ is input mode for ON, LOW for off)
// ==================================================
// === hd44780 i/o subclass virtual i/o functions ===
// ==================================================
// ioinit() - initialize the h/w
int ioinit()
{
// initialize Arduino pins used for hd44780 signals
// NOTE:
// _rw and _en pins are set to low even though they should
// default to this from a an initial reset.
// the reason is it is possible initilization is called without
// without resetting the processor. In that case the state of the
// bit in the port register would be unknown, so it is safest to set
// to the desired state (LOW).
pinMode(_rs, OUTPUT);
if(_rw != 0xff)
{
pinMode(_rw, OUTPUT);
// if r/w is used it be will set to be low,
// it will only be turned hi during a read and
// then set back to low. This offers a slight boost
// for writes since normally reads are not done very often
digitalWrite(_rw, LOW);
}
pinMode(_en, OUTPUT);
digitalWrite(_en, LOW);
pinMode(_d4, OUTPUT);
pinMode(_d5, OUTPUT);
pinMode(_d6, OUTPUT);
pinMode(_d7, OUTPUT);
if(_bl != 0xff)
{
// check for broken backlight circuit on LCDkeypads
// and protect Arduino if it appears to be broken
// code will do "safe" backlight control
if((_blLevel == HIGH) && blPinTest(_bl))
_blLevel = HIGHZ;
pinMode(_bl, OUTPUT);
}
return(hd44780::RV_ENOERR); // all is good
}
// ioread(type) - read a byte from LCD DDRAM
//
// returns:
// success: 8 bit value read
// failure: negative value: reading not supported
//
int ioread(hd44780::iotype type)
{
uint8_t data = 0;
// check if r/w control supported
if(_rw == 0xff)
return(hd44780::RV_ENOTSUP);
waitReady(); // ensure previous instruction finished
// put all the LCD data pins into input mode.
pinMode(_d4, INPUT);
pinMode(_d5, INPUT);
pinMode(_d6, INPUT);
pinMode(_d7, INPUT);
// set RS based on type of read (data or status/cmd)
if(type == hd44780::HD44780_IOdata)
{
digitalWrite(_rs, HIGH); // RS high to access data reg
}
else
{
digitalWrite(_rs, LOW); // RS LOW to access status/cmd reg
}
// r/w HIGH for reading
digitalWrite(_rw, HIGH);
// raise E to allow reading the data.
digitalWrite(_en, HIGH);
// allow for hd44780 tDDR (Data delay time) before reading data
// this could be much shorter but this is portable for all CPUs.
// and it will ensure that hd44780 PWEH timing is honored as well.
delayMicroseconds(1);
// read pins for d4-d7 into upper nibble of byte
if(digitalRead(_d4) == HIGH)
data |= (1 << 4);
if(digitalRead(_d5) == HIGH)
data |= (1 << 5);
if(digitalRead(_d6) == HIGH)
data |= (1 << 6);
if(digitalRead(_d7) == HIGH)
data |= (1 << 7);
// lower E after reading nibble
digitalWrite(_en, LOW);
// allow for hd44780 1/2 of tcycE (Enable cycle time)
// this could be much shorter but this is portable for all CPUs.
delayMicroseconds(1);
// raise E to allow reading the lower nibbly of the byte
digitalWrite(_en, HIGH);
// allow for hd44780 tDDR (Data delay time) before reading data
// this could be shorter but this is portable for all CPUs.
// and it will ensure that hd44780 PWEH timing is honored as well.
delayMicroseconds(1);
// read pins for d4-d7 into upper nibble of byte
if(digitalRead(_d4) == HIGH)
data |= (1 << 0);
if(digitalRead(_d5) == HIGH)
data |= (1 << 1);
if(digitalRead(_d6) == HIGH)
data |= (1 << 2);
if(digitalRead(_d7) == HIGH)
data |= (1 << 3);
// lower E after reading nibble
digitalWrite(_en, LOW);
//
// put all pins back into state for writing to LCD
//
// put all the LCD data pins into input mode.
pinMode(_d4, OUTPUT);
pinMode(_d5, OUTPUT);
pinMode(_d6, OUTPUT);
pinMode(_d7, OUTPUT);
// r/w LOW for Writing
digitalWrite(_rw, LOW);
return(data);
}
// iowrite(type, value) - send either a command or data byte to lcd
// returns zero on success, non zero on failure
int iowrite(hd44780::iotype type, uint8_t value)
{
if(type == hd44780::HD44780_IOdata)
digitalWrite(_rs, HIGH);
else
digitalWrite(_rs, LOW);
// "4 bit commands" are special.
// They are used only during initalization and
// are used to reliably get the LCD and host in nibble sync
// with each other and to get the LCD into 4 bit mode.
// 8 bit host interfaces don't have to worry about this,
// but 4 bit host interfaces must only send the upper nibble.
// note:
// waitReady() is explicitly called as late as possible *after* the Arduino
// pins for the LCD control and data signals are set to allow
// overhead of the digitalWrite() calls to be hidden under execution time.
write4bits(value>>4); // setup uppper nibble d4-d7 lcd pins
waitReady(); // ensure previous instruction finished
pulseEnable(); // send upper nibble to LCD
// send lower nibble if not a 4 bit command
// (sends nibble for both data and "normal" commands)
if (type != hd44780::HD44780_IOcmd4bit )
{
write4bits((value & 0x0F));// setup lower nibble on d4-d7 lcd pins
pulseEnable(); // send lower nibble to LCD
}
return(hd44780::RV_ENOERR); // it never fails
}
// iosetBacklight() - set backlight brightness
// if dimming not supported, any non zero dimvalue turns backlight on
int iosetBacklight(uint8_t dimvalue)
{
if (_bl == 0xff )
return(hd44780::RV_ENOTSUP); // no backlight pin so nothing to do
// Check for HIGHZ active level which is used on LCDkeypads with broken backlight circuits
// In HIGHZ active level, the output is is never driven high, instead the pin is put
// into input mode to turn on the backlight.
// To turn off the backlight, the pin is flipped to output mode.
// because the pin is never set to HIGH, when the pin is set to output mode it will be LOW.
if(_blLevel == HIGHZ)
{
if(dimvalue)
pinMode(_bl, INPUT);
else
pinMode(_bl, OUTPUT);
return(hd44780::RV_ENOERR);
}
// Check if backlight pin has PWM support
// The reason this has to be done is that Arduino analogWrite()
// sets a pin to LOW if there is no PWM support and the PWM value is
// less than half of maximum. This is not desired behavior.
// The desired behavior is that if dimming is not supported, then the
// output is full on until the PWM value is zero to indicate "off".
// So we have to bypass the goofy code in analogWrite()
// by detecting if the pin supports PWM and controlling the output manually
// when no PWM is available.
// NOTE: the ESP32 core does not have analogWrite() PWM support !
// so on ESP32 core, all you get is on/off
#if !defined(ARDUINO_ARCH_ESP32)
#if defined(digitalPinHasPWM)
// Newer 1.5x Arduino has a macro to check for PWM capability on a pin
if(digitalPinHasPWM(_bl))
#elif defined(digitalPinToTimer)
// On 1.x Arduino have to check differently
if(digitalPinToTimer(_bl) != NOT_ON_TIMER)
#else
if(0) // no way to tell so assume no PWM
#endif
{
// set PWM signal appropriately for active level
if(_blLevel == HIGH)
{
analogWrite(_bl, dimvalue);
}
else
{
analogWrite(_bl, 255 - dimvalue); // active low is inverse PWM
}
}
// No PWM support on pin, so
// dimvalue 0 is off, any other value is on
else
#endif
if(((dimvalue) && (_blLevel == HIGH)) ||
((dimvalue == 0) && (_blLevel == LOW)))
{
digitalWrite(_bl, HIGH);
}
else
{
digitalWrite(_bl, LOW);
}
return(hd44780::RV_ENOERR);
}
// ================================
// === internal class functions ===
// ================================
// write4bits() - set the 4 hd44780 data lines
void write4bits(uint8_t value)
{
// write the bits on the LCD data lines but don't send the data
if(value & 1)
digitalWrite(_d4, HIGH);
else
digitalWrite(_d4, LOW);
if(value & 2)
digitalWrite(_d5, HIGH);
else
digitalWrite(_d5, LOW);
if(value & 4)
digitalWrite(_d6, HIGH);
else
digitalWrite(_d6, LOW);
if(value & 8)
digitalWrite(_d7, HIGH);
else
digitalWrite(_d7, LOW);
}
// pulseEnable() - toggle en to send data to hd44780 module
void pulseEnable(void)
{
digitalWrite(_en, HIGH);
#if defined (ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
// the extra delay here is not for the LCD, it is to allow signal lines time
// to settle when using 3v esp modules with 5v LCDs.
// 3v outputs on 5v inputs is already a bit out of spec and
// without this, the slew rate isn't fast enough to get "reliable"
// signal levels
// while it isn't needed for 3v LCDs, the time penalty isn't much
// to always do it.
delayMicroseconds(2); // enable pulse must be >450ns
#else
delayMicroseconds(1); // enable pulse must be >450ns
#endif
digitalWrite(_en, LOW);
}
//
// Function to test a backlight pin
// Returns non-zero if test fails (bad circuit design)
int blPinTest(int pin)
{
int val;
/*
* Check to see if there
* is a problem in the backlight circuit
* So far, the "broken" designs connected D10
* directly to the base of a NPN transistor,
* this will cause a short when D10 is set to HIGH
* as there is no current limiting resistor in the path
* between D10 to the base and the emitter to ground.
*/
/*
* Set the pin to an input with pullup disabled.
* This should be safe on all shields.
* The reason for the digitalWrite() first is that
* only the newer Arduino cores disable the pullup
* when setting the pin to INPUT.
* On boards that have a pullup on the transistor base,
* this should cause the backlight to be on.
*/
digitalWrite(pin, LOW);
pinMode(pin, INPUT);
/*
* Set the backlight pin to an output.
* since the pullup was turned off above by
* setting the pin to input mode,
* it should drive the pin LOW which should
* be safe given the known design flaw.
*/
pinMode(pin, OUTPUT);
/*
* Set the backlight pin to HIGH
* NOTE: This is NOT a safe thing to do
* on the broken designs. The code will minimize
* the time this is done to prevent any potential damage.
*/
digitalWrite(pin, HIGH);
/*
* Now read back the pin value to
* See if a short is pulling down the HIGH output.
*/
delayMicroseconds(5); // give some time for the signal to droop
val = digitalRead(pin); // read the level on the pin
/*
* Restore the pin to a safe state
* Input with pullup turned off
*/
digitalWrite(pin, LOW);
pinMode(pin, INPUT);
/*
* If the level read back is not HIGH
* Then there is a problem because the pin is
* being driven HIGH by the AVR.
*/
if (val != HIGH)
return(-1); // test failed
else
return(0); // all is ok.
}
}; // end of class definition
#endif