diff --git a/bmi160/bmi160.go b/bmi160/bmi160.go index cd3e88237..a8712bfcb 100644 --- a/bmi160/bmi160.go +++ b/bmi160/bmi160.go @@ -1,17 +1,17 @@ package bmi160 import ( - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" ) // DeviceSPI is the SPI interface to a BMI160 accelerometer/gyroscope. There is // also an I2C interface, but it is not yet supported. type DeviceSPI struct { // Chip select pin - CSB machine.Pin + CSB drivers.PinOutput buf [7]byte @@ -22,9 +22,9 @@ type DeviceSPI struct { // 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 // using this device. -func NewSPI(csb machine.Pin, spi drivers.SPI) *DeviceSPI { +func NewSPI(csb drivers.PinOutput, spi drivers.SPI) *DeviceSPI { return &DeviceSPI{ - CSB: csb, // chip select + CSB: legacy.PinOutput(csb), // chip select Bus: spi, } } @@ -33,8 +33,7 @@ func NewSPI(csb machine.Pin, spi drivers.SPI) *DeviceSPI { // configures the BMI160, but it does not configure the SPI interface (it is // assumed to be up and running). func (d *DeviceSPI) Configure() error { - d.CSB.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.CSB.High() + d.CSB.Set(true) // The datasheet recommends doing a register read from address 0x7F to get // SPI communication going: @@ -86,9 +85,9 @@ func (d *DeviceSPI) ReadTemperature() (temperature int32, err error) { data[0] = 0x80 | reg_TEMPERATURE_0 data[1] = 0 data[2] = 0 - d.CSB.Low() + d.CSB.Set(false) err = d.Bus.Tx(data, data) - d.CSB.High() + d.CSB.Set(true) if err != nil { return } @@ -123,9 +122,9 @@ func (d *DeviceSPI) ReadAcceleration() (x int32, y int32, z int32, err error) { for i := 1; i < len(data); i++ { data[i] = 0 } - d.CSB.Low() + d.CSB.Set(false) err = d.Bus.Tx(data, data) - d.CSB.High() + d.CSB.Set(true) if err != nil { return } @@ -153,9 +152,9 @@ func (d *DeviceSPI) ReadRotation() (x int32, y int32, z int32, err error) { for i := 1; i < len(data); i++ { data[i] = 0 } - d.CSB.Low() + d.CSB.Set(false) err = d.Bus.Tx(data, data) - d.CSB.High() + d.CSB.Set(true) if err != nil { return } @@ -201,9 +200,9 @@ func (d *DeviceSPI) readRegister(address uint8) uint8 { data := d.buf[:2] data[0] = 0x80 | address data[1] = 0 - d.CSB.Low() + d.CSB.Set(false) d.Bus.Tx(data, data) - d.CSB.High() + d.CSB.Set(true) return data[1] } @@ -217,7 +216,7 @@ func (d *DeviceSPI) writeRegister(address, data uint8) { buf[0] = address buf[1] = data - d.CSB.Low() + d.CSB.Set(false) d.Bus.Tx(buf, buf) - d.CSB.High() + d.CSB.Set(true) } diff --git a/buzzer/buzzer.go b/buzzer/buzzer.go index 04c112953..bbface5f4 100644 --- a/buzzer/buzzer.go +++ b/buzzer/buzzer.go @@ -2,22 +2,22 @@ package buzzer // import "tinygo.org/x/drivers/buzzer" import ( - "machine" - "time" + + "tinygo.org/x/drivers" ) // Device wraps a GPIO connection to a buzzer. type Device struct { - pin machine.Pin + set drivers.PinSet High bool BPM float64 } // 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{ - pin: pin, + set: pin.Set, High: false, BPM: 96.0, } @@ -25,14 +25,14 @@ func New(pin machine.Pin) Device { // On sets the buzzer to a high state. func (l *Device) On() (err error) { - l.pin.Set(true) + l.set(true) l.High = true return } // Off sets the buzzer to a low state. func (l *Device) Off() (err error) { - l.pin.Set(false) + l.set(false) l.High = false return } diff --git a/dht/thermometer.go b/dht/thermometer.go index f9405aac6..4f6e07fcb 100644 --- a/dht/thermometer.go +++ b/dht/thermometer.go @@ -9,9 +9,10 @@ package dht // import "tinygo.org/x/drivers/dht" import ( - "machine" "runtime/interrupt" "time" + + "tinygo.org/x/drivers" ) // 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, // user can avoid any hidden calls to the sensor. type device struct { - pin machine.Pin + pin drivers.Pin measurements DeviceType initialized bool @@ -93,14 +94,12 @@ func (t *device) HumidityFloat() (float32, error) { // Perform initialization of the communication protocol. // Device lowers the voltage on pin for startingLow=20ms and starts listening for response // Section 5.2 in [1] -func initiateCommunication(p machine.Pin) { +func initiateCommunication(p drivers.Pin) { // Send low signal to the device - p.Configure(machine.PinConfig{Mode: machine.PinOutput}) - p.Low() + p.Set(false) time.Sleep(startingLow) // Set pin to high and wait for reply - p.High() - p.Configure(machine.PinConfig{Mode: machine.PinInput}) + p.Set(true) } // Measurements returns both measurements: temperature and humidity as they sent by the device. @@ -158,7 +157,7 @@ func (t *device) read() error { // receiveSignals counts number of low and high cycles. The execution is time critical, so the function disables // interrupts -func receiveSignals(pin machine.Pin, result []counter) { +func receiveSignals(pin drivers.PinInput, result []counter) { i := uint8(0) mask := interrupt.Disable() defer interrupt.Restore(mask) @@ -189,7 +188,7 @@ func (t *device) extractData(signals []counter, buf []uint8) error { // waitForDataTransmission waits for reply from the sensor. // If no reply received, returns NoSignalError. // 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 if expectChange(p, true) == timeout { return NoSignalError @@ -209,8 +208,8 @@ func waitForDataTransmission(p machine.Pin) error { // This device provides full control to the user. // It does not do any hidden measurements calls and does not check // for 2 seconds delay between measurements. -func NewDummyDevice(pin machine.Pin, deviceType DeviceType) DummyDevice { - pin.High() +func NewDummyDevice(pin drivers.Pin, deviceType DeviceType) DummyDevice { + pin.Set(true) return &device{ pin: pin, measurements: deviceType, diff --git a/dht/timesafethermometer.go b/dht/timesafethermometer.go index 87fab50c7..81e82bffd 100644 --- a/dht/timesafethermometer.go +++ b/dht/timesafethermometer.go @@ -9,8 +9,9 @@ package dht // import "tinygo.org/x/drivers/dht" import ( - "machine" "time" + + "tinygo.org/x/drivers" ) // Device interface provides main functionality of the DHTXX sensors. @@ -124,8 +125,8 @@ func (m *managedDevice) Configure(policy UpdatePolicy) { // Constructor of the Device implementation. // This implementation updates data every 2 seconds during data access. -func New(pin machine.Pin, deviceType DeviceType) Device { - pin.High() +func New(pin drivers.Pin, deviceType DeviceType) Device { + pin.Set(true) return &managedDevice{ t: device{ pin: pin, @@ -141,8 +142,8 @@ func New(pin machine.Pin, deviceType DeviceType) Device { } // Constructor of the Device implementation with given UpdatePolicy -func NewWithPolicy(pin machine.Pin, deviceType DeviceType, updatePolicy UpdatePolicy) Device { - pin.High() +func NewWithPolicy(pin drivers.Pin, deviceType DeviceType, updatePolicy UpdatePolicy) Device { + pin.Set(true) result := &managedDevice{ t: device{ pin: pin, diff --git a/dht/util.go b/dht/util.go index d2b1cc70b..0d750203a 100644 --- a/dht/util.go +++ b/dht/util.go @@ -3,21 +3,22 @@ package dht // import "tinygo.org/x/drivers/dht" import ( - "machine" "time" + + "tinygo.org/x/drivers" ) // Check if the pin is disabled -func powerUp(p machine.Pin) bool { +func powerUp(p drivers.Pin) bool { state := p.Get() if !state { - p.High() + p.Set(true) time.Sleep(startTimeout) } return state } -func expectChange(p machine.Pin, oldState bool) counter { +func expectChange(p drivers.PinInput, oldState bool) counter { cnt := counter(0) for ; p.Get() == oldState && cnt != timeout; cnt++ { } diff --git a/examples/dht/main.go b/examples/dht/main.go index 151258bf7..f6b987898 100644 --- a/examples/dht/main.go +++ b/examples/dht/main.go @@ -1,14 +1,18 @@ +//go:build baremetal && tinygo + package main import ( "fmt" "machine" "time" + "tinygo.org/x/drivers/dht" + "tinygo.org/x/drivers/tinygo" ) func main() { - pin := machine.D6 + pin := tinygo.New(machine.D6) dhtSensor := dht.New(pin, dht.DHT11) for { temp, hum, err := dhtSensor.Measurements() diff --git a/examples/hcsr04/main.go b/examples/hcsr04/main.go index 191bc0709..9bf976578 100644 --- a/examples/hcsr04/main.go +++ b/examples/hcsr04/main.go @@ -5,10 +5,13 @@ import ( "time" "tinygo.org/x/drivers/hcsr04" + "tinygo.org/x/drivers/tinygo" ) func main() { - sensor := hcsr04.New(machine.D10, machine.D9) + trigger := tinygo.New(machine.D10) // automatically configures pin as output + echo := tinygo.New(machine.D9) // automatically configures pin as input + sensor := hcsr04.New(trigger, echo) sensor.Configure() println("Ultrasonic starts") diff --git a/examples/rpio/main.go b/examples/rpio/main.go new file mode 100644 index 000000000..4531cf94f --- /dev/null +++ b/examples/rpio/main.go @@ -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) + } +} diff --git a/go.mod b/go.mod index 10c3e98a7..e48ae9324 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/orsinium-labs/tinymath v1.1.0 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/net v0.33.0 tinygo.org/x/tinyfont v0.3.0 diff --git a/go.sum b/go.sum index 6bb35574d..cf62ab3f5 100644 --- a/go.sum +++ b/go.sum @@ -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/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/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= 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= diff --git a/hcsr04/hcsr04.go b/hcsr04/hcsr04.go index 703fa7850..06f984f41 100644 --- a/hcsr04/hcsr04.go +++ b/hcsr04/hcsr04.go @@ -5,20 +5,22 @@ package hcsr04 import ( - "machine" "time" + + "tinygo.org/x/drivers" ) const TIMEOUT = 23324 // max sensing distance (4m) // Device holds the pins type Device struct { - trigger machine.Pin - echo machine.Pin + trigger drivers.PinOutput + echo drivers.PinInput } -// New returns a new ultrasonic driver given 2 pins -func New(trigger, echo machine.Pin) Device { +// New returns a new ultrasonic driver given 2 pins. +// Pins must be configured as output (trigger) and input (echo). +func New(trigger drivers.PinOutput, echo drivers.PinInput) Device { return Device{ trigger: trigger, echo: echo, @@ -27,8 +29,7 @@ func New(trigger, echo machine.Pin) Device { // Configure configures the pins of the Device func (d *Device) Configure() { - d.trigger.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.echo.Configure(machine.PinConfig{Mode: machine.PinInput}) + // no-op, left for API compatibility } // ReadDistance returns the distance of the object in mm @@ -45,11 +46,11 @@ func (d *Device) ReadDistance() int32 { // ReadPulse returns the time of the pulse (roundtrip) in microseconds func (d *Device) ReadPulse() int32 { t := time.Now() - d.trigger.Low() + d.trigger.Set(false) time.Sleep(2 * time.Microsecond) - d.trigger.High() + d.trigger.Set(true) time.Sleep(10 * time.Microsecond) - d.trigger.Low() + d.trigger.Set(false) i := uint8(0) for { if d.echo.Get() { diff --git a/internal/legacy/pinlegacy_generic.go b/internal/legacy/pinlegacy_generic.go new file mode 100644 index 000000000..8ca2a240d --- /dev/null +++ b/internal/legacy/pinlegacy_generic.go @@ -0,0 +1,9 @@ +//go:build !(baremetal && tinygo) + +package legacy + +import "tinygo.org/x/drivers" + +func PinOutput(pin drivers.PinOutput) drivers.PinOutput { + return pin +} diff --git a/internal/legacy/pinlegacy_tinygo.go b/internal/legacy/pinlegacy_tinygo.go new file mode 100644 index 000000000..209096f75 --- /dev/null +++ b/internal/legacy/pinlegacy_tinygo.go @@ -0,0 +1,16 @@ +//go:build baremetal && tinygo + +package legacy + +import ( + "machine" + + "tinygo.org/x/drivers" +) + +func PinOutput(pin drivers.PinOutput) drivers.PinOutput { + if p, ok := pin.(machine.Pin); ok { + p.Configure(machine.PinConfig{Mode: machine.PinOutput}) + } + return pin +} diff --git a/pin.go b/pin.go new file mode 100644 index 000000000..783d1cc2a --- /dev/null +++ b/pin.go @@ -0,0 +1,41 @@ +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. +// Client code is responsible for configuring pin modes correctly. +// This can be done either before passing the pin to a driver constructor +// or by ensuring correct pin mode is set when Get or Set methods are called. +// See rpio package for an example of a pin implementation that does this. +// +// Drivers that used to configure output pin mode in their constructors can use +// legacy.PinOutput() wrapper to keep the same behavior for machine.Pin. +// See internal/legacy/pinlegacy_tinygo.go and internal/legacy/pinlegacy_generic.go for details. +// +// All new drivers are encouraged to not configure pin modes in their constructors and +// do not depend on either machine package or legacy wrappers. + +// 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) diff --git a/rpio/pin.go b/rpio/pin.go new file mode 100644 index 000000000..369c0e52f --- /dev/null +++ b/rpio/pin.go @@ -0,0 +1,37 @@ +// Implementation of the Pin interface for the Raspberry Pi GPIO pins. +// Depends on the go-rpio library. +// Configures pin modes automatically when Get or Set methods are called. + +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) +} diff --git a/rpio/spi.go b/rpio/spi.go new file mode 100644 index 000000000..39fa5cc47 --- /dev/null +++ b/rpio/spi.go @@ -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 +} diff --git a/ssd1306/ssd1306_spi.go b/ssd1306/ssd1306_spi.go index d96299de5..83217f184 100644 --- a/ssd1306/ssd1306_spi.go +++ b/ssd1306/ssd1306_spi.go @@ -1,46 +1,43 @@ package ssd1306 import ( - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" ) type SPIBus struct { wire drivers.SPI - dcPin machine.Pin - resetPin machine.Pin - csPin machine.Pin + dcPin drivers.PinOutput + resetPin drivers.PinOutput + csPin drivers.PinOutput buffer []byte // buffer to avoid heap allocations } // NewSPI creates a new SSD1306 connection. The SPI wire must already be configured. -func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin machine.Pin) *Device { - dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) +func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin drivers.PinOutput) *Device { return &Device{ bus: &SPIBus{ wire: bus, - dcPin: dcPin, - resetPin: resetPin, - csPin: csPin, + dcPin: legacy.PinOutput(dcPin), + resetPin: legacy.PinOutput(resetPin), + csPin: legacy.PinOutput(csPin), }, } } // configure pins with the SPI bus and allocate the buffer func (b *SPIBus) configure(address uint16, size int16) []byte { - b.csPin.Low() - b.dcPin.Low() - b.resetPin.Low() + b.csPin.Set(false) + b.dcPin.Set(false) + b.resetPin.Set(false) - b.resetPin.High() + b.resetPin.Set(true) time.Sleep(1 * time.Millisecond) - b.resetPin.Low() + b.resetPin.Set(false) time.Sleep(10 * time.Millisecond) - b.resetPin.High() + b.resetPin.Set(true) b.buffer = make([]byte, size+1) // +1 for a command return b.buffer[1:] // return the image buffer @@ -59,10 +56,10 @@ func (b *SPIBus) flush() error { // tx sends data to the display func (b *SPIBus) tx(data []byte, isCommand bool) error { - b.csPin.High() + b.csPin.Set(true) b.dcPin.Set(!isCommand) - b.csPin.Low() + b.csPin.Set(false) err := b.wire.Tx(data, nil) - b.csPin.High() + b.csPin.Set(true) return err } diff --git a/st7735/st7735.go b/st7735/st7735.go index 6f15781c2..0b85e9e46 100644 --- a/st7735/st7735.go +++ b/st7735/st7735.go @@ -5,12 +5,12 @@ package st7735 // import "tinygo.org/x/drivers/st7735" import ( "image/color" - "machine" "time" "errors" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" "tinygo.org/x/drivers/pixel" ) @@ -39,10 +39,10 @@ type Device = DeviceOf[pixel.RGB565BE] // formats. type DeviceOf[T Color] struct { bus drivers.SPI - dcPin machine.Pin - resetPin machine.Pin - csPin machine.Pin - blPin machine.Pin + dcPin drivers.PinOutput + resetPin drivers.PinOutput + csPin drivers.PinOutput + blPin drivers.PinOutput width int16 height int16 columnOffset int16 @@ -65,23 +65,19 @@ type Config struct { } // 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) } // NewOf creates a new ST7735 connection with a particular pixel format. The SPI // wire must already be configured. -func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) 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}) +func NewOf[T Color](bus drivers.SPI, resetPin, dcPin, csPin, blPin drivers.PinOutput) DeviceOf[T] { return DeviceOf[T]{ bus: bus, - dcPin: dcPin, - resetPin: resetPin, - csPin: csPin, - blPin: blPin, + dcPin: legacy.PinOutput(dcPin), + resetPin: legacy.PinOutput(resetPin), + csPin: legacy.PinOutput(csPin), + blPin: legacy.PinOutput(blPin), } } @@ -114,11 +110,11 @@ func (d *DeviceOf[T]) Configure(cfg Config) { d.batchData = pixel.NewImage[T](int(d.batchLength), 1) // reset the device - d.resetPin.High() + d.resetPin.Set(true) time.Sleep(5 * time.Millisecond) - d.resetPin.Low() + d.resetPin.Set(false) time.Sleep(20 * time.Millisecond) - d.resetPin.High() + d.resetPin.Set(true) time.Sleep(150 * time.Millisecond) // Common initialization @@ -226,7 +222,7 @@ func (d *DeviceOf[T]) Configure(cfg Config) { 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 @@ -438,9 +434,9 @@ func (d *DeviceOf[T]) Size() (w, h int16) { // EnableBacklight enables or disables the backlight func (d *DeviceOf[T]) EnableBacklight(enable bool) { if enable { - d.blPin.High() + d.blPin.Set(true) } else { - d.blPin.Low() + d.blPin.Set(false) } } diff --git a/tinygo/pin.go b/tinygo/pin.go new file mode 100644 index 000000000..363001b97 --- /dev/null +++ b/tinygo/pin.go @@ -0,0 +1,39 @@ +//go:build baremetal && tinygo + +package tinygo // import "tinygo.org/x/drivers/tinygo" + +import "machine" + +// tinygo.Pin wraps machine.Pin to ensure correct pin mode is set when Get or Set methods are called. + +type Pin struct { + pin machine.Pin + mode machine.PinMode + + modeSet bool + inputMode machine.PinMode +} + +func New(pin machine.Pin) *Pin { + return &Pin{pin: pin, inputMode: machine.PinInput} +} + +func NewWithInputMode(pin machine.Pin, inputMode machine.PinMode) *Pin { + return &Pin{pin: pin, inputMode: inputMode} +} + +func (p *Pin) Get() bool { + if !p.modeSet || p.mode != p.inputMode { + p.pin.Configure(machine.PinConfig{Mode: p.inputMode}) + p.mode = machine.PinInput + } + return p.pin.Get() +} + +func (p *Pin) 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) +}