mirror of https://github.com/periph/devices
Initial Add
parent
cad22759dd
commit
60a61ef359
@ -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 matrixorbital_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/matrixorbital"
|
||||
"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: 0x11}
|
||||
dev := matrixorbital.NewConnLK2047T(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,377 @@
|
||||
// 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 interface to MatrixOrbital Character LCD displays.
|
||||
// The LK2047T display is compatible with the Adafruit USB-LCD Backpack.
|
||||
package matrixorbital
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
// "time"
|
||||
|
||||
"periph.io/x/conn/v3"
|
||||
"periph.io/x/conn/v3/display"
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
)
|
||||
|
||||
// Constants for programmable LEDs on some models.
|
||||
type LEDColor int
|
||||
|
||||
const (
|
||||
// Constants for colors supported by LEDs.
|
||||
Off LEDColor = iota
|
||||
Red
|
||||
Green
|
||||
Yellow
|
||||
)
|
||||
|
||||
// The LK2047T is a basic MatrixOrbital LCD display. It's a 20x4 LCD with a
|
||||
// keypad and LEDs.
|
||||
//
|
||||
// Implements periph.io/x/conn/v3/display.TextDisplay, Backlight, and
|
||||
// DisplayContrast.
|
||||
type LK2047T struct {
|
||||
// Pins represents the set of gpio.PinOut pins exposed by the device. For
|
||||
// units with LEDS, the pins are used to manipulate them. For the Adafruit
|
||||
// USB/LCD backpack, 4 pins are exposed.
|
||||
Pins []gpio.PinOut
|
||||
d conn.Conn
|
||||
writer io.Writer
|
||||
mu sync.Mutex
|
||||
rows int
|
||||
cols int
|
||||
chKeyboard chan byte
|
||||
shutdown chan struct{}
|
||||
}
|
||||
|
||||
type GPOEnabledDisplay interface {
|
||||
// SetGPO turns a GPO pin on or off
|
||||
GPO(pin int, l gpio.Level) error
|
||||
}
|
||||
|
||||
// Command byte values used by the display. This only implements a subset of
|
||||
// commands.
|
||||
var cmdByte byte = 0xfe
|
||||
var autoScrollOff = []byte{cmdByte, 0x52}
|
||||
var autoScrollOn = []byte{cmdByte, 0x51}
|
||||
var blockCursorOff = []byte{cmdByte, 0x54}
|
||||
var blockCursorOn = []byte{cmdByte, 0x53}
|
||||
var clearScreen = []byte{cmdByte, 0x58}
|
||||
var cursorBack = []byte{cmdByte, 0x4c}
|
||||
var cursorBlinkOff = []byte{cmdByte, 0x54}
|
||||
var cursorBlinkOn = []byte{cmdByte, 0x53}
|
||||
var cursorForward = []byte{cmdByte, 0x4d}
|
||||
var displayOff = []byte{cmdByte, 0x46}
|
||||
var displayOn = []byte{cmdByte, 0x42}
|
||||
var goHome = []byte{cmdByte, 0x48}
|
||||
var keypadBacklightOff = []byte{cmdByte, 0x98}
|
||||
var setBrightness = []byte{cmdByte, 0x99}
|
||||
var setContrast = []byte{cmdByte, 0x50}
|
||||
var setCursorPosition = []byte{cmdByte, 0x47}
|
||||
var setGPOOn = []byte{cmdByte, 0x57}
|
||||
var setGPOOff = []byte{cmdByte, 0x56}
|
||||
var underlineCursorOff = []byte{cmdByte, 0x4b}
|
||||
var underlineCursorOn = []byte{cmdByte, 0x4a}
|
||||
|
||||
func wrapErr(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("lk2047t: %w", err)
|
||||
}
|
||||
|
||||
// Create a new LCD device using a periph.io/conn/Conn
|
||||
func NewConnLK2047T(conn conn.Conn, rows, cols int) *LK2047T {
|
||||
dev := &LK2047T{d: conn, rows: rows, cols: cols, Pins: make([]gpio.PinOut, 6)}
|
||||
a := GPOEnabledDisplay(dev)
|
||||
makePins(&a, dev.Pins)
|
||||
return dev
|
||||
}
|
||||
|
||||
// Create a new LCD device using an io.Writer. If your display is connected
|
||||
// using a hardware interface that periph.io doesn't support (e.g. UART),
|
||||
// you can still use this package as long as the hardware interface provides
|
||||
// the io.Writer interface. rows is the number of lines the display supports,
|
||||
// and cols is the character width of the device.
|
||||
func NewWriterLK2047T(writer io.Writer, rows, cols int) *LK2047T {
|
||||
dev := &LK2047T{writer: writer, rows: rows, cols: cols, Pins: make([]gpio.PinOut, 6)}
|
||||
a := GPOEnabledDisplay(dev)
|
||||
makePins(&a, dev.Pins)
|
||||
return dev
|
||||
}
|
||||
|
||||
// Enable or disable AutoScroll.
|
||||
func (dev *LK2047T) AutoScroll(enabled bool) (err error) {
|
||||
if enabled {
|
||||
_, err = dev.Write(autoScrollOn)
|
||||
} else {
|
||||
_, err = dev.Write(autoScrollOff)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Clears the screen, and moves the cursor to the home position.
|
||||
func (dev *LK2047T) Clear() (err error) {
|
||||
_, err = dev.Write(clearScreen)
|
||||
if err == nil {
|
||||
err = dev.Home()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Return the number of columns supported by the device.
|
||||
func (dev *LK2047T) Cols() int {
|
||||
return dev.cols
|
||||
}
|
||||
|
||||
// Set the cursor mode. E.G. underline, block, etc.
|
||||
func (dev *LK2047T) Cursor(modes ...display.CursorMode) (err error) {
|
||||
for _, mode := range modes {
|
||||
switch mode {
|
||||
case display.CursorOff:
|
||||
_, err = dev.Write(blockCursorOff)
|
||||
if err == nil {
|
||||
_, err = dev.Write(underlineCursorOff)
|
||||
}
|
||||
if err == nil {
|
||||
_, err = dev.Write(cursorBlinkOff)
|
||||
}
|
||||
case display.CursorUnderline:
|
||||
_, err = dev.Write(underlineCursorOn)
|
||||
case display.CursorBlock:
|
||||
_, err = dev.Write(blockCursorOn)
|
||||
case display.CursorBlink:
|
||||
_, err = dev.Write(cursorBlinkOn)
|
||||
default:
|
||||
err = fmt.Errorf("lk2047t: invalid cursor mode %d", mode)
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Halt shuts down the display, and closes the output device if it implements
|
||||
// io.Closer. If a keypad read operation is running, closing the device will
|
||||
// terminate it.
|
||||
func (dev *LK2047T) Halt() (err error) {
|
||||
err = dev.Display(false)
|
||||
_ = dev.KeypadBacklight(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dev.mu.Lock()
|
||||
defer dev.mu.Unlock()
|
||||
if dev.shutdown != nil {
|
||||
dev.shutdown <- struct{}{}
|
||||
}
|
||||
var cl io.Closer
|
||||
var ok bool
|
||||
if dev.d != nil {
|
||||
cl, ok = dev.d.(io.Closer)
|
||||
} else {
|
||||
cl, ok = dev.writer.(io.Closer)
|
||||
}
|
||||
|
||||
if ok {
|
||||
err = cl.Close()
|
||||
} else {
|
||||
err = errors.New("output connection doesn't support io.Closer()")
|
||||
}
|
||||
err = wrapErr(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Home resets the cursor to the default position.
|
||||
func (dev *LK2047T) Home() (err error) {
|
||||
_, err = dev.Write(goHome)
|
||||
return
|
||||
}
|
||||
|
||||
// MinCol returns the numbering scheme of the device's minimum column number.
|
||||
// Generally, it will be 0 or 1
|
||||
func (dev *LK2047T) MinCol() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// MinRow returns the numbering scheme of the device's minimum row (line)
|
||||
// number. Generally, it will be 0 or 1.
|
||||
func (dev *LK2047T) MinRow() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Move the cursor forward or backwards.
|
||||
func (dev *LK2047T) Move(direction display.CursorDirection) (err error) {
|
||||
switch direction {
|
||||
case display.Forward:
|
||||
_, err = dev.Write(cursorForward)
|
||||
case display.Backward:
|
||||
_, err = dev.Write(cursorBack)
|
||||
case display.Up:
|
||||
case display.Down:
|
||||
default:
|
||||
err = errors.New("lk2047t: invalid move direction.")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Move the cursor to an arbitrary row/column on the device.
|
||||
func (dev *LK2047T) MoveTo(row, col int) (err error) {
|
||||
if row < 1 || row > dev.rows || col < 1 || col > dev.cols {
|
||||
return fmt.Errorf("lk2047t: MoveTo(%d, %d) value out of range.", row, col)
|
||||
}
|
||||
_, err = dev.Write([]byte{setCursorPosition[0], setCursorPosition[1], byte(col), byte(row)})
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadKeypad reads from the displays built-in keypad. The io device used by the
|
||||
// display must implement io.Reader. If it does not, then an error is returned.
|
||||
func (dev *LK2047T) ReadKeypad() (<-chan byte, error) {
|
||||
dev.mu.Lock()
|
||||
defer dev.mu.Unlock()
|
||||
if dev.chKeyboard != nil {
|
||||
return dev.chKeyboard, nil
|
||||
}
|
||||
var rdr io.Reader
|
||||
var ok bool
|
||||
if dev.writer == nil {
|
||||
rdr, ok = dev.d.(io.Reader)
|
||||
} else {
|
||||
rdr, ok = dev.writer.(io.Reader)
|
||||
}
|
||||
if !ok {
|
||||
return nil, errors.New("lk2047t: output device does not implement io.Reader")
|
||||
}
|
||||
|
||||
dev.chKeyboard = make(chan byte, 8)
|
||||
dev.shutdown = make(chan struct{})
|
||||
go func() {
|
||||
defer func() {
|
||||
dev.mu.Lock()
|
||||
close(dev.chKeyboard)
|
||||
dev.chKeyboard = nil
|
||||
dev.mu.Unlock()
|
||||
}()
|
||||
buf := make([]byte, 4)
|
||||
var err error
|
||||
var n int
|
||||
for err == nil {
|
||||
select {
|
||||
case <-dev.shutdown:
|
||||
return
|
||||
default:
|
||||
n, err = rdr.Read(buf)
|
||||
if n > 0 {
|
||||
for ix := range n {
|
||||
dev.chKeyboard <- buf[ix]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return dev.chKeyboard, nil
|
||||
}
|
||||
|
||||
// Return the number of rows supported by the device.
|
||||
func (dev *LK2047T) Rows() int {
|
||||
return dev.rows
|
||||
}
|
||||
|
||||
// Set the intensity of the backlight. Refer to the docs in the lcd package
|
||||
// for warnings on this function. Provides periph.io/x/conn/v3/display.Backlight
|
||||
func (dev *LK2047T) Backlight(intensity display.Intensity) error {
|
||||
_, err := dev.Write([]byte{setBrightness[0], setBrightness[1], byte(intensity)})
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the constrast of the display. Refer to the docs in the lcd package
|
||||
// for warnings on this function. Provides periph.io/x/conn/v3/display.DisplayContrast
|
||||
func (dev *LK2047T) Contrast(contrast display.Contrast) error {
|
||||
_, err := dev.Write([]byte{setContrast[0], setContrast[1], byte(contrast)})
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the display on or off.
|
||||
func (dev *LK2047T) Display(on bool) (err error) {
|
||||
if on {
|
||||
_, err = dev.Write([]byte{displayOn[0], displayOn[1], 0})
|
||||
} else {
|
||||
_, err = dev.Write(displayOff)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (dev *LK2047T) KeypadBacklight(on bool) error {
|
||||
if on {
|
||||
return dev.Display(on)
|
||||
}
|
||||
_, err := dev.Write(keypadBacklightOff)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the specified output pin state.
|
||||
func (dev *LK2047T) GPO(pin int, on gpio.Level) (err error) {
|
||||
|
||||
if on {
|
||||
_, err = dev.Write([]byte{setGPOOn[0], setGPOOn[1], byte(pin)})
|
||||
} else {
|
||||
_, err = dev.Write([]byte{setGPOOff[0], setGPOOff[1], byte(pin)})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Set an led to a supported color. number is 0 based.
|
||||
func (dev *LK2047T) LED(number int, color LEDColor) error {
|
||||
if color < Off || color > Yellow {
|
||||
return fmt.Errorf("lk2047t: invalid color: %d", color)
|
||||
}
|
||||
err := dev.Pins[number*2].Out(gpio.Level(color&Red == Red))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dev.Pins[number*2+1].Out(gpio.Level(color&Green == Green))
|
||||
return err
|
||||
}
|
||||
|
||||
func (dev *LK2047T) String() string {
|
||||
var ioType any
|
||||
if dev.d != nil {
|
||||
ioType = dev.d
|
||||
} else {
|
||||
ioType = dev.writer
|
||||
}
|
||||
return fmt.Sprintf("MatrixOrbital LK204-7T LCD Display: Rows: %d Cols: %d Connection: %T", dev.rows, dev.cols, ioType)
|
||||
}
|
||||
|
||||
// Write commands or data to the display
|
||||
func (dev *LK2047T) Write(p []byte) (n int, err error) {
|
||||
dev.mu.Lock()
|
||||
defer dev.mu.Unlock()
|
||||
if dev.writer == nil {
|
||||
err = dev.d.Tx(p, nil)
|
||||
n = len(p)
|
||||
} else {
|
||||
n, err = dev.writer.Write(p)
|
||||
}
|
||||
err = wrapErr(err)
|
||||
return
|
||||
}
|
||||
|
||||
// WriteString sends a text string to the display.
|
||||
func (dev *LK2047T) WriteString(text string) (int, error) {
|
||||
n, err := dev.Write([]byte(text))
|
||||
return n, err
|
||||
}
|
||||
|
||||
var _ display.TextDisplay = &LK2047T{}
|
||||
var _ GPOEnabledDisplay = &LK2047T{}
|
||||
var _ display.DisplayContrast = &LK2047T{}
|
||||
var _ display.DisplayBacklight = &LK2047T{}
|
||||
var _ conn.Resource = &LK2047T{}
|
||||
@ -0,0 +1,191 @@
|
||||
// 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 matrixorbital
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"periph.io/x/conn/v3/display"
|
||||
"periph.io/x/conn/v3/display/displaytest"
|
||||
)
|
||||
|
||||
type mockReadWriterCloser struct {
|
||||
closed bool
|
||||
bytesWritten int
|
||||
bytesRead int
|
||||
readChars string
|
||||
hash hash.Hash32
|
||||
// TODO: Add CRC32 verification of written bytes...
|
||||
}
|
||||
|
||||
func (mr *mockReadWriterCloser) Read(p []byte) (n int, err error) {
|
||||
if mr.closed {
|
||||
err = io.EOF
|
||||
}
|
||||
cPos := rand.Intn(len(mr.readChars))
|
||||
p[0] = byte(mr.readChars[cPos])
|
||||
n = 1
|
||||
mr.bytesRead += 1
|
||||
return
|
||||
}
|
||||
|
||||
func (mr *mockReadWriterCloser) Write(p []byte) (n int, err error) {
|
||||
if mr.closed {
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
n = len(p)
|
||||
mr.hash.Write(p)
|
||||
mr.bytesWritten += n
|
||||
return
|
||||
}
|
||||
|
||||
func (mr *mockReadWriterCloser) Close() error {
|
||||
mr.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown accepts an expected hash. If expectedHash doesn't match the hash of
|
||||
// the stream written to the mock, it writes an error to t. If the test changes,
|
||||
// or the implementation of the device changes, you'll have to update the
|
||||
// expectedHash value in the Shutdown() call.
|
||||
func (mr *mockReadWriterCloser) Shutdown(t *testing.T, expectedHash uint32) {
|
||||
finalHash := mr.hash.Sum32()
|
||||
if expectedHash == 0 {
|
||||
t.Logf("Mock Reader: Final Hash=0x%x", finalHash)
|
||||
} else if finalHash != expectedHash {
|
||||
t.Errorf("Incorrect hash. Expected: 0x%x, received 0x%x", expectedHash, finalHash)
|
||||
}
|
||||
}
|
||||
|
||||
// getDisplay returns an LCD display constructed with a mock read/writer for
|
||||
// testing.
|
||||
func getDisplay() (*LK2047T, *mockReadWriterCloser) {
|
||||
|
||||
wr := &mockReadWriterCloser{readChars: "ABCDEGH", hash: crc32.NewIEEE()}
|
||||
|
||||
return NewWriterLK2047T(wr, 4, 20), wr
|
||||
}
|
||||
|
||||
func TestTextDisplay(t *testing.T) {
|
||||
fmt.Println("beginning tests")
|
||||
dev, mock := getDisplay()
|
||||
defer mock.Shutdown(t, 0x4b8e39ef)
|
||||
wrTest := "abcdef"
|
||||
nWritten, err := dev.WriteString(wrTest)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if nWritten != len(wrTest) || mock.bytesWritten != len(wrTest) {
|
||||
t.Errorf("write string error wrote %d bytes expected %d", mock.bytesWritten, len(wrTest))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterface(t *testing.T) {
|
||||
dev, mock := getDisplay()
|
||||
defer mock.Shutdown(t, 0x1d42cd75)
|
||||
errors := displaytest.TestTextDisplay(dev, false)
|
||||
for _, err := range errors {
|
||||
if err != display.ErrNotImplemented {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
if mock.bytesWritten == 27 {
|
||||
t.Error("27")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLEDs(t *testing.T) {
|
||||
dev, mock := getDisplay()
|
||||
defer mock.Shutdown(t, 0x3d9030ea)
|
||||
for ix := range 3 {
|
||||
for color := Off; color <= Yellow; color++ {
|
||||
err := dev.LED(ix, color)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Peform a basic test on optional interface methods.
|
||||
func TestContrastBacklight(t *testing.T) {
|
||||
dev, mock := getDisplay()
|
||||
defer mock.Shutdown(t, 0x93097842)
|
||||
if err := dev.Contrast(0); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := dev.Contrast(100); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := dev.Backlight(0); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := dev.Backlight(50); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := dev.KeypadBacklight(true); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := dev.KeypadBacklight(false); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPins(t *testing.T) {
|
||||
dev, _ := getDisplay()
|
||||
for ix, pin := range dev.Pins {
|
||||
if len(pin.String()) == 0 {
|
||||
t.Errorf("pin %d return empty string", ix)
|
||||
}
|
||||
if pin.Number() != ix+1 {
|
||||
t.Errorf("Pin %d, unexpected pin # %d", ix, pin.Number())
|
||||
}
|
||||
if len(pin.Name()) == 0 {
|
||||
t.Errorf("pin %d returned empty name!", ix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeypad(t *testing.T) {
|
||||
dev, _ := getDisplay()
|
||||
ch, err := dev.ReadKeypad()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ch == nil {
|
||||
t.Fatal("ReadKeypad() returned nil channel!")
|
||||
}
|
||||
received := atomic.Int32{}
|
||||
expected := int32(10)
|
||||
go func() {
|
||||
for {
|
||||
if received.Load() >= expected {
|
||||
|
||||
if err := dev.Halt(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}()
|
||||
for c := range ch {
|
||||
t.Logf("received %s", string(c))
|
||||
received.Add(1)
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
var _ io.Reader = &mockReadWriterCloser{}
|
||||
var _ io.Writer = &mockReadWriterCloser{}
|
||||
var _ io.Closer = &mockReadWriterCloser{}
|
||||
@ -0,0 +1,62 @@
|
||||
// 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 matrixorbital
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/physic"
|
||||
)
|
||||
|
||||
// A gpoPin is an output pin that can be toggled on the display. On the
|
||||
// LK204-7T-1U, there are six GPO pins wired to LEDs. On the Adafruit
|
||||
// USB LCD Backpack, there are four bare pins exposed. gpoPin implements
|
||||
// gpio.PinOut.
|
||||
type gpoPin struct {
|
||||
name string
|
||||
number int
|
||||
display *GPOEnabledDisplay
|
||||
}
|
||||
|
||||
// A generic routine to create our set of pins.
|
||||
func makePins(display *GPOEnabledDisplay, pins []gpio.PinOut) {
|
||||
for ix := range len(pins) {
|
||||
pin := &gpoPin{name: fmt.Sprintf("GPO%d", ix+1), number: ix + 1, display: display}
|
||||
pins[ix] = pin
|
||||
}
|
||||
}
|
||||
|
||||
func (pin *gpoPin) Name() string {
|
||||
return pin.name
|
||||
}
|
||||
|
||||
func (pin *gpoPin) Number() int {
|
||||
return pin.number
|
||||
}
|
||||
|
||||
func (pin *gpoPin) String() string {
|
||||
return fmt.Sprintf("matrixorbital Pin: Name: %s Number %d", pin.name, pin.number)
|
||||
}
|
||||
|
||||
func (pin *gpoPin) Halt() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pin *gpoPin) Out(l gpio.Level) error {
|
||||
d := *pin.display
|
||||
return d.GPO(pin.number, l)
|
||||
}
|
||||
|
||||
func (pin *gpoPin) Function() string {
|
||||
return "Out"
|
||||
}
|
||||
|
||||
func (pin *gpoPin) PWM(duty gpio.Duty, f physic.Frequency) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
var _ gpio.PinOut = &gpoPin{}
|
||||
Loading…
Reference in New Issue