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