Initial add

pull/91/head
George Sexton 1 year ago
parent ea55a05aa1
commit 75e8126551

@ -0,0 +1,56 @@
# Hitachi HD44780 Package
## Overview
The Hitachi HD44780 is an LCD driver chip. It's used in a variety of text LCD
displays. The datasheet is available here:
https://www.sparkfun.com/datasheets/LCD/HD44780.pdf
Generally, there are three kinds of displays that use this chip.
1. A raw display. This is a board with a row of pin connectors at the top. It's
made for interfacing directly to GPIO pins. This is the most complicated method
of using the LCD because you have to wire a minimum of 6 GPIO pins (4 data, 1
reset, 1 enable) and 2 power pins to make it work.
There are also complex initialization routines that have to be performed. This
is further complicated by having specific initialization calls depending upon
whether the device is connected to 4 data lines, or 8 data lines.
2. A backpack display. With this type of LCD display, an I2C, serial, or SPI
interface is provided. This type of display is easier because there are fewer
pins, but the complex intialization remains. Examples of backpack interfaces
include The Adafruit I2C/SPI backpack (MCP23008/74HC595D), the generic
LCDXXXX/PCF8547T backpack, etc.
3. The third and final variety is the "Intelligent" display. These displays have
a micro-controller that is connected to the LCD chip. Typically these intelligent
displays support multiple I/O methods and the micro-controller handles the
LCD initialization/communication.
Examples of this kind of display would be the SparkFun SerLCD display, the
MatrixOrbital LK2047T, the AdaFruit USB+Serial Backpack etc.
## Interfacing Notes
The driver package is designed to use the gpio.Group interface. This allows
the LCD driver to be agnostic about the physical connection between the display
and the host device. Any host/expander that supports the GPIO group can be used
to easily drive the LCD display.
## Hardware Notes
DO NOT attempt to source VCC for the unit backlight, or sink VCC to ground.
The backlight draws ~250ma of current which exceeds the current capability
of GPIO pins. It will permanently damage your device. If you would like to
control the backlight, connect a GPIO pin through a 1K Ohm resistor to a
transistor (2N2222 or equivalent).
## Troubleshooting
If nothing displays at all, check the contrast. Adjust the contrast control
until the 5x7 dot grid on the display is visible.
If the text is garbled, verify the gpio.Group is configured and the IO Pins
are connected to the right pins of the LCD display.

@ -0,0 +1,46 @@
// Copyright 2025 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package hd44780
import (
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/i2c"
"periph.io/x/devices/v3/mcp23xxx"
)
const (
// Name is the LCD pin name, and the integer value is the GPIO
// number (not physical) of the MCP23008 I2C GPIO Expander.
d4 = 3
d5 = 4
d6 = 5
d7 = 6
rsPin = 1
enablePin = 2
backlightPin = 7
)
// This function returns a display configured to use the Adafruit I2C/SPI LCD Backpack.
//
// # Product Information
//
// https://www.adafruit.com/product/292
//
// The I2C side of this backpack uses an MCP23008 I/O expander. This function
// creates an MCP23008 device with the required pin configuration. To use this,
// get an I2C bus, and call this function with the bus, i2c address, number of
// rows, and columns.
func NewAdafruitI2CBackpack(bus i2c.Bus, address uint16, rows, cols int) (*HD44780, error) {
mcp, err := mcp23xxx.NewI2C(bus, mcp23xxx.MCP23008, address)
if err != nil {
return nil, err
}
gr := *mcp.Group(0, []int{d4, d5, d6, d7, rsPin, enablePin, backlightPin})
grPins := gr.Pins()
reset := grPins[4].(gpio.PinOut)
enable := grPins[5].(gpio.PinOut)
bl := grPins[6].(gpio.PinOut)
return NewHD44780(gr, &reset, &enable, &bl, rows, cols)
}

