Created
January 23, 2020 21:11
-
-
Save smittytone/c0e92ef31ad957dece3634dd9c63941b to your computer and use it in GitHub Desktop.
Squirrel driver for 16 x 2 to 20 x 4 character LCDs driven by a Hitachi HD44780 controller
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class LCD { | |
// A Squirrel class to drive a 16 x 2 to 20 x 4 character LCD driven by a Hitachi HD44780 controller | |
// via an MCP23008 interface chip on an Adafruit I2C backpack [http://www.adafruit.com/product/292] | |
// Communicates with any imp I2C bus | |
// Written by Tony Smith (@smittytone) October 2014 | |
// Version 1.1 | |
static MCP23008_IODIR = "\x00"; | |
static MCP23008_GPIO = "\x09"; | |
// HD44780 Commands | |
static LCD_CLEARDISPLAY = 0x01; | |
static LCD_RETURNHOME = 0x02; | |
static LCD_ENTRYMODESET = 0x04; | |
static LCD_DISPLAYCONTROL = 0x08; | |
static LCD_CURSORSHIFT = 0x10; | |
static LCD_FUNCTIONSET = 0x20; | |
static LCD_SETCGRAMADDR = 0x40; | |
static LCD_SETDDRAMADDR = 0x80; | |
// Flags for display entry mode | |
static LCD_ENTRYRIGHT = 0x00; | |
static LCD_ENTRYLEFT = 0x02; | |
static LCD_ENTRYSHIFTINCREMENT = 0x01; | |
static LCD_ENTRYSHIFTDECREMENT = 0x00; | |
// Flags for display on/off control | |
static LCD_DISPLAYON = 0x04; | |
static LCD_DISPLAYOFF = 0x00; | |
static LCD_CURSORON = 0x02; | |
static LCD_CURSOROFF = 0x00; | |
static LCD_BLINKON = 0x01; | |
static LCD_BLINKOFF = 0x00; | |
static LCD_BACKLIGHT = 0x80; | |
// Flags for display/cursor shift | |
static LCD_DISPLAYMOVE = 0x08; | |
static LCD_CURSORMOVE = 0x00; | |
static LCD_MOVERIGHT = 0x04; | |
static LCD_MOVELEFT = 0x00; | |
// Flags for display mode | |
static LCD_8BITMODE = 0x10; | |
static LCD_4BITMODE = 0x00; | |
static LCD_2LINE = 0x08; | |
static LCD_1LINE = 0x00; | |
static LCD_5x10DOTS = 0x04; | |
static LCD_5x8DOTS = 0x00; | |
static HIGH = 1; | |
static LOW = 0; | |
_lcdWidth = 0; | |
_lcdHeight = 0; | |
_currentLine = 0; | |
_currentCol = 0; | |
_displayControl = 0; | |
_device = null; | |
_devAddress = 0; | |
constructor(impI2Cbus = null, mcp2008address = 0x20) { | |
// Constructor function takes chosen CONFIGURED imp I2C bus | |
// Note: I2C address is fixed | |
if (impI2Cbus == null) throw "LCD class requires valid I2C bus"; | |
_device = impI2Cbus; | |
_devAddress = mcp2008address << 1; | |
} | |
function init(chars = 16, rows = 2) { | |
// Initializes the display | |
// | |
// Parameters: | |
// 1. Integer value for the number of characters per line that the display supports (Default: 16) | |
// 2. Integer value for the number of lines that the display supports (Default: 2) | |
if (rows < 1) rows = 1; | |
if (chars < 1) chars = 1; | |
_lcdWidth = chars; | |
_lcdHeight = rows; | |
local displayFunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; | |
if (_lcdHeight > 1) displayFunction = displayFunction | LCD_2LINE; | |
delay(5); | |
_device.write(_devAddress, MCP23008_IODIR + "\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00"); | |
_device.write(_devAddress, MCP23008_IODIR + "\x00"); | |
// Must write the next two lines two further times each | |
_device.write(_devAddress, MCP23008_GPIO + "\x9C"); | |
_device.write(_devAddress, MCP23008_GPIO + "\x98"); | |
_device.write(_devAddress, MCP23008_GPIO + "\x9C"); | |
_device.write(_devAddress, MCP23008_GPIO + "\x98"); | |
_device.write(_devAddress, MCP23008_GPIO + "\x9C"); | |
_device.write(_devAddress, MCP23008_GPIO + "\x98"); | |
_device.write(_devAddress, MCP23008_GPIO + "\x94"); | |
_device.write(_devAddress, MCP23008_GPIO + "\x90"); | |
delay(5); | |
_sendCommand(LCD_FUNCTIONSET | displayFunction); | |
delay(5); | |
_sendCommand(LCD_FUNCTIONSET | displayFunction); | |
delay(5); | |
displayOn(); | |
clearScreen(); | |
_sendCommand(LCD_ENTRYMODESET | (LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT)); | |
setBacklight(HIGH); | |
} | |
function clearScreen() { | |
// Clear the display | |
_sendCommand(LCD_CLEARDISPLAY); | |
delay(2); | |
} | |
function displayOn() { | |
// Power up and activate the display | |
_displayControl = _displayControl | LCD_DISPLAYON; | |
_sendCommand(LCD_DISPLAYCONTROL | _displayControl); | |
} | |
function displayOff() { | |
// Power down the display | |
_displayControl = _displayControl | LCD_DISPLAYOFF; | |
_sendCommand(LCD_DISPLAYCONTROL | _displayControl); | |
} | |
function setBacklight(setting) { | |
// Set the backlight | |
// | |
// Parameters: | |
// 1. Boolean value indicating whether the backlight should be on or off | |
// | |
// Note: Backlight intensity is not supported by HD44780 | |
if (setting == HIGH || setting == true) { | |
_displayControl = _displayControl | 0x80; | |
_device.write(_devAddress, MCP23008_GPIO + "\x80"); | |
} else { | |
_displayControl = _displayControl & 0x7F; | |
_device.write(_devAddress, MCP23008_GPIO + "\x00"); | |
} | |
} | |
function setCursor(col = 0, row = 0) { | |
// Set the print cursor at the selected co-ordinates | |
// | |
// Parameters: | |
// 1. Integer for the column number | |
// 2. Integer for the row number | |
// | |
// Note: Column and row values begin at zero | |
local rowOffsets = [0x00, 0x40, 0x14, 0x54]; | |
if (row < 0) row = 0; | |
if (row >= _lcdHeight) row = _lcdHeight - 1; | |
if (col < 0) col = 0; | |
if (col >= _lcdWidth) col = _lcdWidth - 1; | |
_sendCommand(LCD_SETDDRAMADDR | (col + rowOffsets[row])); | |
_currentLine = col; | |
_currentCol = col; | |
} | |
function print(stringToPrint = "") { | |
// Print a string at the current cursor location | |
// | |
// Parameters: | |
// 1. String value to be printed | |
if (stringToPrint == "" || stringToPrint == null) return; | |
if (typeof stringToPrint != "string") return; | |
for (local i = 0 ; i < stringToPrint.len() ; ++i) { | |
_writeData(stringToPrint[i]); | |
} | |
} | |
function printChar(ascii = 65) { | |
// Print a string at the current cursor location | |
// | |
// Parameters: | |
// 1. Integer value for the Ascii code of the character to be printed | |
if (ascii < 0) ascii = 32; | |
if (ascii > 7 && ascii < 32) ascii = 32; | |
//if (ascii > 127) ascii = 32; | |
_writeData(ascii); | |
} | |
function centerText(stringToPrint) { | |
// Returns a string formatted to be centred on the display, ie. it pads with spaces | |
// so the string can be printed at column 0 | |
// | |
// Parameters: | |
// 1. String value to be printed | |
if (stringToPrint == "" || stringToPrint == null) return; | |
if (typeof stringToPrint != "string") return; | |
local inset = stringToPrint.len(); | |
if (inset > _lcdWidth) { | |
inset = inset - _lcdWidth; | |
inset = inset / 2; | |
stringToPrint = stringToPrint.slice(inset, inset + _lcdWidth); | |
} else if (inset < _lcdWidth) { | |
inset = _lcdWidth - inset; | |
inset = inset / 2; | |
local spaces = " "; | |
stringToPrint = spaces.slice(0, inset) + stringToPrint + spaces.slice(0, inset); | |
} | |
return stringToPrint; | |
} | |
function createCustomChar(charMatrix = [], asciiCode = 0) { | |
// Set one of the HD44780's eight custom 5 x 8 user-definable characters | |
if (asciiCode < 0 || asciiCode > 7) return; | |
if (charMatrix.len() == 0 || charMatrix == null) return; | |
_sendCommand(LCD_SETCGRAMADDR | (asciiCode << 3)); | |
for (local i = 0 ; i < 8 ; ++i) { | |
_writeData(charMatrix[i]); | |
} | |
} | |
function delay(value) { | |
// Blocking delay for ‘value’ milliseconds | |
local a = hardware.millis() + value; | |
while (hardware.millis() < a) {} | |
} | |
// PRIVATE FUNCTIONS - DO NOT CALL DIRECTLY | |
function _sendCommand(command) { | |
// Send a command to the HD77480 | |
_send(command, LOW); | |
} | |
function _writeData(dataByte) { | |
// Send data to the HD77480 | |
_send(dataByte, HIGH); | |
} | |
function _send(value, rsPinValue) { | |
// Generic send function to pass both a command or data byte (value) to the HD44780 | |
// We use the controller's 4-bit mode to send the data in two batches, each of which | |
// also contains the HD44780 pin settings we need. Each byte sent is packaged as follows: | |
// | |
// Register Select pin = bit 1 | |
// Enable pin = bit 2 | |
// Data pin 4 = bit 3 | |
// Data pin 5 = bit 4 | |
// Data pin 6 = bit 5 | |
// Data pin 7 = bit 6 | |
// Backlight = bit 7 | |
// | |
// The format is set by the backpack, which decodes the data and puts it onto the appropriate | |
// HD44780 lines | |
// Get the passed value's upper four bits and shift down to bits 6-3 (data pins) | |
// of the byte we will actually send | |
local buffer = (value & 0xF0) >> 1; | |
if (rsPinValue == HIGH) { | |
// Set the transmission byte's E + RS bits | |
buffer = buffer | 0x06; | |
} else { | |
// Set the transmission byte's E bit | |
buffer = buffer | 0x04; | |
} | |
// Set backlight bit if the backlight has been enabled in _displayControl | |
if (_displayControl & LCD_BACKLIGHT) { | |
buffer = buffer | 0x80; | |
} else { | |
buffer = buffer & 0x7F; | |
} | |
// Write the byte ot the backpack via I2C | |
_device.write(_devAddress, MCP23008_GPIO + buffer.tochar()); | |
// Clear the E bit and send the byte again | |
buffer = buffer & 0xFB; | |
_device.write(_devAddress, MCP23008_GPIO + buffer.tochar()); | |
// Get the passed value's lower four bits and shift down to bits 6-3 | |
buffer = (value & 0x0F) << 3; | |
if (rsPinValue == HIGH) { | |
// Set the transmission byte's E + RS bits | |
buffer = buffer | 0x06; | |
} else { | |
// Set the transmission byte's E bit | |
buffer = buffer | 0x04; | |
} | |
// Set backlight bit if the backlight has been enabled in _displayControl | |
if (_displayControl & LCD_BACKLIGHT) { | |
buffer = buffer | 0x80; | |
} else { | |
buffer = buffer & 0x7F; | |
} | |
// Write the byte ot the backpack via I2C | |
_device.write(_devAddress, MCP23008_GPIO + buffer.tochar()); | |
// clear the E bit and send again | |
buffer = buffer & 0xFB; | |
_device.write(_devAddress, MCP23008_GPIO + buffer.tochar()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment