Add Support for SparkFun SerLCD Display (#88)

* Initial Creation
* Bump conn version
* add more documentation for 32 byte limit. minor code restructure to be more transparent
pull/89/head
gsexton 1 year ago committed by GitHub
parent c46abe928c
commit cad22759dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -11,7 +11,7 @@ require (
github.com/maruel/ansi256 v1.0.2 github.com/maruel/ansi256 v1.0.2
github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-colorable v0.1.13
golang.org/x/image v0.23.0 golang.org/x/image v0.23.0
periph.io/x/conn/v3 v3.7.1 periph.io/x/conn/v3 v3.7.2
periph.io/x/host/v3 v3.8.3 periph.io/x/host/v3 v3.8.3
) )

@ -15,7 +15,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
periph.io/x/conn/v3 v3.7.1 h1:tMjNv3WO8jEz/ePuXl7y++2zYi8LsQ5otbmqGKy3Myg= periph.io/x/conn/v3 v3.7.2 h1:qt9dE6XGP5ljbFnCKRJ9OOCoiOyBGlw7JZgoi72zZ1s=
periph.io/x/conn/v3 v3.7.1/go.mod h1:c+HCVjkzbf09XzcqZu/t+U8Ss/2QuJj0jgRF6Nye838= periph.io/x/conn/v3 v3.7.2/go.mod h1:Ao0b4sFRo4QOx6c1tROJU1fLJN1hUIYggjOrkIVnpGg=
periph.io/x/host/v3 v3.8.3 h1:v90ozCFDWgEyfNElZ+JnOvq0jAdW0vmgjCUy8dYXDds= periph.io/x/host/v3 v3.8.3 h1:v90ozCFDWgEyfNElZ+JnOvq0jAdW0vmgjCUy8dYXDds=
periph.io/x/host/v3 v3.8.3/go.mod h1:uKrIpfXjELwHkwGBNe6aos//XiQ/3uxDa1P2BmLV6Ok= periph.io/x/host/v3 v3.8.3/go.mod h1:uKrIpfXjELwHkwGBNe6aos//XiQ/3uxDa1P2BmLV6Ok=

@ -0,0 +1,50 @@
// Copyright 2024 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 serlcd_test
import (
"errors"
"fmt"
"log"
"time"
"periph.io/x/conn/v3/display"
"periph.io/x/conn/v3/display/displaytest"
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/i2c/i2creg"
"periph.io/x/devices/v3/serlcd"
"periph.io/x/host/v3"
)
func Example() {
if _, err := host.Init(); err != nil {
log.Fatal(err)
}
bus, err := i2creg.Open("")
if err != nil {
log.Fatal(err)
}
defer bus.Close()
conn := &i2c.Dev{Bus: bus, Addr: serlcd.DefaultI2CAddress}
dev := serlcd.NewConn(conn, 4, 20)
_ = dev.Clear()
n, err := dev.WriteString("Hello")
fmt.Printf("n=%d, err=%s\n", n, err)
time.Sleep(10 * time.Second)
fmt.Println("calling TestTextDisplay")
errs := displaytest.TestTextDisplay(dev, true)
fmt.Println("back from TestTextDsiplay")
for _, e := range errs {
if !errors.Is(e, display.ErrNotImplemented) {
fmt.Println(e)
}
}
}

@ -0,0 +1,254 @@
// Copyright 2024 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.
// This package provides an implementation for the SparkFun SerLCD intelligent
// LCD display. This display provides hardware interfaces for SPI, I2C, and
// UART. Implements conn.display.TextDisplay
package serlcd
import (
"errors"
"fmt"
"io"
"time"
"periph.io/x/conn/v3"
"periph.io/x/conn/v3/display"
)
// Representation of a SerLCD display.
type Dev struct {
conn conn.Conn
w io.Writer
cols int
rows int
// Display on/off, Curosr, Blink
displayDCB byte
}
const (
// For i2c there's a buffer limitation of 32 bytes.
// See this issue:
//
// https://github.com/sparkfun/OpenLCD/issues/29
//
// Writing more than that will lock the device up necessitating
// a device reset.
_MAX_I2C_WRITE = 32
DefaultI2CAddress uint16 = 0x72
)
var settingMode byte = 0x7c
var cmdMode byte = 0xfe
var clear = []byte{settingMode, 0x2d}
func wrap(err error) error {
return fmt.Errorf("serlcd: %w", err)
}
// Create a SerLCD display using a hardware interface that provides io.Writer.
// That can be the i2c.Bus, or a 3rd Party UART library for serial
// communications.
func NewSerLCD(writer io.Writer, rows, cols int) *Dev {
dev := &Dev{w: writer, rows: rows, cols: cols}
_ = dev.Display(true)
return dev
}
// Create a SerLCD display using a hardware interface that provides
// conn.Conn. For example, a conn.spi.Conn
func NewConn(conn conn.Conn, rows, cols int) *Dev {
dev := &Dev{conn: conn, rows: rows, cols: cols, w: nil}
return dev
}
// Enable/Disable auto scroll
func (dev *Dev) AutoScroll(enabled bool) (err error) {
return wrap(display.ErrNotImplemented)
}
// Return the number of columns the display supports
func (dev *Dev) Cols() int {
return dev.cols
}
// Clear the display and move the cursor home.
func (dev *Dev) Clear() (err error) {
_, err = dev.Write(clear)
time.Sleep(2 * time.Millisecond)
return
}
// Set the cursor mode. You can pass multiple arguments.
// Cursor(CursorOff, CursorUnderline)
func (dev *Dev) Cursor(mode ...display.CursorMode) (err error) {
dev.displayDCB &= 0x04
for _, cmd := range mode {
switch cmd {
case display.CursorBlink:
dev.displayDCB |= 0x01
case display.CursorUnderline:
dev.displayDCB |= 0x02
case display.CursorBlock:
dev.displayDCB |= 0x01
case display.CursorOff:
default:
err = wrap(display.ErrInvalidCommand)
return
}
}
dev.displayDCB = (dev.displayDCB | 0x08) & 0xf
_, err = dev.Write([]byte{cmdMode, dev.displayDCB})
return
}
// Halt shuts down the display. If the IO source implements io.Closer, it is
// called.
func (dev *Dev) Halt() (err error) {
err = dev.Clear()
if err != nil {
return
}
err = dev.Display(false)
if err != nil {
return
}
if dev.w != nil {
if cl, ok := dev.w.(io.Closer); ok {
err = cl.Close()
}
}
return
}
// Move the cursor home (MinRow(),MinCol())
func (dev *Dev) Home() (err error) {
err = dev.MoveTo(dev.MinRow(), dev.MinCol())
time.Sleep(2 * time.Millisecond)
return
}
// Return the min column position.
func (dev *Dev) MinCol() int {
return 0
}
// Return the min row position.
func (dev *Dev) MinRow() int {
return 0
}
// Move the cursor forward or backward.
func (dev *Dev) Move(dir display.CursorDirection) (err error) {
cmdByte := byte(0x10)
switch dir {
case display.Backward:
// Nothing
case display.Forward:
cmdByte |= 0x04
case display.Down:
fallthrough
case display.Up:
fallthrough
default:
err = wrap(display.ErrNotImplemented)
return
}
_, err = dev.Write([]byte{cmdMode, cmdByte})
return
}
// Move the cursor to an arbitrary position.
func (dev *Dev) MoveTo(row, col int) (err error) {
lineOffsets := []byte{0, 64, 20, 84}
if row < dev.MinRow() || row >= dev.Rows() ||
col < dev.MinCol() || col >= dev.Cols() {
return errors.New("serlcd: invalid MoveTo() offset")
}
cmdByte := byte(0x80) + lineOffsets[row] + byte(col)
_, err = dev.Write([]byte{cmdMode, byte(cmdByte)})
return
}
// Return the number of rows the display supports.
func (dev *Dev) Rows() int {
return dev.rows
}
// Turn the display on / off
func (dev *Dev) Display(on bool) (err error) {
if on {
dev.displayDCB |= 0x04
} else {
dev.displayDCB ^= 0x04
}
_, err = dev.Write([]byte{cmdMode, (dev.displayDCB | 0x08) & 0x0f})
return
}
// return info about the display.
func (dev *Dev) String() string {
ioType := "None"
if dev.conn != nil {
ioType = "periph.io.Conn.Conn"
} else if dev.w != nil {
ioType = fmt.Sprintf("%#v", dev.w)
}
return fmt.Sprintf("SparkFun SerLCD %dx%d Display - %s", dev.cols, dev.rows, ioType)
}
// Write a set of bytes to the display.
func (dev *Dev) Write(p []byte) (n int, err error) {
if dev.w != nil {
n, err = dev.w.Write(p)
return
}
for n < len(p) {
bytesToWrite := len(p) - n
if bytesToWrite > _MAX_I2C_WRITE {
bytesToWrite = _MAX_I2C_WRITE
}
w := p[n : n+bytesToWrite]
err = dev.conn.Tx(w, nil)
if err != nil {
break
}
n = n + bytesToWrite
time.Sleep(time.Duration(40*bytesToWrite) * time.Microsecond)
}
return
}
// Write a string output to the display.
func (dev *Dev) WriteString(text string) (n int, err error) {
n, err = dev.Write([]byte(text))
return
}
// Set the backlight intensity with 0 being off, and 255 being maximum.
func (dev *Dev) Backlight(intensity display.Intensity) error {
return dev.RGBBacklight(intensity, intensity, intensity)
}
// Set the character contrast on the device. Writes to EEPROM, so this should
// be used sparingly. The default device Contrast is 40.
func (dev *Dev) Contrast(contrast display.Contrast) error {
_, err := dev.Write([]byte{settingMode, 0x18, byte(contrast)})
return err
}
// Set the backlight color with 0 being off, and 255 being maximum intensity
// for each color.
func (dev *Dev) RGBBacklight(red, green, blue display.Intensity) error {
_, err := dev.Write([]byte{settingMode, 0x2b, byte(red & 0xff), byte(green & 0xff), byte(blue & 0xff)})
return err
}
var _ display.TextDisplay = &Dev{}
var _ display.DisplayContrast = &Dev{}
var _ display.DisplayBacklight = &Dev{}
var _ display.DisplayRGBBacklight = &Dev{}
var _ conn.Resource = &Dev{}