@ -0,0 +1,225 @@
// Copyright 2018 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package hd44780
import (
"fmt"
"time"
"periph.io/x/conn/v3"
"periph.io/x/conn/v3/gpio"
)
// lineTwo offset for the second line in the LCD buffer.
const lineTwo = 0x40
// Deprecated: Use HD44780
type Dev struct {
// data pins
dataPins []gpio.PinOut
// register select pin
rsPin gpio.PinOut
// enable pin
enablePin gpio.PinOut
}
// Deprecated: Use NewHD44780()
// New creates and initializes the LCD device
//
// data - references to data pins
// rs - rs pin
// e - strobe pin
func New(data []gpio.PinOut, rs, e gpio.PinOut) (*Dev, error) {
if len(data) != 4 {
return nil, fmt.Errorf("expected 4 data pins, passed %d", len(data))
}
dev := &Dev{
dataPins: data,
enablePin: e,
rsPin: rs,
}
if err := dev.Reset(); err != nil {
return nil, err
}
return dev, nil
}
// Reset resets the HC-44780 chipset, clears the screen buffer and moves cursor to the
// home of screen (line 0, column 0).
func (r *Dev) Reset() error {
if err := r.clearBits(); err != nil {
return err
}
delayMs(15)
if err := r.rsPin.Out(gpio.Low); err != nil {
return err
}
if err := r.enablePin.Out(gpio.Low); err != nil {
return err
}
if err := r.bulkSendData(resetSequence, r.write4Bits); err != nil {
return err
}
return r.bulkSendData(initSequence, r.writeInstruction)
}
func (r *Dev) String() string {
return "HD44870, 4 bit mode"
}
// Halt clears the LCD screen
func (r *Dev) Halt() error {
if err := r.writeInstruction(0x01); err != nil {
return err
}
delayMs(2)
return nil
}
// SetCursor positions the cursor
//
// line - screen line, 0-based
// column - column, 0-based
func (r *Dev) SetCursor(line uint8, column uint8) error {
return r.writeInstruction(0x80 | (line*lineTwo + column))
}
// Print the data string
//
// data string to display
func (r *Dev) Print(data string) error {
for _, v := range []byte(data) {
if err := r.WriteChar(v); err != nil {
return err
}
}
return nil
}
// WriteChar writes a single byte (character) at the cursor position.
//
// data - character code
func (r *Dev) WriteChar(data uint8) error {
if err := r.sendData(); err != nil {
return err
}
if err := r.write4Bits(data >> 4); err != nil {
return err
}
if err := r.write4Bits(data); err != nil {
return err
}
delayUs(10)
return nil
}
// service methods
var resetSequence = [][]uint{
{0x03, 50}, // init 1-st cycle
{0x03, 10}, // init 2-nd cycle
{0x03, 10}, // init 3-rd cycle
{0x02, 10}, // init finish
}
var initSequence = [][]uint{
{0x14, 0}, // 4-bit mode, 2 lines, 5x7 chars high
{0x10, 0}, // disable display
{0x01, 2000}, // clear screen
{0x06, 0}, // cursor shift right, no display move
{0x0c, 0}, // enable display no cursor
{0x01, 2000}, // clear screen
{0x02, 2000}, // cursor home
}
func (r *Dev) bulkSendData(seq [][]uint, f func(_data uint8) error) error {
for _, v := range seq {
if err := f(uint8(v[0])); err != nil {
return err
}
if v[1] > 0 {
delayUs(v[1])
}
}
return nil
}
func (r *Dev) clearBits() error {
for _, v := range r.dataPins {
if err := v.Out(gpio.Low); err != nil {
return err
}
}
return nil
}
func (r *Dev) write4Bits(data uint8) error {
for i, v := range r.dataPins {
if data&(1<<uint(i)) > 0 {
if err := v.Out(gpio.High); err != nil {
return err
}
} else {
if err := v.Out(gpio.Low); err != nil {
return err
}
}
}
return r.strobe()
}
func (r *Dev) sendInstruction() error {
if err := r.rsPin.Out(gpio.Low); err != nil {
return err
}
return r.enablePin.Out(gpio.Low)
}
func (r *Dev) sendData() error {
if err := r.rsPin.Out(gpio.High); err != nil {
return err
}
return r.enablePin.Out(gpio.Low)
}
func (r *Dev) writeInstruction(data uint8) error {
if err := r.sendInstruction(); err != nil {
return err
}
// write high 4 bits
if err := r.write4Bits(data >> 4); err != nil {
return err
}
// write low bits
if err := r.write4Bits(data); err != nil {
return err
}
delayUs(50)
return nil
}
func (r *Dev) strobe() error {
if err := r.enablePin.Out(gpio.High); err != nil {
return err
}
delayUs(2)
return r.enablePin.Out(gpio.Low)
}
func delayUs(ms uint) {
time.Sleep(time.Duration(ms) * time.Microsecond)
}
func delayMs(ms int) {
time.Sleep(time.Duration(ms) * time.Millisecond)
}
var _ conn.Resource = &Dev{}

