mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-15 20:33:39 +00:00
Add Hd44780i2c driver (#173)
* hd44780i2c: add support for hd44780i2c LCD display.
This commit is contained in:
@@ -57,6 +57,8 @@ smoke-test:
|
|||||||
@md5sum ./build/test.hex
|
@md5sum ./build/test.hex
|
||||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hd44780/text/main.go
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hd44780/text/main.go
|
||||||
@md5sum ./build/test.hex
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/hd44780i2c/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hub75/main.go
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hub75/main.go
|
||||||
@md5sum ./build/test.hex
|
@md5sum ./build/test.hex
|
||||||
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/basic
|
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/basic
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers/hd44780i2c"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// Note: most HD44780 LCD modules requires 5V power, however some variations
|
||||||
|
// use 3.3V (and may be damaged by 5V).
|
||||||
|
|
||||||
|
machine.I2C0.Configure(machine.I2CConfig{
|
||||||
|
Frequency: machine.TWI_FREQ_400KHZ,
|
||||||
|
})
|
||||||
|
|
||||||
|
lcd := hd44780i2c.New(machine.I2C0, 0x27) // some modules have address 0x3F
|
||||||
|
|
||||||
|
lcd.Configure(hd44780i2c.Config{
|
||||||
|
Width: 16, // required
|
||||||
|
Height: 2, // required
|
||||||
|
CursorOn: true,
|
||||||
|
CursorBlink: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
lcd.Print([]byte(" TinyGo\n LCD Test "))
|
||||||
|
|
||||||
|
// CGRAM address 0x0-0x7 can be used to store 8 custom characters
|
||||||
|
lcd.CreateCharacter(0x0, []byte{0x00, 0x11, 0x0E, 0x1F, 0x15, 0x1F, 0x1F, 0x1F})
|
||||||
|
lcd.Print([]byte{0x0})
|
||||||
|
|
||||||
|
// You can use https://maxpromer.github.io/LCD-Character-Creator/
|
||||||
|
// to crete your own characters.
|
||||||
|
|
||||||
|
time.Sleep(time.Millisecond * 7000)
|
||||||
|
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
lcd.BacklightOn(false)
|
||||||
|
time.Sleep(time.Millisecond * 250)
|
||||||
|
lcd.BacklightOn(true)
|
||||||
|
time.Sleep(time.Millisecond * 250)
|
||||||
|
}
|
||||||
|
|
||||||
|
lcd.CursorOn(false)
|
||||||
|
lcd.CursorBlink(false)
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for {
|
||||||
|
|
||||||
|
lcd.ClearDisplay()
|
||||||
|
lcd.SetCursor(2, 1)
|
||||||
|
lcd.Print([]byte(strconv.FormatInt(int64(i), 10)))
|
||||||
|
i++
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
// Package hd44780i2c implements a driver for the Hitachi HD44780 LCD display module
|
||||||
|
// with an I2C adapter.
|
||||||
|
//
|
||||||
|
// Datasheet: https://www.sparkfun.com/datasheets/LCD/HD44780.pdf
|
||||||
|
//
|
||||||
|
package hd44780i2c
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Device wraps an I2C connection to a HD44780 I2C LCD with related data.
|
||||||
|
type Device struct {
|
||||||
|
bus machine.I2C
|
||||||
|
addr uint8
|
||||||
|
width uint8
|
||||||
|
height uint8
|
||||||
|
cursor cursor
|
||||||
|
backlight uint8
|
||||||
|
displayfunction uint8
|
||||||
|
displaycontrol uint8
|
||||||
|
displaymode uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type cursor struct {
|
||||||
|
x, y uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config for HD44780 I2C LCD.
|
||||||
|
type Config struct {
|
||||||
|
Width uint8
|
||||||
|
Height uint8
|
||||||
|
Font uint8
|
||||||
|
CursorOn bool
|
||||||
|
CursorBlink bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new HD44780 I2C LCD connection. The I2C bus must already be
|
||||||
|
// configured.
|
||||||
|
//
|
||||||
|
// This function only creates the Device object, it does not touch the device.
|
||||||
|
func New(bus machine.I2C, addr uint8) Device {
|
||||||
|
if addr == 0 {
|
||||||
|
addr = 0x27
|
||||||
|
}
|
||||||
|
return Device{
|
||||||
|
bus: bus,
|
||||||
|
addr: addr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure sets up the display. Display itself and backlight is default on.
|
||||||
|
func (d *Device) Configure(cfg Config) error {
|
||||||
|
|
||||||
|
if cfg.Width == 0 || cfg.Height == 0 {
|
||||||
|
return errors.New("width and height must be set")
|
||||||
|
}
|
||||||
|
d.width = uint8(cfg.Width)
|
||||||
|
d.height = uint8(cfg.Height)
|
||||||
|
|
||||||
|
delayms(50)
|
||||||
|
|
||||||
|
d.backlight = BACKLIGHT_ON
|
||||||
|
d.expanderWrite(0)
|
||||||
|
delayms(1000)
|
||||||
|
|
||||||
|
d.write4bits(0x03 << 4)
|
||||||
|
delayus(4500)
|
||||||
|
d.write4bits(0x03 << 4)
|
||||||
|
delayus(4500)
|
||||||
|
d.write4bits(0x03 << 4)
|
||||||
|
delayus(150)
|
||||||
|
d.write4bits(0x02 << 4)
|
||||||
|
|
||||||
|
d.displayfunction = DATA_LENGTH_4BIT | ONE_LINE | FONT_5X8
|
||||||
|
if d.height > 1 {
|
||||||
|
d.displayfunction |= TWO_LINE
|
||||||
|
}
|
||||||
|
if cfg.Font != 0 && d.height == 1 {
|
||||||
|
d.displayfunction |= FONT_5X10
|
||||||
|
}
|
||||||
|
d.sendCommand(FUNCTION_MODE | d.displayfunction)
|
||||||
|
|
||||||
|
d.displaycontrol = DISPLAY_ON | CURSOR_OFF | CURSOR_BLINK_OFF
|
||||||
|
if cfg.CursorOn {
|
||||||
|
d.displaycontrol |= CURSOR_ON
|
||||||
|
}
|
||||||
|
if cfg.CursorBlink {
|
||||||
|
d.displaycontrol |= CURSOR_BLINK_ON
|
||||||
|
}
|
||||||
|
d.sendCommand(DISPLAY_ON_OFF | d.displaycontrol)
|
||||||
|
d.ClearDisplay()
|
||||||
|
|
||||||
|
d.displaymode = CURSOR_INCREASE | DISPLAY_NO_SHIFT
|
||||||
|
d.sendCommand(ENTRY_MODE | d.displaymode)
|
||||||
|
d.Home()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearDisplay clears all texts on the display.
|
||||||
|
func (d *Device) ClearDisplay() {
|
||||||
|
d.sendCommand(DISPLAY_CLEAR)
|
||||||
|
d.cursor.x = 0
|
||||||
|
d.cursor.y = 0
|
||||||
|
delayus(2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Home sets the cursor back to position (0, 0).
|
||||||
|
func (d *Device) Home() {
|
||||||
|
d.sendCommand(CURSOR_HOME)
|
||||||
|
d.cursor.x = 0
|
||||||
|
d.cursor.y = 0
|
||||||
|
delayus(2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCursor sets the cursor to a specific position (x, y).
|
||||||
|
//
|
||||||
|
// if y (row) is set larger than actual rows, it would be set to 0.
|
||||||
|
func (d *Device) SetCursor(x, y uint8) {
|
||||||
|
rowOffset := []uint8{0x0, 0x40, 0x14, 0x54}
|
||||||
|
if y > (d.height - 1) {
|
||||||
|
y = 0
|
||||||
|
}
|
||||||
|
d.cursor.x = x
|
||||||
|
d.cursor.y = y
|
||||||
|
d.sendCommand(DDRAM_SET | (x + (rowOffset[y])))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print prints text on the display (started from current cursor position).
|
||||||
|
//
|
||||||
|
// It would automatically break to new line when the text is too long.
|
||||||
|
// You can also use \n as line breakers.
|
||||||
|
func (d *Device) Print(data []byte) {
|
||||||
|
for _, chr := range data {
|
||||||
|
if chr == '\n' {
|
||||||
|
d.newLine()
|
||||||
|
} else {
|
||||||
|
d.cursor.x++
|
||||||
|
if d.cursor.x >= d.width {
|
||||||
|
d.newLine()
|
||||||
|
}
|
||||||
|
d.sendData(uint8(rune(chr)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCharacter crates custom characters (using data parameter)
|
||||||
|
// and stores it under CGRAM address (using cgramAddr, 0x0-0x7).
|
||||||
|
func (d *Device) CreateCharacter(cgramAddr uint8, data []byte) {
|
||||||
|
cgramAddr &= 0x7
|
||||||
|
d.sendCommand(CGRAM_SET | cgramAddr<<3)
|
||||||
|
for _, dd := range data {
|
||||||
|
d.sendData(dd)
|
||||||
|
}
|
||||||
|
d.SetCursor(d.cursor.x, d.cursor.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisplayOn turns on/off the display.
|
||||||
|
func (d *Device) DisplayOn(option bool) {
|
||||||
|
if option {
|
||||||
|
d.displaycontrol |= DISPLAY_ON
|
||||||
|
} else {
|
||||||
|
d.displaycontrol &= ^uint8(DISPLAY_ON)
|
||||||
|
}
|
||||||
|
d.sendCommand(DISPLAY_ON_OFF | d.displaycontrol)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CursorOn display/hides the cursor.
|
||||||
|
func (d *Device) CursorOn(option bool) {
|
||||||
|
if option {
|
||||||
|
d.displaycontrol |= CURSOR_ON
|
||||||
|
} else {
|
||||||
|
d.displaycontrol &= ^uint8(CURSOR_ON)
|
||||||
|
}
|
||||||
|
d.sendCommand(DISPLAY_ON_OFF | d.displaycontrol)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CursorBlink turns on/off the blinking cursor mode.
|
||||||
|
func (d *Device) CursorBlink(option bool) {
|
||||||
|
if option {
|
||||||
|
d.displaycontrol |= CURSOR_BLINK_ON
|
||||||
|
} else {
|
||||||
|
d.displaycontrol &= ^uint8(CURSOR_BLINK_ON)
|
||||||
|
}
|
||||||
|
d.sendCommand(DISPLAY_ON_OFF | d.displaycontrol)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BacklightOn turns on/off the display backlight.
|
||||||
|
func (d *Device) BacklightOn(option bool) {
|
||||||
|
if option {
|
||||||
|
d.backlight = BACKLIGHT_ON
|
||||||
|
} else {
|
||||||
|
d.backlight = BACKLIGHT_OFF
|
||||||
|
}
|
||||||
|
d.expanderWrite(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) newLine() {
|
||||||
|
d.cursor.x = 0
|
||||||
|
d.cursor.y++
|
||||||
|
d.SetCursor(d.cursor.x, d.cursor.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func delayms(t uint16) {
|
||||||
|
time.Sleep(time.Millisecond * time.Duration(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
func delayus(t uint16) {
|
||||||
|
time.Sleep(time.Microsecond * time.Duration(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) expanderWrite(value uint8) {
|
||||||
|
d.bus.Tx(uint16(d.addr), []uint8{value | d.backlight}, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) pulseEnable(value uint8) {
|
||||||
|
d.expanderWrite(value | En)
|
||||||
|
delayus(1)
|
||||||
|
d.expanderWrite(value & ^uint8(En))
|
||||||
|
delayus(50)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) write4bits(value uint8) {
|
||||||
|
d.expanderWrite(value)
|
||||||
|
d.pulseEnable(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) write(value uint8, mode uint8) {
|
||||||
|
d.write4bits(uint8(value&0xf0) | mode)
|
||||||
|
d.write4bits(uint8((value<<4)&0xf0) | mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) sendCommand(value uint8) {
|
||||||
|
d.write(value, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Device) sendData(value uint8) {
|
||||||
|
d.write(value, Rs)
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package hd44780i2c
|
||||||
|
|
||||||
|
const (
|
||||||
|
|
||||||
|
// commands
|
||||||
|
DISPLAY_CLEAR = 0x01
|
||||||
|
CURSOR_HOME = 0x02
|
||||||
|
ENTRY_MODE = 0x04
|
||||||
|
DISPLAY_ON_OFF = 0x08
|
||||||
|
CURSOR_DISPLAY_SHIFT = 0x10
|
||||||
|
FUNCTION_MODE = 0x20
|
||||||
|
CGRAM_SET = 0x40
|
||||||
|
DDRAM_SET = 0x80
|
||||||
|
|
||||||
|
// flags for display entry mode
|
||||||
|
// CURSOR_DECREASE = 0x00
|
||||||
|
CURSOR_INCREASE = 0x02
|
||||||
|
// DISPLAY_SHIFT = 0x01
|
||||||
|
DISPLAY_NO_SHIFT = 0x00
|
||||||
|
|
||||||
|
// flags for display on/off control
|
||||||
|
DISPLAY_ON = 0x04
|
||||||
|
DISPLAY_OFF = 0x00
|
||||||
|
CURSOR_ON = 0x02
|
||||||
|
CURSOR_OFF = 0x00
|
||||||
|
CURSOR_BLINK_ON = 0x01
|
||||||
|
CURSOR_BLINK_OFF = 0x00
|
||||||
|
|
||||||
|
// flags for function set
|
||||||
|
// DATA_LENGTH_8BIT = 0x10
|
||||||
|
DATA_LENGTH_4BIT = 0x00
|
||||||
|
TWO_LINE = 0x08
|
||||||
|
ONE_LINE = 0x00
|
||||||
|
FONT_5X10 = 0x04
|
||||||
|
FONT_5X8 = 0x00
|
||||||
|
|
||||||
|
// flags for backlight control
|
||||||
|
BACKLIGHT_ON = 0x08
|
||||||
|
BACKLIGHT_OFF = 0x00
|
||||||
|
|
||||||
|
En = 0x04 // Enable bit
|
||||||
|
// Rw = 0x02 // Read/Write bit
|
||||||
|
Rs = 0x01 // Register select bit
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user