@ -0,0 +1,387 @@
// Copyright 2024 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 serlcd
import (
"errors"
"fmt"
"os"
"testing"
"time"
"periph.io/x/conn/v3/display"
"periph.io/x/conn/v3/display/displaytest"
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/i2c/i2creg"
"periph.io/x/conn/v3/i2c/i2ctest"
"periph.io/x/host/v3"
)
var liveDevice bool = false
var bus i2c.Bus
var eepromTests bool = false
var sleepDuration time.Duration = time.Millisecond
// Playback for interface tests.
var pbInterface = []i2ctest.IO{
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0x53, 0x70, 0x61, 0x72, 0x6b, 0x46, 0x75, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x4c, 0x43, 0x44, 0x20, 0x32, 0x30, 0x78, 0x34, 0x20, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x20, 0x2d, 0x20, 0x70}},
{Addr: DefaultI2CAddress, W: []uint8{0x65, 0x72, 0x69, 0x70, 0x68, 0x2e, 0x69, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x6e}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0x41, 0x75, 0x74, 0x6f, 0x20, 0x53, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x20, 0x54, 0x65, 0x73, 0x74}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x80}},
{Addr: DefaultI2CAddress, W: []uint8{0x41}},
{Addr: DefaultI2CAddress, W: []uint8{0x42}},
{Addr: DefaultI2CAddress, W: []uint8{0x43}},
{Addr: DefaultI2CAddress, W: []uint8{0x44}},
{Addr: DefaultI2CAddress, W: []uint8{0x45}},
{Addr: DefaultI2CAddress, W: []uint8{0x20}},
{Addr: DefaultI2CAddress, W: []uint8{0x47}},
{Addr: DefaultI2CAddress, W: []uint8{0x48}},
{Addr: DefaultI2CAddress, W: []uint8{0x49}},
{Addr: DefaultI2CAddress, W: []uint8{0x4a}},
{Addr: DefaultI2CAddress, W: []uint8{0x20}},
{Addr: DefaultI2CAddress, W: []uint8{0x4c}},
{Addr: DefaultI2CAddress, W: []uint8{0x4d}},
{Addr: DefaultI2CAddress, W: []uint8{0x4e}},
{Addr: DefaultI2CAddress, W: []uint8{0x4f}},
{Addr: DefaultI2CAddress, W: []uint8{0x20}},
{Addr: DefaultI2CAddress, W: []uint8{0x51}},
{Addr: DefaultI2CAddress, W: []uint8{0x52}},
{Addr: DefaultI2CAddress, W: []uint8{0x53}},
{Addr: DefaultI2CAddress, W: []uint8{0x54}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc0}},
{Addr: DefaultI2CAddress, W: []uint8{0x41}},
{Addr: DefaultI2CAddress, W: []uint8{0x42}},
{Addr: DefaultI2CAddress, W: []uint8{0x43}},
{Addr: DefaultI2CAddress, W: []uint8{0x44}},
{Addr: DefaultI2CAddress, W: []uint8{0x45}},
{Addr: DefaultI2CAddress, W: []uint8{0x20}},
{Addr: DefaultI2CAddress, W: []uint8{0x47}},
{Addr: DefaultI2CAddress, W: []uint8{0x48}},
{Addr: DefaultI2CAddress, W: []uint8{0x49}},
{Addr: DefaultI2CAddress, W: []uint8{0x4a}},
{Addr: DefaultI2CAddress, W: []uint8{0x20}},
{Addr: DefaultI2CAddress, W: []uint8{0x4c}},
{Addr: DefaultI2CAddress, W: []uint8{0x4d}},
{Addr: DefaultI2CAddress, W: []uint8{0x4e}},
{Addr: DefaultI2CAddress, W: []uint8{0x4f}},
{Addr: DefaultI2CAddress, W: []uint8{0x20}},
{Addr: DefaultI2CAddress, W: []uint8{0x51}},
{Addr: DefaultI2CAddress, W: []uint8{0x52}},
{Addr: DefaultI2CAddress, W: []uint8{0x53}},
{Addr: DefaultI2CAddress, W: []uint8{0x54}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x94}},
{Addr: DefaultI2CAddress, W: []uint8{0x41}},
{Addr: DefaultI2CAddress, W: []uint8{0x42}},
{Addr: DefaultI2CAddress, W: []uint8{0x43}},
{Addr: DefaultI2CAddress, W: []uint8{0x44}},
{Addr: DefaultI2CAddress, W: []uint8{0x45}},
{Addr: DefaultI2CAddress, W: []uint8{0x20}},
{Addr: DefaultI2CAddress, W: []uint8{0x47}},
{Addr: DefaultI2CAddress, W: []uint8{0x48}},
{Addr: DefaultI2CAddress, W: []uint8{0x49}},
{Addr: DefaultI2CAddress, W: []uint8{0x4a}},
{Addr: DefaultI2CAddress, W: []uint8{0x20}},
{Addr: DefaultI2CAddress, W: []uint8{0x4c}},
{Addr: DefaultI2CAddress, W: []uint8{0x4d}},
{Addr: DefaultI2CAddress, W: []uint8{0x4e}},
{Addr: DefaultI2CAddress, W: []uint8{0x4f}},
{Addr: DefaultI2CAddress, W: []uint8{0x20}},
{Addr: DefaultI2CAddress, W: []uint8{0x51}},
{Addr: DefaultI2CAddress, W: []uint8{0x52}},
{Addr: DefaultI2CAddress, W: []uint8{0x53}},
{Addr: DefaultI2CAddress, W: []uint8{0x54}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xd4}},
{Addr: DefaultI2CAddress, W: []uint8{0x41}},
{Addr: DefaultI2CAddress, W: []uint8{0x42}},
{Addr: DefaultI2CAddress, W: []uint8{0x43}},
{Addr: DefaultI2CAddress, W: []uint8{0x44}},
{Addr: DefaultI2CAddress, W: []uint8{0x45}},
{Addr: DefaultI2CAddress, W: []uint8{0x20}},
{Addr: DefaultI2CAddress, W: []uint8{0x47}},
{Addr: DefaultI2CAddress, W: []uint8{0x48}},
{Addr: DefaultI2CAddress, W: []uint8{0x49}},
{Addr: DefaultI2CAddress, W: []uint8{0x4a}},
{Addr: DefaultI2CAddress, W: []uint8{0x20}},
{Addr: DefaultI2CAddress, W: []uint8{0x4c}},
{Addr: DefaultI2CAddress, W: []uint8{0x4d}},
{Addr: DefaultI2CAddress, W: []uint8{0x4e}},
{Addr: DefaultI2CAddress, W: []uint8{0x4f}},
{Addr: DefaultI2CAddress, W: []uint8{0x20}},
{Addr: DefaultI2CAddress, W: []uint8{0x51}},
{Addr: DefaultI2CAddress, W: []uint8{0x52}},
{Addr: DefaultI2CAddress, W: []uint8{0x53}},
{Addr: DefaultI2CAddress, W: []uint8{0x54}},
{Addr: DefaultI2CAddress, W: []uint8{0x61, 0x75, 0x74, 0x6f, 0x20, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c, 0x20, 0x68, 0x61, 0x70, 0x70, 0x65, 0x6e}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0x41, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x20, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x80}},
{Addr: DefaultI2CAddress, W: []uint8{0x28, 0x30, 0x2c, 0x30, 0x29}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc1}},
{Addr: DefaultI2CAddress, W: []uint8{0x28, 0x31, 0x2c, 0x31, 0x29}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x96}},
{Addr: DefaultI2CAddress, W: []uint8{0x28, 0x32, 0x2c, 0x32, 0x29}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xd7}},
{Addr: DefaultI2CAddress, W: []uint8{0x28, 0x33, 0x2c, 0x33, 0x29}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc0}},
{Addr: DefaultI2CAddress, W: []uint8{0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x4f, 0x66, 0x66}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc0}},
{Addr: DefaultI2CAddress, W: []uint8{0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x65}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xe}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc0}},
{Addr: DefaultI2CAddress, W: []uint8{0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x42, 0x6c, 0x6f, 0x63, 0x6b}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xd}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc0}},
{Addr: DefaultI2CAddress, W: []uint8{0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x3a, 0x20, 0x42, 0x6c, 0x69, 0x6e, 0x6b}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xd}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0x54, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x3e}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x14}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x14}},
{Addr: DefaultI2CAddress, W: []uint8{0x30}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x10}},
{Addr: DefaultI2CAddress, W: []uint8{0x31}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x10}},
{Addr: DefaultI2CAddress, W: []uint8{0x32}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x10}},
{Addr: DefaultI2CAddress, W: []uint8{0x33}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x10}},
{Addr: DefaultI2CAddress, W: []uint8{0x34}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x10}},
{Addr: DefaultI2CAddress, W: []uint8{0x35}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x10}},
{Addr: DefaultI2CAddress, W: []uint8{0x36}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x10}},
{Addr: DefaultI2CAddress, W: []uint8{0x37}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x10}},
{Addr: DefaultI2CAddress, W: []uint8{0x38}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x10}},
{Addr: DefaultI2CAddress, W: []uint8{0x39}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x10}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0x53, 0x65, 0x74, 0x20, 0x64, 0x65, 0x76, 0x20, 0x6f, 0x66, 0x66}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0x8}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0x53, 0x65, 0x74, 0x20, 0x64, 0x65, 0x76, 0x20, 0x6f, 0x6e}}}
var pbBacklight = []i2ctest.IO{
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0x53, 0x65, 0x74, 0x20, 0x42, 0x61, 0x63, 0x6b, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x20, 0x4f, 0x66, 0x66}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2b, 0x0, 0x0, 0x0}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2b, 0xff, 0xff, 0xff}},
{Addr: DefaultI2CAddress, W: []uint8{0x53, 0x65, 0x74, 0x20, 0x42, 0x61, 0x63, 0x6b, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x20, 0x4f, 0x6e}}}
var pbRGBBacklight = []i2ctest.IO{
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0x53, 0x65, 0x74, 0x20, 0x42, 0x61, 0x63, 0x6b, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x20, 0x5b, 0x5d, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74}},
{Addr: DefaultI2CAddress, W: []uint8{0x79, 0x7b, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x7d}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2b, 0xff, 0x0, 0x0}},
{Addr: DefaultI2CAddress, W: []uint8{0x53, 0x65, 0x74, 0x20, 0x42, 0x61, 0x63, 0x6b, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x20, 0x5b, 0x5d, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74}},
{Addr: DefaultI2CAddress, W: []uint8{0x79, 0x7b, 0x30, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x30, 0x7d}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2b, 0x0, 0xff, 0x0}},
{Addr: DefaultI2CAddress, W: []uint8{0x53, 0x65, 0x74, 0x20, 0x42, 0x61, 0x63, 0x6b, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x20, 0x5b, 0x5d, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74}},
{Addr: DefaultI2CAddress, W: []uint8{0x79, 0x7b, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x7d}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2b, 0x0, 0x0, 0xff}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2b, 0xff, 0xff, 0xff}}}
var pbContrast = []i2ctest.IO{
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0x53, 0x65, 0x74, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x73, 0x74, 0x20, 0x35}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x18, 0x5}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc0}},
{Addr: DefaultI2CAddress, W: []uint8{0x53, 0x65, 0x74, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x73, 0x74, 0x20, 0x30, 0x78, 0x38, 0x30}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x18, 0x80}},
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x18, 0x28}}}
var pbHalt = []i2ctest.IO{
{Addr: DefaultI2CAddress, W: []uint8{0x7c, 0x2d}},
{Addr: DefaultI2CAddress, W: []uint8{0xfe, 0xc}}}
func init() {
var err error
// If the environment variable is set, assume we have a live device on
// the default i2c bus and use it for testing. If the variable is not
// present, then use the playback/read values.
liveDevice = os.Getenv("SERLCD") != ""
// If EEPROM is set, then tests that write to EEPROM (display intensity /
// contrast are tested if it's a live test.
eepromTests = os.Getenv("EEPROM") != ""
if _, err = host.Init(); err != nil {
fmt.Println(err)
}
if liveDevice {
sleepDuration = 5 * time.Second
bus, err = i2creg.Open("")
if err != nil {
fmt.Println(err)
}
// Add the recorder to dump the data stream when we're using a live device.
bus = &i2ctest.Record{Bus: bus}
} else {
bus = &i2ctest.Playback{DontPanic: true}
}
}
// getDev returns a SerLCD device for testing connected to either a live
// bus, or a playback bus. playbackOps is a slice of i2ctest.IO
// operations to be used for playback mode. Ignored for live device
// testing.
func getDev(t *testing.T, playbackOps ...[]i2ctest.IO) (*Dev, error) {
if liveDevice {
if recorder, ok := bus.(*i2ctest.Record); ok {
// Clear the operations buffer.
recorder.Ops = make([]i2ctest.IO, 0, 32)
}
} else {
if len(playbackOps) == 1 {
pb := bus.(*i2ctest.Playback)
pb.Ops = playbackOps[0]
pb.Count = 0
}
}
conn := &i2c.Dev{Bus: bus, Addr: DefaultI2CAddress}
dev := NewConn(conn, 4, 20)
return dev, nil
}
// shutdown dumps the recorder values if we we're running a live device.
func shutdown(t *testing.T) {
if recorder, ok := bus.(*i2ctest.Record); ok {
t.Logf("%#v", recorder.Ops)
}
}
// This tests all functions in the TextDisplay interface.
func TestInterface(t *testing.T) {
dev, err := getDev(t, pbInterface)
defer shutdown(t)
if err != nil {
t.Fatal(err)
}
errs := displaytest.TestTextDisplay(dev, liveDevice)
for _, err := range errs {
if !errors.Is(err, display.ErrNotImplemented) {
t.Error(err)
}
}
}
// TestBacklight verifies the backlight turns off.
func TestBacklight(t *testing.T) {
if liveDevice && !eepromTests {
return
}
dev, err := getDev(t, pbBacklight)
defer shutdown(t)
if err != nil {
t.Fatal(err)
}
_ = dev.Clear()
_, err = dev.WriteString("Set Backlight Off")
if err != nil {
t.Error(err)
}
err = dev.Backlight(0)
if err != nil {
t.Error(err)
}
time.Sleep(sleepDuration)
_ = dev.Clear()
err = dev.Backlight(0xff)
if err != nil {
t.Error(err)
}
_, _ = dev.WriteString("Set Backlight On")
time.Sleep(sleepDuration)
}
// TestRGBBacklight tests the RGB Backlight works as expected
func TestRGBBacklight(t *testing.T) {
if liveDevice && !eepromTests {
return
}
dev, err := getDev(t, pbRGBBacklight)
defer shutdown(t)
if err != nil {
t.Fatal(err)
}
_ = dev.Clear()
for ix := range 3 {
w := make([]display.Intensity, 3)
w[ix] = 0xff
_, err = dev.WriteString(fmt.Sprintf("Set Backlight %#v", w))
if err != nil {
t.Error(err)
}
err = dev.RGBBacklight(w[0], w[1], w[2])
if err != nil {
t.Error(err)
}
time.Sleep(sleepDuration)
}
err = dev.RGBBacklight(0xff, 0xff, 0xff)
if err != nil {
t.Error(err)
}
}
// TestContrast() checks contrast operations.
func TestContrast(t *testing.T) {
if liveDevice && !eepromTests {
return
}
dev, err := getDev(t, pbContrast)
defer shutdown(t)
if err != nil {
t.Fatal(err)
}
_ = dev.Clear()
_, _ = dev.WriteString("Set Contrast 5")
err = dev.Contrast(5)
if err != nil {
t.Error(err)
}
_ = dev.MoveTo(dev.MinRow()+1, dev.MinCol())
time.Sleep(sleepDuration)
_, _ = dev.WriteString("Set Contrast 0x80")
time.Sleep(sleepDuration)
err = dev.Contrast(0x80)
if err != nil {
t.Error(err)
}
time.Sleep(sleepDuration)
_ = dev.Contrast(40)
}
func TestHalt(t *testing.T) {
dev, err := getDev(t, pbHalt)
defer shutdown(t)
if err != nil {
t.Fatal(err)
}
err = dev.Halt()
if err != nil {
t.Error(err)
}
}
Loading…
Cancel
Save