Pin HAL to break dependency of drivers from TinyGo's machine package

This commit is contained in:
Yurii Soldak
2025-09-21 04:38:21 +02:00
parent 3fa08112db
commit c6d83b783f
15 changed files with 328 additions and 88 deletions
+14 -16
View File
@@ -1,7 +1,6 @@
package bmi160 package bmi160
import ( import (
"machine"
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
@@ -11,7 +10,7 @@ import (
// also an I2C interface, but it is not yet supported. // also an I2C interface, but it is not yet supported.
type DeviceSPI struct { type DeviceSPI struct {
// Chip select pin // Chip select pin
CSB machine.Pin CSB drivers.PinOutput
buf [7]byte buf [7]byte
@@ -22,9 +21,9 @@ type DeviceSPI struct {
// NewSPI returns a new device driver. The pin and SPI interface are not // NewSPI returns a new device driver. The pin and SPI interface are not
// touched, provide a fully configured SPI object and call Configure to start // touched, provide a fully configured SPI object and call Configure to start
// using this device. // using this device.
func NewSPI(csb machine.Pin, spi drivers.SPI) *DeviceSPI { func NewSPI(csb drivers.PinOutput, spi drivers.SPI) *DeviceSPI {
return &DeviceSPI{ return &DeviceSPI{
CSB: csb, // chip select CSB: drivers.SafePinOutput(csb), // chip select
Bus: spi, Bus: spi,
} }
} }
@@ -33,8 +32,7 @@ func NewSPI(csb machine.Pin, spi drivers.SPI) *DeviceSPI {
// configures the BMI160, but it does not configure the SPI interface (it is // configures the BMI160, but it does not configure the SPI interface (it is
// assumed to be up and running). // assumed to be up and running).
func (d *DeviceSPI) Configure() error { func (d *DeviceSPI) Configure() error {
d.CSB.Configure(machine.PinConfig{Mode: machine.PinOutput}) d.CSB.Set(true)
d.CSB.High()
// The datasheet recommends doing a register read from address 0x7F to get // The datasheet recommends doing a register read from address 0x7F to get
// SPI communication going: // SPI communication going:
@@ -86,9 +84,9 @@ func (d *DeviceSPI) ReadTemperature() (temperature int32, err error) {
data[0] = 0x80 | reg_TEMPERATURE_0 data[0] = 0x80 | reg_TEMPERATURE_0
data[1] = 0 data[1] = 0
data[2] = 0 data[2] = 0
d.CSB.Low() d.CSB.Set(false)
err = d.Bus.Tx(data, data) err = d.Bus.Tx(data, data)
d.CSB.High() d.CSB.Set(true)
if err != nil { if err != nil {
return return
} }
@@ -123,9 +121,9 @@ func (d *DeviceSPI) ReadAcceleration() (x int32, y int32, z int32, err error) {
for i := 1; i < len(data); i++ { for i := 1; i < len(data); i++ {
data[i] = 0 data[i] = 0
} }
d.CSB.Low() d.CSB.Set(false)
err = d.Bus.Tx(data, data) err = d.Bus.Tx(data, data)
d.CSB.High() d.CSB.Set(true)
if err != nil { if err != nil {
return return
} }
@@ -153,9 +151,9 @@ func (d *DeviceSPI) ReadRotation() (x int32, y int32, z int32, err error) {
for i := 1; i < len(data); i++ { for i := 1; i < len(data); i++ {
data[i] = 0 data[i] = 0
} }
d.CSB.Low() d.CSB.Set(false)
err = d.Bus.Tx(data, data) err = d.Bus.Tx(data, data)
d.CSB.High() d.CSB.Set(true)
if err != nil { if err != nil {
return return
} }
@@ -201,9 +199,9 @@ func (d *DeviceSPI) readRegister(address uint8) uint8 {
data := d.buf[:2] data := d.buf[:2]
data[0] = 0x80 | address data[0] = 0x80 | address
data[1] = 0 data[1] = 0
d.CSB.Low() d.CSB.Set(false)
d.Bus.Tx(data, data) d.Bus.Tx(data, data)
d.CSB.High() d.CSB.Set(true)
return data[1] return data[1]
} }
@@ -217,7 +215,7 @@ func (d *DeviceSPI) writeRegister(address, data uint8) {
buf[0] = address buf[0] = address
buf[1] = data buf[1] = data
d.CSB.Low() d.CSB.Set(false)
d.Bus.Tx(buf, buf) d.Bus.Tx(buf, buf)
d.CSB.High() d.CSB.Set(true)
} }
+7 -7
View File
@@ -2,22 +2,22 @@
package buzzer // import "tinygo.org/x/drivers/buzzer" package buzzer // import "tinygo.org/x/drivers/buzzer"
import ( import (
"machine"
"time" "time"
"tinygo.org/x/drivers"
) )
// Device wraps a GPIO connection to a buzzer. // Device wraps a GPIO connection to a buzzer.
type Device struct { type Device struct {
pin machine.Pin set drivers.PinSet
High bool High bool
BPM float64 BPM float64
} }
// New returns a new buzzer driver given which pin to use // New returns a new buzzer driver given which pin to use
func New(pin machine.Pin) Device { func New(pin drivers.PinOutput) Device {
return Device{ return Device{
pin: pin, set: drivers.SafePinOutput(pin).Set,
High: false, High: false,
BPM: 96.0, BPM: 96.0,
} }
@@ -25,14 +25,14 @@ func New(pin machine.Pin) Device {
// On sets the buzzer to a high state. // On sets the buzzer to a high state.
func (l *Device) On() (err error) { func (l *Device) On() (err error) {
l.pin.Set(true) l.set(true)
l.High = true l.High = true
return return
} }
// Off sets the buzzer to a low state. // Off sets the buzzer to a low state.
func (l *Device) Off() (err error) { func (l *Device) Off() (err error) {
l.pin.Set(false) l.set(false)
l.High = false l.High = false
return return
} }
+14 -13
View File
@@ -9,9 +9,10 @@
package dht // import "tinygo.org/x/drivers/dht" package dht // import "tinygo.org/x/drivers/dht"
import ( import (
"machine"
"runtime/interrupt" "runtime/interrupt"
"time" "time"
"tinygo.org/x/drivers"
) )
// DummyDevice provides a basic interface for DHT devices. // DummyDevice provides a basic interface for DHT devices.
@@ -30,7 +31,7 @@ type DummyDevice interface {
// Since taking measurements from the sensor is time consuming procedure and blocks interrupts, // Since taking measurements from the sensor is time consuming procedure and blocks interrupts,
// user can avoid any hidden calls to the sensor. // user can avoid any hidden calls to the sensor.
type device struct { type device struct {
pin machine.Pin pin drivers.Pin
measurements DeviceType measurements DeviceType
initialized bool initialized bool
@@ -44,7 +45,9 @@ type device struct {
func (t *device) ReadMeasurements() error { func (t *device) ReadMeasurements() error {
// initial waiting // initial waiting
state := powerUp(t.pin) state := powerUp(t.pin)
defer t.pin.Set(state) defer func() {
t.pin.Set(state)
}()
err := t.read() err := t.read()
if err == nil { if err == nil {
t.initialized = true t.initialized = true
@@ -93,14 +96,12 @@ func (t *device) HumidityFloat() (float32, error) {
// Perform initialization of the communication protocol. // Perform initialization of the communication protocol.
// Device lowers the voltage on pin for startingLow=20ms and starts listening for response // Device lowers the voltage on pin for startingLow=20ms and starts listening for response
// Section 5.2 in [1] // Section 5.2 in [1]
func initiateCommunication(p machine.Pin) { func initiateCommunication(p drivers.Pin) {
// Send low signal to the device // Send low signal to the device
p.Configure(machine.PinConfig{Mode: machine.PinOutput}) p.Set(false)
p.Low()
time.Sleep(startingLow) time.Sleep(startingLow)
// Set pin to high and wait for reply // Set pin to high and wait for reply
p.High() p.Set(true)
p.Configure(machine.PinConfig{Mode: machine.PinInput})
} }
// Measurements returns both measurements: temperature and humidity as they sent by the device. // Measurements returns both measurements: temperature and humidity as they sent by the device.
@@ -158,7 +159,7 @@ func (t *device) read() error {
// receiveSignals counts number of low and high cycles. The execution is time critical, so the function disables // receiveSignals counts number of low and high cycles. The execution is time critical, so the function disables
// interrupts // interrupts
func receiveSignals(pin machine.Pin, result []counter) { func receiveSignals(pin drivers.PinInput, result []counter) {
i := uint8(0) i := uint8(0)
mask := interrupt.Disable() mask := interrupt.Disable()
defer interrupt.Restore(mask) defer interrupt.Restore(mask)
@@ -189,7 +190,7 @@ func (t *device) extractData(signals []counter, buf []uint8) error {
// waitForDataTransmission waits for reply from the sensor. // waitForDataTransmission waits for reply from the sensor.
// If no reply received, returns NoSignalError. // If no reply received, returns NoSignalError.
// For more details, see section 5.2 in [1] // For more details, see section 5.2 in [1]
func waitForDataTransmission(p machine.Pin) error { func waitForDataTransmission(p drivers.PinInput) error {
// wait for thermometer to pull down // wait for thermometer to pull down
if expectChange(p, true) == timeout { if expectChange(p, true) == timeout {
return NoSignalError return NoSignalError
@@ -209,10 +210,10 @@ func waitForDataTransmission(p machine.Pin) error {
// This device provides full control to the user. // This device provides full control to the user.
// It does not do any hidden measurements calls and does not check // It does not do any hidden measurements calls and does not check
// for 2 seconds delay between measurements. // for 2 seconds delay between measurements.
func NewDummyDevice(pin machine.Pin, deviceType DeviceType) DummyDevice { func NewDummyDevice(pin drivers.Pin, deviceType DeviceType) DummyDevice {
pin.High() pin.Set(true)
return &device{ return &device{
pin: pin, pin: drivers.SafePin(pin),
measurements: deviceType, measurements: deviceType,
initialized: false, initialized: false,
temperature: 0, temperature: 0,
+8 -7
View File
@@ -9,8 +9,9 @@
package dht // import "tinygo.org/x/drivers/dht" package dht // import "tinygo.org/x/drivers/dht"
import ( import (
"machine"
"time" "time"
"tinygo.org/x/drivers"
) )
// Device interface provides main functionality of the DHTXX sensors. // Device interface provides main functionality of the DHTXX sensors.
@@ -124,11 +125,11 @@ func (m *managedDevice) Configure(policy UpdatePolicy) {
// Constructor of the Device implementation. // Constructor of the Device implementation.
// This implementation updates data every 2 seconds during data access. // This implementation updates data every 2 seconds during data access.
func New(pin machine.Pin, deviceType DeviceType) Device { func New(pin drivers.Pin, deviceType DeviceType) Device {
pin.High() pin.Set(true)
return &managedDevice{ return &managedDevice{
t: device{ t: device{
pin: pin, pin: drivers.SafePin(pin),
measurements: deviceType, measurements: deviceType,
initialized: false, initialized: false,
}, },
@@ -141,11 +142,11 @@ func New(pin machine.Pin, deviceType DeviceType) Device {
} }
// Constructor of the Device implementation with given UpdatePolicy // Constructor of the Device implementation with given UpdatePolicy
func NewWithPolicy(pin machine.Pin, deviceType DeviceType, updatePolicy UpdatePolicy) Device { func NewWithPolicy(pin drivers.Pin, deviceType DeviceType, updatePolicy UpdatePolicy) Device {
pin.High() pin.Set(true)
result := &managedDevice{ result := &managedDevice{
t: device{ t: device{
pin: pin, pin: drivers.SafePin(pin),
measurements: deviceType, measurements: deviceType,
initialized: false, initialized: false,
}, },
+5 -4
View File
@@ -3,21 +3,22 @@
package dht // import "tinygo.org/x/drivers/dht" package dht // import "tinygo.org/x/drivers/dht"
import ( import (
"machine"
"time" "time"
"tinygo.org/x/drivers"
) )
// Check if the pin is disabled // Check if the pin is disabled
func powerUp(p machine.Pin) bool { func powerUp(p drivers.Pin) bool {
state := p.Get() state := p.Get()
if !state { if !state {
p.High() p.Set(true)
time.Sleep(startTimeout) time.Sleep(startTimeout)
} }
return state return state
} }
func expectChange(p machine.Pin, oldState bool) counter { func expectChange(p drivers.PinInput, oldState bool) counter {
cnt := counter(0) cnt := counter(0)
for ; p.Get() == oldState && cnt != timeout; cnt++ { for ; p.Get() == oldState && cnt != timeout; cnt++ {
} }
+68
View File
@@ -0,0 +1,68 @@
package main
// Example program for the ST7735 display (Waveshare 1.44" LCD HAT) using the rpio driver on a Raspberry Pi.
import (
"fmt"
"image/color"
"time"
go_rpio "github.com/stianeikeland/go-rpio/v4"
"tinygo.org/x/drivers/rpio"
"tinygo.org/x/drivers/st7735"
)
var (
black = color.RGBA{0, 0, 0, 255}
colors = [...]color.RGBA{
{255, 0, 0, 255}, // red
{0, 255, 0, 255}, // green
{0, 0, 255, 255}, // blue
{255, 255, 0, 255}, // yellow
}
)
func main() {
if err := go_rpio.Open(); err != nil {
fmt.Println("Error opening GPIO:", err)
return
}
defer go_rpio.Close()
// Initialize SPI and pins
spi := rpio.NewSPI()
resetPin := rpio.NewPin(27)
dcPin := rpio.NewPin(25)
csPin := rpio.NewPin(8)
blPin := rpio.NewPin(24)
// Initialize display
device := st7735.New(spi, resetPin, dcPin, csPin, blPin)
device.Configure(st7735.Config{
Width: 128,
Height: 128,
Model: st7735.GREENTAB,
RowOffset: 3,
ColumnOffset: 2,
})
device.InvertColors(false)
device.EnableBacklight(true)
device.IsBGR(true) // no effect w/o rotation!
device.SetRotation(st7735.NO_ROTATION)
width, height := device.Size()
// Clear display
device.FillScreen(black)
// Draw rectangles in a loop, clockwise rotation of colors
pos := 0
for {
device.FillRectangle(0, 0, width/2, height/2, colors[(pos+0)%len(colors)]) // top left
device.FillRectangle(0, height/2, width/2, height/2, colors[(pos+1)%len(colors)]) // bottom left
device.FillRectangle(width/2, height/2, width/2, height/2, colors[(pos+2)%len(colors)]) // bottom right
device.FillRectangle(width/2, 0, width/2, height/2, colors[(pos+3)%len(colors)]) // top right
pos++
time.Sleep(1 * time.Second)
}
}
+1
View File
@@ -12,6 +12,7 @@ require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/orsinium-labs/tinymath v1.1.0 github.com/orsinium-labs/tinymath v1.1.0
github.com/soypat/natiu-mqtt v0.5.1 github.com/soypat/natiu-mqtt v0.5.1
github.com/stianeikeland/go-rpio/v4 v4.6.0
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
golang.org/x/net v0.33.0 golang.org/x/net v0.33.0
tinygo.org/x/tinyfont v0.3.0 tinygo.org/x/tinyfont v0.3.0
+2
View File
@@ -17,6 +17,8 @@ github.com/orsinium-labs/tinymath v1.1.0 h1:KomdsyLHB7vE3f1nRAJF2dyf1m/gnM2HxfTe
github.com/orsinium-labs/tinymath v1.1.0/go.mod h1:WPXX6ei3KSXG7JfA03a+ekCYaY9SWN4I+JRl2p6ck+A= github.com/orsinium-labs/tinymath v1.1.0/go.mod h1:WPXX6ei3KSXG7JfA03a+ekCYaY9SWN4I+JRl2p6ck+A=
github.com/soypat/natiu-mqtt v0.5.1 h1:rwaDmlvjzD2+3MCOjMZc4QEkDkNwDzbct2TJbpz+TPc= github.com/soypat/natiu-mqtt v0.5.1 h1:rwaDmlvjzD2+3MCOjMZc4QEkDkNwDzbct2TJbpz+TPc=
github.com/soypat/natiu-mqtt v0.5.1/go.mod h1:xEta+cwop9izVCW7xOx2W+ct9PRMqr0gNVkvBPnQTc4= github.com/soypat/natiu-mqtt v0.5.1/go.mod h1:xEta+cwop9izVCW7xOx2W+ct9PRMqr0gNVkvBPnQTc4=
github.com/stianeikeland/go-rpio/v4 v4.6.0 h1:eAJgtw3jTtvn/CqwbC82ntcS+dtzUTgo5qlZKe677EY=
github.com/stianeikeland/go-rpio/v4 v4.6.0/go.mod h1:A3GvHxC1Om5zaId+HqB3HKqx4K/AqeckxB7qRjxMK7o=
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0= golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
+37
View File
@@ -0,0 +1,37 @@
package drivers
// Pin types abstract the underlying implementation of a pin,
// allowing the use of different hardware or libraries without changing driver code.
//
// Note, pin mode functionality is not part of the Pin interface.
// Implementations must ensure correct pin mode is set when Get or Set methods are called.
//
// Drivers must use SafePin(), SafePinInput() and SafePinOutput() wrappers in constructors.
// The client code can pass either machine.Pin or a custom implementation of the Pin interface.
// Wrappers for TinyGo's machine.Pin are provided in pin_tinygo.go.
// It's custom implementation's responsibility to configure the pin modes correctly as
// Wrappers in pin_generic.go are no-ops.
// PinInput is an interface for reading the state of a pin.
type PinInput interface {
Get() bool
}
// PinOutput is an interface for setting the state of a pin.
type PinOutput interface {
Set(bool)
}
// Pin is an interface for a pin that can be both input and output.
type Pin interface {
PinInput
PinOutput
}
// PinGet is a function type for getting the state of a pin
// Shall be used in high-performance drivers to avoid interface call overhead.
type PinGet func() bool
// PinSet is a function type for setting the state of a pin.
// Shall be used in high-performance drivers to avoid interface call overhead.
type PinSet func(bool)
+18
View File
@@ -0,0 +1,18 @@
//go:build !tinygo
// Generic no-op implementations of SafePin, SafePinInput and SafePinOutput.
// It's custom implementation's responsibility to configure the pin modes correctly when needed.
package drivers
func SafePinInput(pin PinInput) PinInput {
return pin
}
func SafePinOutput(pin PinOutput) PinOutput {
return pin
}
func SafePin(pin Pin) Pin {
return pin
}
+57
View File
@@ -0,0 +1,57 @@
//go:build tinygo
// Implementation of the Pin interface for tinygo (baremetal), depends on "machine" package.
// SafePin, SafePinInput and SafePinOutput wrappers must be used in constructors of drivers.
package drivers
import (
"machine"
)
// MachinePin wraps machine.Pin to ensure correct pin mode is set when Get or Set methods are called.
type MachinePin struct {
pin machine.Pin
mode machine.PinMode
modeSet bool
}
func (p *MachinePin) Get() bool {
if !p.modeSet || p.mode != machine.PinInput {
p.pin.Configure(machine.PinConfig{Mode: machine.PinInput})
p.mode = machine.PinInput
}
return p.pin.Get()
}
func (p *MachinePin) Set(high bool) {
if !p.modeSet || p.mode != machine.PinOutput {
p.pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
p.mode = machine.PinOutput
}
p.pin.Set(high)
}
// Wrappers for Pin, PinInput and PinOutput interfaces that convert machine.Pin to safe MachinePin.
func SafePinInput(pin PinInput) PinInput {
if p, ok := pin.(machine.Pin); ok {
return &MachinePin{pin: p}
}
return pin
}
func SafePinOutput(pin PinOutput) PinOutput {
if p, ok := pin.(machine.Pin); ok {
return &MachinePin{pin: p}
}
return pin
}
func SafePin(pin Pin) Pin {
if p, ok := pin.(machine.Pin); ok {
return &MachinePin{pin: p}
}
return pin
}
+35
View File
@@ -0,0 +1,35 @@
// Implementation of the Pin interface for the Raspberry Pi GPIO pins.
package rpio // import "tinygo.org/x/drivers/rpio"
import go_rpio "github.com/stianeikeland/go-rpio/v4"
type Pin struct {
modeSet bool
Mode go_rpio.Mode
Pin go_rpio.Pin
}
func NewPin(pinNumber int) *Pin {
return &Pin{Pin: go_rpio.Pin(pinNumber)}
}
func (p *Pin) Get() bool {
if !p.modeSet || p.Mode != go_rpio.Input {
p.Pin.Input()
p.Mode = go_rpio.Input
}
return p.Pin.Read() == go_rpio.High
}
func (p *Pin) Set(high bool) {
if !p.modeSet || p.Mode != go_rpio.Output {
p.Pin.Output()
p.Mode = go_rpio.Output
}
state := go_rpio.Low
if high {
state = go_rpio.High
}
p.Pin.Write(state)
}
+30
View File
@@ -0,0 +1,30 @@
package rpio // import "tinygo.org/x/drivers/rpio"
import go_rpio "github.com/stianeikeland/go-rpio/v4"
type SPI struct {
}
func NewSPI() (s *SPI) {
if err := go_rpio.SpiBegin(go_rpio.Spi0); err != nil {
panic(err)
}
go_rpio.SpiSpeed(25_000_000) // 25 MHz
go_rpio.SpiChipSelect(0)
return &SPI{}
}
func (s *SPI) Tx(w, r []byte) error {
data := make([]byte, len(w))
copy(data, w)
go_rpio.SpiExchange(data)
copy(r, data)
return nil
}
func (s *SPI) Transfer(b byte) (byte, error) {
w := []byte{b}
r := make([]byte, len(w))
err := s.Tx(w, r)
return r[0], err
}
+16 -20
View File
@@ -1,7 +1,6 @@
package ssd1306 package ssd1306
import ( import (
"machine"
"time" "time"
"tinygo.org/x/drivers" "tinygo.org/x/drivers"
@@ -9,38 +8,35 @@ import (
type SPIBus struct { type SPIBus struct {
wire drivers.SPI wire drivers.SPI
dcPin machine.Pin dcPin drivers.PinOutput
resetPin machine.Pin resetPin drivers.PinOutput
csPin machine.Pin csPin drivers.PinOutput
buffer []byte // buffer to avoid heap allocations buffer []byte // buffer to avoid heap allocations
} }
// NewSPI creates a new SSD1306 connection. The SPI wire must already be configured. // NewSPI creates a new SSD1306 connection. The SPI wire must already be configured.
func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin machine.Pin) *Device { func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin drivers.PinOutput) *Device {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
return &Device{ return &Device{
bus: &SPIBus{ bus: &SPIBus{
wire: bus, wire: bus,
dcPin: dcPin, dcPin: drivers.SafePinOutput(dcPin),
resetPin: resetPin, resetPin: drivers.SafePinOutput(resetPin),
csPin: csPin, csPin: drivers.SafePinOutput(csPin),
}, },
} }
} }
// configure pins with the SPI bus and allocate the buffer // configure pins with the SPI bus and allocate the buffer
func (b *SPIBus) configure(address uint16, size int16) []byte { func (b *SPIBus) configure(address uint16, size int16) []byte {
b.csPin.Low() b.csPin.Set(false)
b.dcPin.Low() b.dcPin.Set(false)
b.resetPin.Low() b.resetPin.Set(false)
b.resetPin.High() b.resetPin.Set(true)
time.Sleep(1 * time.Millisecond) time.Sleep(1 * time.Millisecond)
b.resetPin.Low() b.resetPin.Set(false)
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
b.resetPin.High() b.resetPin.Set(true)
b.buffer = make([]byte, size+1) // +1 for a command b.buffer = make([]byte, size+1) // +1 for a command
return b.buffer[1:] // return the image buffer return b.buffer[1:] // return the image buffer
@@ -59,10 +55,10 @@ func (b *SPIBus) flush() error {
// tx sends data to the display // tx sends data to the display
func (b *SPIBus) tx(data []byte, isCommand bool) error { func (b *SPIBus) tx(data []byte, isCommand bool) error {
b.csPin.High() b.csPin.Set(true)
b.dcPin.Set(!isCommand) b.dcPin.Set(!isCommand)
b.csPin.Low() b.csPin.Set(false)
err := b.wire.Tx(data, nil) err := b.wire.Tx(data, nil)
b.csPin.High() b.csPin.Set(true)
return err return err
} }
+16 -21
View File
@@ -5,7 +5,6 @@ package st7735 // import "tinygo.org/x/drivers/st7735"
import ( import (
"image/color" "image/color"
"machine"
"time" "time"
"errors" "errors"
@@ -39,10 +38,10 @@ type Device = DeviceOf[pixel.RGB565BE]
// formats. // formats.
type DeviceOf[T Color] struct { type DeviceOf[T Color] struct {
bus drivers.SPI bus drivers.SPI
dcPin machine.Pin dcPin drivers.PinOutput
resetPin machine.Pin resetPin drivers.PinOutput
csPin machine.Pin csPin drivers.PinOutput
blPin machine.Pin blPin drivers.PinOutput
width int16 width int16
height int16 height int16
columnOffset int16 columnOffset int16
@@ -65,23 +64,19 @@ type Config struct {
} }
// New creates a new ST7735 connection. The SPI wire must already be configured. // New creates a new ST7735 connection. The SPI wire must already be configured.
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device { func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin drivers.PinOutput) Device {
return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin) return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin)
} }
// NewOf creates a new ST7735 connection with a particular pixel format. The SPI // NewOf creates a new ST7735 connection with a particular pixel format. The SPI
// wire must already be configured. // wire must already be configured.
func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) DeviceOf[T] { func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin drivers.PinOutput) DeviceOf[T] {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
blPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
return DeviceOf[T]{ return DeviceOf[T]{
bus: bus, bus: bus,
dcPin: dcPin, dcPin: drivers.SafePinOutput(dcPin),
resetPin: resetPin, resetPin: drivers.SafePinOutput(resetPin),
csPin: csPin, csPin: drivers.SafePinOutput(csPin),
blPin: blPin, blPin: drivers.SafePinOutput(blPin),
} }
} }
@@ -114,11 +109,11 @@ func (d *DeviceOf[T]) Configure(cfg Config) {
d.batchData = pixel.NewImage[T](int(d.batchLength), 1) d.batchData = pixel.NewImage[T](int(d.batchLength), 1)
// reset the device // reset the device
d.resetPin.High() d.resetPin.Set(true)
time.Sleep(5 * time.Millisecond) time.Sleep(5 * time.Millisecond)
d.resetPin.Low() d.resetPin.Set(false)
time.Sleep(20 * time.Millisecond) time.Sleep(20 * time.Millisecond)
d.resetPin.High() d.resetPin.Set(true)
time.Sleep(150 * time.Millisecond) time.Sleep(150 * time.Millisecond)
// Common initialization // Common initialization
@@ -226,7 +221,7 @@ func (d *DeviceOf[T]) Configure(cfg Config) {
d.SetRotation(d.rotation) d.SetRotation(d.rotation)
d.blPin.High() d.blPin.Set(true)
} }
// Display does nothing, there's no buffer as it might be too big for some boards // Display does nothing, there's no buffer as it might be too big for some boards
@@ -438,9 +433,9 @@ func (d *DeviceOf[T]) Size() (w, h int16) {
// EnableBacklight enables or disables the backlight // EnableBacklight enables or disables the backlight
func (d *DeviceOf[T]) EnableBacklight(enable bool) { func (d *DeviceOf[T]) EnableBacklight(enable bool) {
if enable { if enable {
d.blPin.High() d.blPin.Set(true)
} else { } else {
d.blPin.Low() d.blPin.Set(false)
} }
} }