mirror of https://github.com/periph/devices
Initial add
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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…
Reference in New Issue