@ -0,0 +1,70 @@
// Copyright 2025 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package hd44780_test
import (
"errors"
"fmt"
"log"
"time"
"periph.io/x/conn/v3/display"
"periph.io/x/conn/v3/display/displaytest"
"periph.io/x/conn/v3/gpio"
"periph.io/x/devices/v3/hd44780"
"periph.io/x/host/v3"
"periph.io/x/host/v3/gpioioctl"
)
// This example shows using a gpio.Group with an HD44780 display. For this
// example its using the periph.io/x/host/gpioioctl package to obtain
// the gpio.Group and pins. You can use an I/O device that implements
// gpio.Group and gpio.PinOut to drive a display.
func Example() {
var err error
if _, err = host.Init(); err != nil {
log.Fatal(err)
}
chip := gpioioctl.Chips[0]
var ls gpio.Group
// Using a group to obtain the pins. The first 4 pins in the group are the
// data pins, and the remaining ones are reset, enable, and backlight.
// For 8 bit mode, specify additional data pins.
ls, err = chip.LineSet(gpioioctl.LineOutput, gpio.NoEdge, gpio.PullNoChange,
"GPIO27", "GPIO22", "GPIO23", "GPIO24", "GPIO17", "GPIO18", "GPIO25")
if err != nil {
log.Fatal(err)
}
pins := ls.Pins()
reset := pins[4].(gpio.PinOut)
enable := pins[5].(gpio.PinOut)
bl := pins[6].(gpio.PinOut)
lcd, err := hd44780.NewHD44780(ls, &reset, &enable, &bl, 2, 16)
if err != nil {
log.Fatal(err)
}
n, err := lcd.WriteString("Hello")
time.Sleep(5 * time.Second)
fmt.Printf("n=%d, err=%s\n", n, err)
fmt.Println("lcd=", lcd.String())
lcd.Home()
lcd.MoveTo(1, 1)
lcd.WriteString("Line 1")
lcd.MoveTo(2, 2)
lcd.WriteString("Line 2")
time.Sleep(5 * time.Second)
lcd.Clear()
fmt.Println("calling TestTextDisplay")
errs := displaytest.TestTextDisplay(lcd, true)
fmt.Println("back from TestTextDisplay")
for _, e := range errs {
if !errors.Is(e, display.ErrNotImplemented) {
log.Println(e)
}
}
}

@ -1,4 +1,4 @@
// Copyright 2018 The Periph Authors. All rights reserved.
// Copyright 2025 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
@ -14,216 +14,409 @@ import (
"time"
"periph.io/x/conn/v3"
"periph.io/x/conn/v3/display"
"periph.io/x/conn/v3/gpio"
)
// lineTwo offset for the second line in the LCD buffer.
const lineTwo = 0x40
type writeMode bool
// Dev is the 4-bit addressing device for HD-44780
type Dev struct {
// data pins
dataPins []gpio.PinOut
type ifMode byte
// register select pin
rsPin gpio.PinOut
const (
modeCommand writeMode = false
modeData writeMode = true
// enable pin
enablePin gpio.PinOut
}
cmdByte byte = 0xfe
mode4Bit ifMode = 0x04
mode8Bit ifMode = 0x08
)
// New creates and initializes the LCD device
// HD44780 is an implementation that supports writing to LCD displays using a
// gpio.Group for the data pins, and discrete pins for the reset, enable, and
// and backlight pins.
//
// data - references to data pins
// rs - rs pin
// e - strobe pin
func New(data []gpio.PinOut, rs, e gpio.PinOut) (*Dev, error) {
if len(data) != 4 {
return nil, fmt.Errorf("expected 4 data pins, passed %d", len(data))
}
dev := &Dev{
dataPins: data,
enablePin: e,
rsPin: rs,
}
if err := dev.Reset(); err != nil {
return nil, err
}
return dev, nil
// Implements periph.io/conn/x/display/TextDisplay and display.DisplayBacklight
type HD44780 struct {
dataPins gpio.Group
resetPin gpio.PinOut
enablePin gpio.PinOut
backlightPin gpio.PinOut
mode ifMode
rows int
cols int
on bool
cursor bool
blink bool
lastWrite int64
}
// Reset resets the HC-44780 chipset, clears the screen buffer and moves cursor to the
// home of screen (line 0, column 0).
func (r *Dev) Reset() error {
if err := r.clearBits(); err != nil {
return err
}
const (
delayCommand time.Duration = 2000
delayCharacter time.Duration = 200
)
delayMs(15)
var rowConstants = [][]byte{{0, 0, 64}, {0, 0, 64, 20, 84}}
var clearScreen = []byte{cmdByte, 0x01}
var goHome = []byte{cmdByte, 0x02}
var setCursorPosition = []byte{cmdByte, 0x80}
if err := r.rsPin.Out(gpio.Low); err != nil {
return err
}
if err := r.enablePin.Out(gpio.Low); err != nil {
return err
// Return the row offset value
func getRowConstant(row, maxcols int) byte {
var offset int
if maxcols != 16 {
offset = 1
}
return rowConstants[offset][row]
}
if err := r.bulkSendData(resetSequence, r.write4Bits); err != nil {
return err
// NewHD44780 takes a GPIO group, and gpio.PinOut for reset, enable, and
// backlight. It returns a the HD44780 device in an initialized state and
// ready for use.
//
// The first 4 or 8 pins of the data group must be connected to the data lines
// To use 4 bit mode, you would connect lines D4-D7 on the display, and for
// 8 bit mode, D0-D7. If dataPinGroup is 8 or more pins, then it's assumed the display is
// connected using all 8 pins.
func NewHD44780(
dataPinGroup gpio.Group,
resetPin, enablePin, backlightPin *gpio.PinOut,
rows, cols int) (*HD44780, error) {
mode := mode4Bit
if len(dataPinGroup.Pins()) >= 8 {
mode = mode8Bit
}
return r.bulkSendData(initSequence, r.writeInstruction)
display := &HD44780{
dataPins: dataPinGroup,
resetPin: *resetPin,
enablePin: *enablePin,
backlightPin: *backlightPin,
mode: mode,
rows: rows,
cols: cols,
on: true,
}
return display, display.init()
}
func (r *Dev) String() string {
return "HD44870, 4 bit mode"
// Not supported by this device. Returns display.ErrNotImplemented
func (lcd *HD44780) AutoScroll(enabled bool) error {
// TODO: Wrap
return display.ErrNotImplemented
}
// Halt clears the LCD screen
func (r *Dev) Halt() error {
if err := r.writeInstruction(0x01); err != nil {
return err
}
delayMs(2)
return nil
// Clears the screen and moves the cursor to the first position.
func (lcd *HD44780) Clear() error {
_, err := lcd.Write(clearScreen)
return err
}
// SetCursor positions the cursor
//
// line - screen line, 0-based
// column - column, 0-based
func (r *Dev) SetCursor(line uint8, column uint8) error {
return r.writeInstruction(0x80 | (line*lineTwo + column))
// Return the number of columns the display supports
func (lcd *HD44780) Cols() int {
return lcd.cols
}
// Print the data string
//
// data string to display
func (r *Dev) Print(data string) error {
for _, v := range []byte(data) {
if err := r.WriteChar(v); err != nil {
return err
// Set the cursor mode. You can pass multiple arguments.
// Cursor(CursorOff, CursorUnderline)
func (lcd *HD44780) Cursor(modes ...display.CursorMode) (err error) {
var val = byte(0x08)
if lcd.on {
val |= 0x04
}
for _, mode := range modes {
switch mode {
case display.CursorOff:
// lcd.Write(underlineCursorOff)
lcd.blink = false
lcd.cursor = false
case display.CursorBlink:
lcd.blink = true
lcd.cursor = true
val |= 0x01
case display.CursorUnderline:
lcd.cursor = true
lcd.blink = true
// lcd.Write(underlineCursorOn)
val |= 0x02
case display.CursorBlock:
lcd.cursor = true
lcd.blink = true
val |= 0x01
default:
err = fmt.Errorf("HD44780 - unexpected cursor: %d", mode)
return
}
}
return nil
_, err = lcd.Write([]byte{cmdByte, val & 0x0f})
return err
}
// WriteChar writes a single byte (character) at the cursor position.
//
// data - character code
func (r *Dev) WriteChar(data uint8) error {
if err := r.sendData(); err != nil {
return err
}
if err := r.write4Bits(data >> 4); err != nil {
return err
}
if err := r.write4Bits(data); err != nil {
return err
}
delayUs(10)
return nil
// Move the cursor home (MinRow(),MinCol())
func (lcd *HD44780) Home() (err error) {
_, err = lcd.Write(goHome)
return err
}
// service methods
// Return the min column position.
func (lcd *HD44780) MinCol() int {
return 1
}
var resetSequence = [][]uint{
{0x03, 50}, // init 1-st cycle
{0x03, 10}, // init 2-nd cycle
{0x03, 10}, // init 3-rd cycle
{0x02, 10}, // init finish
// Return the min row position.
func (lcd *HD44780) MinRow() int {
return 1
}
var initSequence = [][]uint{
{0x14, 0}, // 4-bit mode, 2 lines, 5x7 chars high
{0x10, 0}, // disable display
{0x01, 2000}, // clear screen
{0x06, 0}, // cursor shift right, no display move
{0x0c, 0}, // enable display no cursor
{0x01, 2000}, // clear screen
{0x02, 2000}, // cursor home
// Move the cursor forward or backward.
func (lcd *HD44780) Move(dir display.CursorDirection) (err error) {
var val byte = 0x10
switch dir {
case display.Backward:
case display.Forward:
val |= 0x04
case display.Down, display.Up:
fallthrough
default:
err = fmt.Errorf("hd44780: %w", display.ErrNotImplemented)
return
}
_, err = lcd.Write([]byte{cmdByte, val})
return
}
func (r *Dev) bulkSendData(seq [][]uint, f func(_data uint8) error) error {
for _, v := range seq {
if err := f(uint8(v[0])); err != nil {
return err
}
if v[1] > 0 {
delayUs(v[1])
}
// Move the cursor to arbitrary position.
func (lcd *HD44780) MoveTo(row, col int) (err error) {
if row < lcd.MinRow() || row > lcd.rows || col < lcd.MinCol() || col > lcd.cols {
err = fmt.Errorf("HD44780.MoveTo(%d,%d) value out of range.", row, col)
return
}
return nil
var cmd = []byte{cmdByte, setCursorPosition[1]}
cmd[1] |= getRowConstant(row, lcd.cols) + byte(col-1)
_, err = lcd.Write(cmd)
return
}
func (r *Dev) clearBits() error {
for _, v := range r.dataPins {
if err := v.Out(gpio.Low); err != nil {
return err
}
// Return the number of rows the display supports.
func (lcd *HD44780) Rows() int {
return lcd.rows
}
// Return info about the dsiplay.
func (lcd *HD44780) String() string {
return fmt.Sprintf("HD44780::%s - Rows: %d, Cols: %d", lcd.dataPins.String(), lcd.rows, lcd.cols)
}
// Turn the display on / off
func (lcd *HD44780) Display(on bool) error {
lcd.on = on
val := byte(0x08)
if on {
val |= 0x04
}
if lcd.blink {
val |= 0x01
}
return nil
if lcd.cursor {
val |= 0x02
}
_, err := lcd.Write([]byte{cmdByte, val})
return err
}
func (r *Dev) write4Bits(data uint8) error {
for i, v := range r.dataPins {
if data&(1<<uint(i)) > 0 {
if err := v.Out(gpio.High); err != nil {
return err
// Write a set of bytes to the display.
func (lcd *HD44780) Write(p []byte) (n int, err error) {
if len(p) == 0 {
return
}
if p[0] == cmdByte {
n = len(p) - 1
err = lcd.sendCommand(p[1:])
return
}
lcd.delayWrite(delayCommand)
err = lcd.resetPin.Out(gpio.Level(modeData))
if err != nil {
return
}
for _, byteVal := range p {
lcd.lastWrite = time.Now().UnixMicro()
if lcd.mode == mode4Bit {
err = lcd.write4Bits(byteVal >> 4)
if err == nil {
err = lcd.write4Bits(byteVal & 0x0f)
}
} else {
if err := v.Out(gpio.Low); err != nil {
return err
}
err = lcd.write8Bits(byteVal)
}
if err != nil {
return
}
n += 1
time.Sleep(delayCharacter * time.Microsecond)
}
return r.strobe()
lcd.lastWrite = time.Now().UnixMicro()
return
}
// Write a string output to the display.
func (lcd *HD44780) WriteString(text string) (int, error) {
return lcd.Write([]byte(text))
}
// Halt clears the display, turns the backlight off, and turns the display off.
// Halt() is called for the data pins gpio.Group.
func (lcd *HD44780) Halt() error {
_ = lcd.Clear()
_ = lcd.Backlight(0)
_ = lcd.Display(false)
return lcd.dataPins.Halt()
}
func (r *Dev) sendInstruction() error {
if err := r.rsPin.Out(gpio.Low); err != nil {
// Turn the display's backlight on or off. You must supply a backlight control
// pin when creating the display to use this.
func (lcd *HD44780) Backlight(intensity display.Intensity) error {
on := (intensity > 0)
err := lcd.Display(on)
if err != nil {
return err
}
return r.enablePin.Out(gpio.Low)
if lcd.backlightPin != nil {
err = lcd.backlightPin.Out(gpio.Level(on))
}
return err
}
func (r *Dev) sendData() error {
if err := r.rsPin.Out(gpio.High); err != nil {
return err
// delayWrite looks at the time of the last LCD write and if
// the specified microseconds period has not elapsed, it
// invokes time.Sleep() with the difference.
//
// Some I/O methods, like direct GPIO on a Pi are very fast, while other methods
// like i2c take longer. Without delays, on very fast I/O paths, the LCD will
// display garbage. The correct way to handle this would be to read the Busy flag
// on the LCD display. However, some backpacks don't have the capability to
// check the Busy flag because the R/W pin isn't connected. So, we can't correctly
// handle io delays. This handles the very fast interfaces, while not
// penalizing the slower ones with unnecessary delays.
//
// The value of lcd.lastWrite is updated to the current time by the call.
func (lcd *HD44780) delayWrite(microseconds time.Duration) {
diff := microseconds - time.Duration(time.Now().UnixMicro()-lcd.lastWrite)
if diff > 0 {
time.Sleep(time.Duration(diff) * time.Microsecond)
}
return r.enablePin.Out(gpio.Low)
lcd.lastWrite = time.Now().UnixMicro()
}
func (r *Dev) writeInstruction(data uint8) error {
if err := r.sendInstruction(); err != nil {
return err
// Init the display. The HD44780 has a fairly complex initialization cycle
// with variations for 4 and 8 pin mode.
func (lcd *HD44780) init() error {
/*
This is the startup sequence for the Hitachi HD44780U chip as
documented in the Datasheet.
*/
lcd.lastWrite = time.Now().UnixMicro()
if lcd.mode == mode4Bit {
var lineMode byte = 0x20
if lcd.rows > 1 {
lineMode |= 0x08
}
err := lcd.resetPin.Out(gpio.Level(modeCommand))
if err != nil {
return err
}
err = lcd.enablePin.Out(gpio.Low)
if err != nil {
return err
}
err = lcd.write4Bits(0x03)
if err != nil {
return err
}
time.Sleep(4100 * time.Microsecond)
_ = lcd.write4Bits(0x03)
_ = lcd.write4Bits(0x03)
_ = lcd.write4Bits(0x02)
_ = lcd.sendCommand([]byte{lineMode})
} else {
// Init the display for 8 pin operation.
lineMode := byte(0x30) // Set the line mode and interface to 8 bits
if lcd.rows > 1 {
lineMode |= 0x08
}
err := lcd.resetPin.Out(gpio.Level(modeCommand))
if err != nil {
return err
}
err = lcd.enablePin.Out(gpio.Low)
if err != nil {
return err
}
_ = lcd.write8Bits(0x03 << 4) // Get it's attention
time.Sleep(4100 * time.Microsecond)
_ = lcd.write8Bits(0x03 << 4)
_ = lcd.write8Bits(0x03 << 4)
_ = lcd.write8Bits(lineMode)
_ = lcd.write8Bits(0x4) // set entry mode
}
// write high 4 bits
if err := r.write4Bits(data >> 4); err != nil {
_ = lcd.Cursor(display.CursorOff)
_ = lcd.Display(true)
_ = lcd.Clear()
_ = lcd.Home()
return lcd.Backlight(0xff)
}
func (lcd *HD44780) sendCommand(commands []byte) error {
lcd.delayWrite(delayCommand)
err := lcd.resetPin.Out(gpio.Level(modeCommand))
if err != nil {
return err
}
// write low bits
if err := r.write4Bits(data); err != nil {
return err
for _, command := range commands {
if lcd.mode == mode4Bit {
err = lcd.write4Bits(byte(command >> 4))
if err == nil {
err = lcd.write4Bits(byte(command))
}
} else {
err = lcd.write8Bits(command)
}
if err != nil {
break
}
}
delayUs(50)
return nil
lcd.lastWrite = time.Now().UnixMicro()
return err
}
func (r *Dev) strobe() error {
if err := r.enablePin.Out(gpio.High); err != nil {
return err
}
delayUs(2)
return r.enablePin.Out(gpio.Low)
func (lcd *HD44780) write4Bits(value byte) error {
return lcd.writeBits(gpio.GPIOValue(value), 0x0f)
}
func delayUs(ms uint) {
time.Sleep(time.Duration(ms) * time.Microsecond)
func (lcd *HD44780) write8Bits(value byte) error {
return lcd.writeBits(gpio.GPIOValue(value), 0xff)
}
func delayMs(ms int) {
time.Sleep(time.Duration(ms) * time.Millisecond)
func (lcd *HD44780) writeBits(value, mask gpio.GPIOValue) error {
err := lcd.dataPins.Out(value, mask)
if err != nil {
return err
}
err = lcd.enablePin.Out(gpio.High)
if err == nil {
time.Sleep(2 * time.Microsecond)
err = lcd.enablePin.Out(gpio.Low)
}
return err
}
var _ conn.Resource = &Dev{}
var _ display.TextDisplay = &HD44780{}
var _ display.DisplayBacklight = &HD44780{}
var _ conn.Resource = &HD44780{}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,50 @@
// Copyright 2025 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package hd44780
import (
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/i2c"
"periph.io/x/devices/v3/pcf857x"
)
const (
// Name is the LCD pin name, and the integer value is the GPIO
// number (not physical) of the PCF8574 I2C GPIO Expander.
pcf_d4 = 4
pcf_d5 = 5
pcf_d6 = 6
pcf_d7 = 7
pcf_rsPin = 0
pcf_enablePin = 2
pcf_backlightPin = 3
pcf_rwPin = 1
)
// This function returns a display configured to use the pcf8574 i2c backpacks.
//
// # Product Information
//
// https://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf
//
// This function creates a PCF8574 backpack device with the required pin
// configuration. To use this, get an I2C bus, and call this function with the
// bus, i2c address, number of rows, and columns.
func NewPCF857xBackpack(bus i2c.Bus, address uint16, rows, cols int) (*HD44780, error) {
pcf, err := pcf857x.New(bus, address, pcf857x.PCF8574)
if err != nil {
return nil, err
}
// R/W is connected on this backpack. Set it to low.
_ = pcf.Pins[pcf_rwPin].Out(gpio.Low)
// Create our gpio.Group
gr, _ := pcf.Group(pcf_d4, pcf_d5, pcf_d6, pcf_d7, pcf_rsPin, pcf_enablePin, pcf_backlightPin)
grPins := gr.Pins()
reset := grPins[4].(gpio.PinOut)
enable := grPins[5].(gpio.PinOut)
bl := grPins[6].(gpio.PinOut)
return NewHD44780(gr, &reset, &enable, &bl, rows, cols)
}
Loading…
Cancel
Save