diff --git a/apa102/apa102.go b/apa102/apa102.go index 98253bfdf..0cc22e0c5 100644 --- a/apa102/apa102.go +++ b/apa102/apa102.go @@ -5,9 +5,10 @@ package apa102 // import "tinygo.org/x/drivers/apa102" import ( "image/color" - "machine" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" ) const ( @@ -37,8 +38,11 @@ func New(b drivers.SPI) *Device { // NewSoftwareSPI returns a new APA102 driver that will use a software based // implementation of the SPI protocol. -func NewSoftwareSPI(sckPin, sdoPin machine.Pin, delay uint32) *Device { - return New(&bbSPI{SCK: sckPin, SDO: sdoPin, Delay: delay}) +func NewSoftwareSPI(sckPin, sdoPin pin.Output, delay uint32) *Device { + return New(&bbSPI{SCK: sckPin.Set, SDO: sdoPin.Set, Delay: delay, configurePins: func() { + legacy.ConfigurePinOut(sckPin) + legacy.ConfigurePinOut(sdoPin) + }}) } // WriteColors writes the given RGBA color slice out using the APA102 protocol. diff --git a/apa102/softspi.go b/apa102/softspi.go index 89e8a8587..f221d4784 100644 --- a/apa102/softspi.go +++ b/apa102/softspi.go @@ -1,6 +1,9 @@ package apa102 -import "machine" +import ( + "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" +) // bbSPI is a dumb bit-bang implementation of SPI protocol that is hardcoded // to mode 0 and ignores trying to receive data. Just enough for the APA102. @@ -8,15 +11,18 @@ import "machine" // most purposes other than the APA102 package. It might be desirable to make // this more generic and include it in the TinyGo "machine" package instead. type bbSPI struct { - SCK machine.Pin - SDO machine.Pin - Delay uint32 + SCK drivers.PinOutput + SDO drivers.PinOutput + Delay uint32 + configurePins func() } // Configure sets up the SCK and SDO pins as outputs and sets them low func (s *bbSPI) Configure() { - s.SCK.Configure(machine.PinConfig{Mode: machine.PinOutput}) - s.SDO.Configure(machine.PinConfig{Mode: machine.PinOutput}) + if s.configurePins == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } + s.configurePins() s.SCK.Low() s.SDO.Low() if s.Delay == 0 { diff --git a/bmi160/bmi160.go b/bmi160/bmi160.go index cd3e88237..2ea0b011d 100644 --- a/bmi160/bmi160.go +++ b/bmi160/bmi160.go @@ -1,31 +1,36 @@ package bmi160 import ( - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" ) // 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 // SPI bus (requires chip select to be usable). - Bus drivers.SPI + bus drivers.SPI + configurePins func() } // 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 pin.Output, spi drivers.SPI) *DeviceSPI { return &DeviceSPI{ - CSB: csb, // chip select - Bus: spi, + csb: csb.Set, // chip select + bus: spi, + configurePins: func() { + legacy.ConfigurePinOut(csb) + }, } } @@ -33,8 +38,11 @@ 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() + if d.configurePins == nil { + return legacy.ErrConfigBeforeInstantiated + } + d.configurePins() + d.csb.High() // The datasheet recommends doing a register read from address 0x7F to get // SPI communication going: @@ -86,9 +94,9 @@ func (d *DeviceSPI) ReadTemperature() (temperature int32, err error) { data[0] = 0x80 | reg_TEMPERATURE_0 data[1] = 0 data[2] = 0 - d.CSB.Low() - err = d.Bus.Tx(data, data) - d.CSB.High() + d.csb.Low() + err = d.bus.Tx(data, data) + d.csb.High() if err != nil { return } @@ -123,9 +131,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() - err = d.Bus.Tx(data, data) - d.CSB.High() + d.csb.Low() + err = d.bus.Tx(data, data) + d.csb.High() if err != nil { return } @@ -153,9 +161,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() - err = d.Bus.Tx(data, data) - d.CSB.High() + d.csb.Low() + err = d.bus.Tx(data, data) + d.csb.High() if err != nil { return } @@ -201,9 +209,9 @@ func (d *DeviceSPI) readRegister(address uint8) uint8 { data := d.buf[:2] data[0] = 0x80 | address data[1] = 0 - d.CSB.Low() - d.Bus.Tx(data, data) - d.CSB.High() + d.csb.Low() + d.bus.Tx(data, data) + d.csb.High() return data[1] } @@ -217,7 +225,7 @@ func (d *DeviceSPI) writeRegister(address, data uint8) { buf[0] = address buf[1] = data - d.CSB.Low() - d.Bus.Tx(buf, buf) - d.CSB.High() + d.csb.Low() + d.bus.Tx(buf, buf) + d.csb.High() } diff --git a/buzzer/buzzer.go b/buzzer/buzzer.go index 04c112953..daa8ddf2a 100644 --- a/buzzer/buzzer.go +++ b/buzzer/buzzer.go @@ -2,22 +2,23 @@ package buzzer // import "tinygo.org/x/drivers/buzzer" import ( - "machine" - "time" + + "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/pin" ) // Device wraps a GPIO connection to a buzzer. type Device struct { - pin machine.Pin + pin drivers.PinOutput High bool BPM float64 } // New returns a new buzzer driver given which pin to use -func New(pin machine.Pin) Device { +func New(pin pin.Output) Device { return Device{ - pin: pin, + pin: pin.Set, High: false, BPM: 96.0, } @@ -25,14 +26,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.pin.High() l.High = true return } // Off sets the buzzer to a low state. func (l *Device) Off() (err error) { - l.pin.Set(false) + l.pin.Low() l.High = false return } diff --git a/easystepper/easystepper.go b/easystepper/easystepper.go index b6b93f830..8862d3355 100644 --- a/easystepper/easystepper.go +++ b/easystepper/easystepper.go @@ -2,9 +2,9 @@ package easystepper // import "tinygo.org/x/drivers/easystepper" import ( - "errors" - "machine" "time" + + "tinygo.org/x/drivers" ) // StepMode determines the coil sequence used to perform a single step @@ -30,28 +30,10 @@ func (sm StepMode) stepCount() uint { } } -// DeviceConfig contains the configuration data for a single easystepper driver -type DeviceConfig struct { - // Pin1 ... Pin4 determines the pins to configure and use for the device - Pin1, Pin2, Pin3, Pin4 machine.Pin - // StepCount is the number of steps required to perform a full revolution of the stepper motor - StepCount uint - // RPM determines the speed of the stepper motor in 'Revolutions per Minute' - RPM uint - // Mode determines the coil sequence used to perform a single step - Mode StepMode -} - -// DualDeviceConfig contains the configuration data for a dual easystepper driver -type DualDeviceConfig struct { - DeviceConfig - // Pin5 ... Pin8 determines the pins to configure and use for the second device - Pin5, Pin6, Pin7, Pin8 machine.Pin -} - // Device holds the pins and the delay between steps type Device struct { - pins [4]machine.Pin + pins [4]drivers.PinOutput + config func() stepDelay time.Duration stepNumber uint8 stepMode StepMode @@ -62,51 +44,6 @@ type DualDevice struct { devices [2]*Device } -// New returns a new single easystepper driver given a DeviceConfig -func New(config DeviceConfig) (*Device, error) { - if config.StepCount == 0 || config.RPM == 0 { - return nil, errors.New("config.StepCount and config.RPM must be > 0") - } - return &Device{ - pins: [4]machine.Pin{config.Pin1, config.Pin2, config.Pin3, config.Pin4}, - stepDelay: time.Second * 60 / time.Duration((config.StepCount * config.RPM)), - stepMode: config.Mode, - }, nil -} - -// Configure configures the pins of the Device -func (d *Device) Configure() { - for _, pin := range d.pins { - pin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - } -} - -// NewDual returns a new dual easystepper driver given 8 pins, number of steps and rpm -func NewDual(config DualDeviceConfig) (*DualDevice, error) { - // Create the first device - dev1, err := New(config.DeviceConfig) - if err != nil { - return nil, err - } - // Create the second device - config.DeviceConfig.Pin1 = config.Pin5 - config.DeviceConfig.Pin2 = config.Pin6 - config.DeviceConfig.Pin3 = config.Pin7 - config.DeviceConfig.Pin4 = config.Pin8 - dev2, err := New(config.DeviceConfig) - if err != nil { - return nil, err - } - // Return composite dual device - return &DualDevice{devices: [2]*Device{dev1, dev2}}, nil -} - -// Configure configures the pins of the DualDevice -func (d *DualDevice) Configure() { - d.devices[0].Configure() - d.devices[1].Configure() -} - // Move rotates the motor the number of given steps // (negative steps will rotate it the opposite direction) func (d *Device) Move(steps int32) { diff --git a/easystepper/easystepper_go.go b/easystepper/easystepper_go.go new file mode 100644 index 000000000..c189a6df9 --- /dev/null +++ b/easystepper/easystepper_go.go @@ -0,0 +1,26 @@ +package easystepper + +import ( + "errors" + "time" + + "tinygo.org/x/drivers" +) + +func NewCrossPlatform(stepcount, rpm uint, mode StepMode, pins [4]drivers.PinOutput) (*Device, error) { + if stepcount == 0 || rpm == 0 { + return nil, errors.New("zero rpm and/or stepcount") + } + for i := range pins { + if pins[i] == nil { + return nil, errors.New("nil pin") + } + } + d := &Device{ + pins: pins, + stepDelay: time.Second * 60 / time.Duration((stepcount * rpm)), + stepMode: mode, + config: func() {}, + } + return d, nil +} diff --git a/easystepper/easystepper_tinygo.go b/easystepper/easystepper_tinygo.go new file mode 100644 index 000000000..ae1a7cfb8 --- /dev/null +++ b/easystepper/easystepper_tinygo.go @@ -0,0 +1,83 @@ +//go:build baremetal + +package easystepper + +import ( + "errors" + "machine" + "time" + + "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" +) + +// New returns a new single easystepper driver given a DeviceConfig +func New(config DeviceConfig) (*Device, error) { + if config.StepCount == 0 || config.RPM == 0 { + return nil, errors.New("config.StepCount and config.RPM must be > 0") + } + return &Device{ + pins: [4]drivers.PinOutput{config.Pin1.Set, config.Pin2.Set, config.Pin3.Set, config.Pin4.Set}, + stepDelay: time.Second * 60 / time.Duration((config.StepCount * config.RPM)), + stepMode: config.Mode, + config: func() { + legacy.ConfigurePinOut(config.Pin1) + legacy.ConfigurePinOut(config.Pin2) + legacy.ConfigurePinOut(config.Pin3) + legacy.ConfigurePinOut(config.Pin4) + }, + }, nil +} + +// Configure configures the pins of the Device +func (d *Device) Configure() { + if d.config == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } + d.config() +} + +// Configure configures the pins of the DualDevice +func (d *DualDevice) Configure() { + d.devices[0].Configure() + d.devices[1].Configure() +} + +// NewDual returns a new dual easystepper driver given 8 pins, number of steps and rpm +func NewDual(config DualDeviceConfig) (*DualDevice, error) { + // Create the first device + dev1, err := New(config.DeviceConfig) + if err != nil { + return nil, err + } + // Create the second device + config.DeviceConfig.Pin1 = config.Pin5 + config.DeviceConfig.Pin2 = config.Pin6 + config.DeviceConfig.Pin3 = config.Pin7 + config.DeviceConfig.Pin4 = config.Pin8 + dev2, err := New(config.DeviceConfig) + if err != nil { + return nil, err + } + // Return composite dual device + return &DualDevice{devices: [2]*Device{dev1, dev2}}, nil +} + +// DeviceConfig contains the configuration data for a single easystepper driver +type DeviceConfig struct { + // Pin1 ... Pin4 determines the pins to configure and use for the device + Pin1, Pin2, Pin3, Pin4 machine.Pin + // StepCount is the number of steps required to perform a full revolution of the stepper motor + StepCount uint + // RPM determines the speed of the stepper motor in 'Revolutions per Minute' + RPM uint + // Mode determines the coil sequence used to perform a single step + Mode StepMode +} + +// DualDeviceConfig contains the configuration data for a dual easystepper driver +type DualDeviceConfig struct { + DeviceConfig + // Pin5 ... Pin8 determines the pins to configure and use for the second device + Pin5, Pin6, Pin7, Pin8 machine.Pin +} diff --git a/ft6336/ft6336.go b/ft6336/ft6336.go index 6b2032d43..9252f3c1b 100644 --- a/ft6336/ft6336.go +++ b/ft6336/ft6336.go @@ -5,28 +5,29 @@ package ft6336 import ( - "machine" - "tinygo.org/x/drivers" "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" "tinygo.org/x/drivers/touch" ) // Device wraps FT6336 I2C Self-Capacitive touch type Device struct { - bus drivers.I2C - buf []byte - Address uint8 - intPin machine.Pin + bus drivers.I2C + buf []byte + Address uint8 + configurePins func() } // New returns FT6336 device for the provided I2C bus using default address. -func New(i2c drivers.I2C, intPin machine.Pin) *Device { +func New(i2c drivers.I2C, intPin pin.Input) *Device { return &Device{ bus: i2c, buf: make([]byte, 11), Address: Address, - intPin: intPin, + configurePins: func() { + legacy.ConfigurePinInputPulldown(intPin) + }, } } @@ -36,8 +37,11 @@ type Config struct { // Configure the FT6336 device. func (d *Device) Configure(config Config) error { + if d.configurePins == nil { + return legacy.ErrConfigBeforeInstantiated + } d.write1Byte(0xA4, 0x00) - d.intPin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) + d.configurePins() return nil } diff --git a/gc9a01/gc9a01.go b/gc9a01/gc9a01.go index 1426241b2..fd9d2b940 100644 --- a/gc9a01/gc9a01.go +++ b/gc9a01/gc9a01.go @@ -5,12 +5,13 @@ package gc9a01 // import "tinygo.org/x/drivers/gc9a01" import ( "image/color" - "machine" "time" "errors" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" ) // Rotation controls the rotation used by the display. @@ -22,10 +23,10 @@ type FrameRate uint8 // Device wraps an SPI connection. type Device 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 columnOffsetCfg int16 @@ -52,17 +53,17 @@ type Config struct { } // New creates a new ST7789 connection. The SPI wire must already be configured. -func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device { - resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - blPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) +func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin pin.Output) Device { + legacy.ConfigurePinOut(resetPin) + legacy.ConfigurePinOut(dcPin) + legacy.ConfigurePinOut(csPin) + legacy.ConfigurePinOut(blPin) return Device{ bus: bus, - resetPin: resetPin, - dcPin: dcPin, - csPin: csPin, - blPin: blPin, + resetPin: resetPin.Set, + dcPin: dcPin.Set, + csPin: csPin.Set, + blPin: blPin.Set, } } @@ -226,7 +227,7 @@ func (d *Device) Data(data uint8) { // Tx sends data to the display func (d *Device) Tx(data []byte, isCommand bool) { - d.dcPin.Set(!isCommand) + d.dcPin(!isCommand) d.bus.Tx(data, nil) } diff --git a/hcsr04/hcsr04.go b/hcsr04/hcsr04.go index 703fa7850..c2f6e1f46 100644 --- a/hcsr04/hcsr04.go +++ b/hcsr04/hcsr04.go @@ -5,30 +5,40 @@ package hcsr04 import ( - "machine" "time" + + "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" ) 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 + configurePins func() } // New returns a new ultrasonic driver given 2 pins -func New(trigger, echo machine.Pin) Device { +func New(trigger pin.Output, echo pin.Input) Device { return Device{ - trigger: trigger, - echo: echo, + trigger: trigger.Set, + echo: echo.Get, + configurePins: func() { + legacy.ConfigurePinOut(trigger) + legacy.ConfigurePinInput(echo) + }, } } // 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}) + if d.configurePins == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } + d.configurePins() } // ReadDistance returns the distance of the object in mm @@ -52,7 +62,7 @@ func (d *Device) ReadPulse() int32 { d.trigger.Low() i := uint8(0) for { - if d.echo.Get() { + if d.echo() { t = time.Now() break } @@ -66,7 +76,7 @@ func (d *Device) ReadPulse() int32 { } i = 0 for { - if !d.echo.Get() { + if !d.echo() { return int32(time.Since(t).Microseconds()) } i++ diff --git a/hd44780/hd44780.go b/hd44780/hd44780.go index 111f70d43..2cf2c0ae5 100644 --- a/hd44780/hd44780.go +++ b/hd44780/hd44780.go @@ -210,7 +210,7 @@ func (d *Device) SendCommand(command byte) { d.bus.SetCommandMode(true) d.bus.Write([]byte{command}) - for d.busy(command == DISPLAY_CLEAR || command == CURSOR_HOME) { + for d.isBusy(command == DISPLAY_CLEAR || command == CURSOR_HOME) { } } @@ -219,7 +219,7 @@ func (d *Device) sendData(data byte) { d.bus.SetCommandMode(false) d.bus.Write([]byte{data}) - for d.busy(false) { + for d.isBusy(false) { } } @@ -231,9 +231,9 @@ func (d *Device) CreateCharacter(cgramAddr uint8, data []byte) { } } -// busy returns true when hd447890 is busy +// isBusy returns true when hd447890 is isBusy // or after the timeout specified -func (d *Device) busy(longDelay bool) bool { +func (d *Device) isBusy(longDelay bool) bool { if d.bus.WriteOnly() { // Can't read busy flag if write only, so sleep a bit then return if longDelay { @@ -261,7 +261,7 @@ func (d *Device) busy(longDelay bool) bool { // Busy returns true when hd447890 is busy func (d *Device) Busy() bool { - return d.busy(false) + return d.isBusy(false) } // Size returns the current size of the display. diff --git a/internal/legacy/pinhal.go b/internal/legacy/pinhal.go new file mode 100644 index 000000000..1a9da9af9 --- /dev/null +++ b/internal/legacy/pinhal.go @@ -0,0 +1,54 @@ +package legacy + +import ( + "errors" + + "tinygo.org/x/drivers/internal/pin" +) + +// ConfigurePinOut is a legacy function used to configure pins as outputs. +// +// Deprecated: Do not configure pins in drivers. +// This is a legacy feature and should only be used by drivers that +// previously configured pins in initialization to avoid breaking users. +func ConfigurePinOut(po pin.Output) { + configurePinOut(po) +} + +// ConfigurePinInput is a legacy function used to configure pins as inputs. +// +// Deprecated: Do not configure pins in drivers. +// This is a legacy feature and should only be used by drivers that +// previously configured pins in initialization to avoid breaking users. +func ConfigurePinInputPulldown(pi pin.Input) { + configurePinInputPulldown(pi) +} + +// ConfigurePinInput is a legacy function used to configure pins as inputs. +// +// Deprecated: Do not configure pins in drivers. +// This is a legacy feature and should only be used by drivers that +// previously configured pins in initialization to avoid breaking users. +func ConfigurePinInput(pi pin.Input) { + configurePinInput(pi) +} + +// ConfigurePinInput is a legacy function used to configure pins as inputs. +// +// Deprecated: Do not configure pins in drivers. +// This is a legacy feature and should only be used by drivers that +// previously configured pins in initialization to avoid breaking users. +func ConfigurePinInputPullup(pi pin.Input) { + configurePinInputPullup(pi) +} + +// PinIsNoPin returns true if the argument is a machine.Pin type and is the machine.NoPin predeclared type. +// +// Deprecated: Drivers do not require pin knowledge from now on. +func PinIsNoPin(pin any) bool { + return pinIsNoPin(pin) +} + +var ( + ErrConfigBeforeInstantiated = errors.New("device must be instantiated with New before calling Configure method") +) diff --git a/internal/legacy/pinhal_go.go b/internal/legacy/pinhal_go.go new file mode 100644 index 000000000..f76cc8bff --- /dev/null +++ b/internal/legacy/pinhal_go.go @@ -0,0 +1,11 @@ +//go:build !baremetal + +package legacy + +import "tinygo.org/x/drivers/internal/pin" + +func configurePinOut(p pin.Output) {} +func configurePinInput(p pin.Input) {} +func configurePinInputPulldown(p pin.Input) {} +func configurePinInputPullup(p pin.Input) {} +func pinIsNoPin(a any) bool { return false } diff --git a/internal/legacy/pinhal_nopulls.go b/internal/legacy/pinhal_nopulls.go new file mode 100644 index 000000000..1fc61389d --- /dev/null +++ b/internal/legacy/pinhal_nopulls.go @@ -0,0 +1,10 @@ +//go:build baremetal && fe310 + +package legacy + +import "machine" + +const ( + pulldown = machine.PinInput + pullup = machine.PinInput +) diff --git a/internal/legacy/pinhal_pulls.go b/internal/legacy/pinhal_pulls.go new file mode 100644 index 000000000..d7e88c658 --- /dev/null +++ b/internal/legacy/pinhal_pulls.go @@ -0,0 +1,13 @@ +//go:build baremetal && !fe310 + +package legacy + +import "machine" + +// If you are getting a build error here you then we missed adding +// your CPU build tag to the list of CPUs that do not have pulldown/pullups. +// Add it above and in pinhal_nopulls! You should also add a smoketest for it :) +const ( + pulldown = machine.PinInputPulldown + pullup = machine.PinInputPullup +) diff --git a/internal/legacy/pinhal_tinygo.go b/internal/legacy/pinhal_tinygo.go new file mode 100644 index 000000000..d0068b2c8 --- /dev/null +++ b/internal/legacy/pinhal_tinygo.go @@ -0,0 +1,37 @@ +//go:build baremetal + +package legacy + +import ( + "machine" + + "tinygo.org/x/drivers/internal/pin" +) + +func configurePinOut(po pin.Output) { + configurePin(po, machine.PinOutput) +} + +func configurePinInputPulldown(pi pin.Input) { + configurePin(pi, pulldown) // some chips do not have pull down, in which case pulldown==machine.PinInput. +} + +func configurePinInput(pi pin.Input) { + configurePin(pi, machine.PinInput) +} + +func configurePinInputPullup(pi pin.Input) { + configurePin(pi, pullup) // some chips do not have pull up, in which case pullup==machine.PinInput. +} + +func pinIsNoPin(a any) bool { + p, ok := a.(machine.Pin) + return ok && p == machine.NoPin +} + +func configurePin(p any, mode machine.PinMode) { + machinePin, ok := p.(machine.Pin) + if ok { + machinePin.Configure(machine.PinConfig{Mode: mode}) + } +} diff --git a/internal/pin/pin.go b/internal/pin/pin.go new file mode 100644 index 000000000..3eb0e7e6f --- /dev/null +++ b/internal/pin/pin.go @@ -0,0 +1,23 @@ +package pin + +import "tinygo.org/x/drivers" + +// Here to aid relevant documentation links of [drivers.PinOutput] and [drivers.PinInput]. +var _ drivers.PinOutput + +// Output represents a pin hardware abstraction layer for a pin that can output a digital signal. +// +// This is an alternative to [drivers.PinOutput] abstraction which is a function type and has +// not been standardized as of yet as a standard HAL in the drivers package, +// [discussion ongoing here]. +// +// [discussion ongoing here]: https://github.com/orgs/tinygo-org/discussions/5043 +type Output interface { + Set(level bool) +} + +// Input represents a pin hardware abstraction layer. +// See [Output] for more information on why this type exists separate to drivers. +type Input interface { + Get() (level bool) +} diff --git a/max6675/max6675.go b/max6675/max6675.go index eb67596b6..a712df0f8 100644 --- a/max6675/max6675.go +++ b/max6675/max6675.go @@ -3,9 +3,9 @@ package max6675 import ( "errors" - "machine" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/pin" ) // ErrThermocoupleOpen is returned when the thermocouple input is open. @@ -14,16 +14,16 @@ var ErrThermocoupleOpen = errors.New("thermocouple input open") type Device struct { bus drivers.SPI - cs machine.Pin + cs drivers.PinOutput } // Create a new Device to read from a MAX6675 thermocouple. // Pins must be configured before use. Frequency for SPI // should be 4.3MHz maximum. -func NewDevice(bus drivers.SPI, cs machine.Pin) *Device { +func NewDevice(bus drivers.SPI, cs pin.Output) *Device { return &Device{ bus: bus, - cs: cs, + cs: cs.Set, } } diff --git a/max72xx/max72xx.go b/max72xx/max72xx.go index ca6c81929..815ff7b14 100644 --- a/max72xx/max72xx.go +++ b/max72xx/max72xx.go @@ -3,31 +3,36 @@ package max72xx import ( - "machine" - "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" ) type Device struct { - bus drivers.SPI - cs machine.Pin + bus drivers.SPI + cs drivers.PinOutput + configurePins func() } // NewDriver creates a new max7219 connection. The SPI wire must already be configured // The SPI frequency must not be higher than 10MHz. // parameter cs: the datasheet also refers to this pin as "load" pin. -func NewDevice(bus drivers.SPI, cs machine.Pin) *Device { +func NewDevice(bus drivers.SPI, cs pin.Output) *Device { return &Device{ bus: bus, - cs: cs, + cs: cs.Set, + configurePins: func() { + legacy.ConfigurePinOut(cs) + }, } } // Configure setups the pins. func (driver *Device) Configure() { - outPutConfig := machine.PinConfig{Mode: machine.PinOutput} - - driver.cs.Configure(outPutConfig) + if driver.configurePins == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } + driver.configurePins() } // SetScanLimit sets the scan limit. Maximum is 8. diff --git a/mcp2515/mcp2515.go b/mcp2515/mcp2515.go index 2b636b19b..48ebaf100 100644 --- a/mcp2515/mcp2515.go +++ b/mcp2515/mcp2515.go @@ -8,18 +8,20 @@ package mcp2515 // import "tinygo.org/x/drivers/mcp2515" import ( "errors" "fmt" - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" ) // Device wraps MCP2515 SPI CAN Module. type Device struct { - spi SPI - cs machine.Pin - msg *CANMsg - mcpMode byte + spi SPI + cs drivers.PinOutput + msg *CANMsg + mcpMode byte + configurePins func() } // CANMsg stores CAN message fields. @@ -36,15 +38,18 @@ const ( ) // New returns a new MCP2515 driver. Pass in a fully configured SPI bus. -func New(b drivers.SPI, csPin machine.Pin) *Device { +func New(b drivers.SPI, csPin pin.Output) *Device { d := &Device{ spi: SPI{ bus: b, tx: make([]byte, 0, bufferSize), rx: make([]byte, 0, bufferSize), }, - cs: csPin, + cs: csPin.Set, msg: &CANMsg{}, + configurePins: func() { + legacy.ConfigurePinOut(csPin) + }, } return d @@ -52,7 +57,10 @@ func New(b drivers.SPI, csPin machine.Pin) *Device { // Configure sets up the device for communication. func (d *Device) Configure() { - d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput}) + if d.configurePins == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } + d.configurePins() } const beginTimeoutValue int = 10 diff --git a/onewire/onewire.go b/onewire/onewire.go index ee1828387..f62656464 100644 --- a/onewire/onewire.go +++ b/onewire/onewire.go @@ -5,8 +5,9 @@ package onewire // import "tinygo.org/x/drivers/onewire" import ( "errors" - "machine" "time" + + "tinygo.org/x/drivers" ) // OneWire ROM commands @@ -19,7 +20,8 @@ const ( // Device wraps a connection to an 1-Wire devices. type Device struct { - p machine.Pin + set drivers.PinOutput + get drivers.PinInput } // Config wraps a configuration to an 1-Wire devices. @@ -32,24 +34,30 @@ var ( errReadAddress = errors.New("Error: OneWire. Read address error: CRC mismatch.") ) -// New creates a new GPIO 1-Wire connection. -// The pin must be pulled up to the VCC via a resistor greater than 500 ohms (default 4.7k). -func New(p machine.Pin) Device { - return Device{ - p: p, +// NewFromFuncs expects pin setter and getter for DQ line. Ideally this driver should receive +// a one-wire bus HAL abstraction, but I was asked to show how this could be done using pin HAL so here goes. +func NewFromFuncs(getPinLevel drivers.PinInput, setPinLevel drivers.PinOutput) *Device { + return &Device{ + set: setPinLevel, + get: getPinLevel, } } // Configure initializes the protocol. func (d *Device) Configure(config Config) {} +// By setting the pin value one should configure as output and also expect a more +// consistent behaviour across all tinygo hosts by pulling DQ line low consistently. Win-win. +func (d *Device) cfgOut() { d.set.Low() } +func (d *Device) cfgIn() { d.get() } + // Reset pull DQ line low, then up. func (d Device) Reset() error { - d.p.Configure(machine.PinConfig{Mode: machine.PinOutput}) + d.cfgOut() time.Sleep(480 * time.Microsecond) - d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + d.cfgIn() time.Sleep(70 * time.Microsecond) - precence := d.p.Get() + precence := d.get() time.Sleep(410 * time.Microsecond) if precence { return errNoPresence @@ -59,14 +67,14 @@ func (d Device) Reset() error { // WriteBit transmits a bit to 1-Wire bus. func (d Device) WriteBit(data uint8) { - d.p.Configure(machine.PinConfig{Mode: machine.PinOutput}) + d.cfgOut() if data&1 == 1 { // Send '1' time.Sleep(5 * time.Microsecond) - d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + d.cfgIn() time.Sleep(60 * time.Microsecond) } else { // Send '0' time.Sleep(60 * time.Microsecond) - d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + d.cfgIn() time.Sleep(5 * time.Microsecond) } } @@ -81,11 +89,11 @@ func (d Device) Write(data uint8) { // ReadBit receives a bit from 1-Wire bus. func (d Device) ReadBit() (data uint8) { - d.p.Configure(machine.PinConfig{Mode: machine.PinOutput}) + d.cfgOut() time.Sleep(3 * time.Microsecond) - d.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + d.cfgIn() time.Sleep(8 * time.Microsecond) - if d.p.Get() { + if d.get() { data = 1 } time.Sleep(60 * time.Microsecond) diff --git a/onewire/onewire_tinygo.go b/onewire/onewire_tinygo.go new file mode 100644 index 000000000..95a348d8a --- /dev/null +++ b/onewire/onewire_tinygo.go @@ -0,0 +1,29 @@ +package onewire + +import ( + "machine" + + "tinygo.org/x/drivers/internal/legacy" +) + +// New creates a new GPIO 1-Wire connection. +// The pin must be pulled up to the VCC via a resistor greater than 500 ohms (default 4.7k). +func New(p machine.Pin) Device { + isOut := false + return Device{ + set: func(level bool) { + if !isOut { + legacy.ConfigurePinOut(p) + isOut = true + } + p.Set(level) + }, + get: func() (level bool) { + if isOut { + legacy.ConfigurePinInputPullup(p) + isOut = false + } + return p.Get() + }, + } +} diff --git a/pcd8544/pcd8544.go b/pcd8544/pcd8544.go index 3a843cbab..73a17f0c0 100644 --- a/pcd8544/pcd8544.go +++ b/pcd8544/pcd8544.go @@ -6,18 +6,18 @@ package pcd8544 // import "tinygo.org/x/drivers/pcd8544" import ( "errors" "image/color" - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/pin" ) // Device wraps an SPI connection. type Device struct { bus drivers.SPI - dcPin machine.Pin - rstPin machine.Pin - scePin machine.Pin + dcPin drivers.PinOutput + rstPin drivers.PinOutput + scePin drivers.PinOutput buffer []byte width int16 height int16 @@ -30,12 +30,12 @@ type Config struct { } // New creates a new PCD8544 connection. The SPI bus must already be configured. -func New(bus drivers.SPI, dcPin, rstPin, scePin machine.Pin) *Device { +func New(bus drivers.SPI, dcPin, rstPin, scePin pin.Output) *Device { return &Device{ bus: bus, - dcPin: dcPin, - rstPin: rstPin, - scePin: scePin, + dcPin: dcPin.Set, + rstPin: rstPin.Set, + scePin: scePin.Set, } } diff --git a/pin.go b/pin.go new file mode 100644 index 000000000..33ea5ac99 --- /dev/null +++ b/pin.go @@ -0,0 +1,29 @@ +package drivers + +// PinOutput is hardware abstraction for a pin which outputs a +// digital signal (high or low level). +// +// // Code conversion demo: from machine.Pin to drivers.PinOutput +// led := machine.LED +// led.Configure(machine.PinConfig{Mode: machine.PinOutput}) +// var pin drivers.PinOutput = led.Set // Going from a machine.Pin to a drivers.PinOutput +type PinOutput func(level bool) + +// High sets the underlying pin's level to high. This is equivalent to calling PinOutput(true). +func (po PinOutput) High() { + po(true) +} + +// Low sets the underlying pin's level to low. This is equivalent to calling PinOutput(false). +func (po PinOutput) Low() { + po(false) +} + +// PinInput is hardware abstraction for a pin which receives a +// digital signal and reads it (high or low level). +// +// // Code conversion demo: from machine.Pin to drivers.PinInput +// input := machine.LED +// input.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) // or use machine.PinInputPullup or machine.PinInput +// var pin drivers.PinInput = input.Get // Going from a machine.Pin to a drivers.PinInput +type PinInput func() (level bool) diff --git a/shiftregister/shiftregister.go b/shiftregister/shiftregister.go index 95f1f20c9..c315387c3 100644 --- a/shiftregister/shiftregister.go +++ b/shiftregister/shiftregister.go @@ -2,7 +2,9 @@ package shiftregister import ( - "machine" + "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" ) type NumberBit int8 @@ -16,9 +18,10 @@ const ( // Device holds pin number type Device struct { - latch, clock, out machine.Pin // IC wiring - bits NumberBit // Pin number - mask uint32 // keep all pins state + latch, clock, out drivers.PinOutput // IC wiring + config func() + bits NumberBit // Pin number + mask uint32 // keep all pins state } // ShiftPin is the implementation of the ShiftPin interface. @@ -29,20 +32,25 @@ type ShiftPin struct { } // New returns a new shift output register device -func New(Bits NumberBit, Latch, Clock, Out machine.Pin) *Device { +func New(Bits NumberBit, Latch, Clock, Out pin.Output) *Device { return &Device{ - latch: Latch, - clock: Clock, - out: Out, + latch: Latch.Set, + clock: Clock.Set, + out: Out.Set, bits: Bits, + config: func() { + legacy.ConfigurePinOut(Latch) + legacy.ConfigurePinOut(Clock) + legacy.ConfigurePinOut(Out) + }, } } // Configure set hardware configuration func (d *Device) Configure() { - d.latch.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.clock.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.out.Configure(machine.PinConfig{Mode: machine.PinOutput}) + if d.config == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } d.latch.High() } @@ -53,7 +61,7 @@ func (d *Device) WriteMask(mask uint32) { d.latch.Low() for i := 0; i < int(d.bits); i++ { d.clock.Low() - d.out.Set(mask&1 != 0) + d.out(mask&1 != 0) mask = mask >> 1 d.clock.High() } diff --git a/ssd1289/pinbus.go b/ssd1289/pinbus.go index aacdf79aa..5c703777b 100644 --- a/ssd1289/pinbus.go +++ b/ssd1289/pinbus.go @@ -1,3 +1,5 @@ +//go:build baremetal + package ssd1289 import "machine" diff --git a/ssd1289/ssd1289.go b/ssd1289/ssd1289.go index f5cc22a0e..c649d848b 100644 --- a/ssd1289/ssd1289.go +++ b/ssd1289/ssd1289.go @@ -7,6 +7,9 @@ import ( "image/color" "machine" "time" + + "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" ) type Bus interface { @@ -14,10 +17,10 @@ type Bus interface { } type Device struct { - rs machine.Pin - wr machine.Pin - cs machine.Pin - rst machine.Pin + rs drivers.PinOutput + wr drivers.PinOutput + cs drivers.PinOutput + rst drivers.PinOutput bus Bus } @@ -26,21 +29,20 @@ const height = int16(320) func New(rs machine.Pin, wr machine.Pin, cs machine.Pin, rst machine.Pin, bus Bus) Device { d := Device{ - rs: rs, - wr: wr, - cs: cs, - rst: rst, + rs: rs.Set, + wr: wr.Set, + cs: cs.Set, + rst: rst.Set, bus: bus, } - - rs.Configure(machine.PinConfig{Mode: machine.PinOutput}) - wr.Configure(machine.PinConfig{Mode: machine.PinOutput}) - cs.Configure(machine.PinConfig{Mode: machine.PinOutput}) - rst.Configure(machine.PinConfig{Mode: machine.PinOutput}) - - cs.High() - rst.High() - wr.High() + legacy.ConfigurePinOut(rs) + legacy.ConfigurePinOut(wr) + legacy.ConfigurePinOut(cs) + legacy.ConfigurePinOut(rst) + + cs.Set(true) + rst.Set(true) + wr.Set(true) return d } diff --git a/ssd1331/ssd1331.go b/ssd1331/ssd1331.go index 0717c48e1..9775819c2 100644 --- a/ssd1331/ssd1331.go +++ b/ssd1331/ssd1331.go @@ -11,6 +11,7 @@ import ( "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" ) type Model uint8 @@ -19,9 +20,9 @@ type Rotation uint8 // Device wraps an SPI connection. type Device struct { bus drivers.SPI - dcPin machine.Pin - resetPin machine.Pin - csPin machine.Pin + dcPin drivers.PinOutput + resetPin drivers.PinOutput + csPin drivers.PinOutput width int16 height int16 batchLength int16 @@ -37,14 +38,14 @@ type Config struct { // New creates a new SSD1331 connection. The SPI wire must already be configured. func New(bus drivers.SPI, resetPin, dcPin, 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}) + legacy.ConfigurePinOut(dcPin) + legacy.ConfigurePinOut(resetPin) + legacy.ConfigurePinOut(csPin) return Device{ bus: bus, - dcPin: dcPin, - resetPin: resetPin, - csPin: csPin, + dcPin: dcPin.Set, + resetPin: resetPin.Set, + csPin: csPin.Set, } } @@ -251,7 +252,7 @@ func (d *Device) Data(data uint8) { // Tx sends data to the display func (d *Device) Tx(data []byte, isCommand bool) { - d.dcPin.Set(!isCommand) + d.dcPin(!isCommand) d.bus.Tx(data, nil) } diff --git a/ssd1351/ssd1351.go b/ssd1351/ssd1351.go index 3561dd6ca..96a6f0ca0 100644 --- a/ssd1351/ssd1351.go +++ b/ssd1351/ssd1351.go @@ -6,10 +6,11 @@ package ssd1351 // import "tinygo.org/x/drivers/ssd1351" import ( "errors" "image/color" - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" ) var ( @@ -19,17 +20,18 @@ var ( // Device wraps an SPI connection. type Device struct { - bus drivers.SPI - dcPin machine.Pin - resetPin machine.Pin - csPin machine.Pin - enPin machine.Pin - rwPin machine.Pin - width int16 - height int16 - rowOffset int16 - columnOffset int16 - bufferLength int16 + bus drivers.SPI + dcPin drivers.PinOutput + resetPin drivers.PinOutput + csPin drivers.PinOutput + enPin drivers.PinOutput + rwPin drivers.PinOutput + configurePins func() + width int16 + height int16 + rowOffset int16 + columnOffset int16 + bufferLength int16 } // Config is the configuration for the display @@ -41,19 +43,29 @@ type Config struct { } // New creates a new SSD1351 connection. The SPI wire must already be configured. -func New(bus drivers.SPI, resetPin, dcPin, csPin, enPin, rwPin machine.Pin) Device { +func New(bus drivers.SPI, resetPin, dcPin, csPin, enPin, rwPin pin.Output) Device { return Device{ bus: bus, - dcPin: dcPin, - resetPin: resetPin, - csPin: csPin, - enPin: enPin, - rwPin: rwPin, + dcPin: dcPin.Set, + resetPin: resetPin.Set, + csPin: csPin.Set, + enPin: enPin.Set, + rwPin: rwPin.Set, + configurePins: func() { + legacy.ConfigurePinOut(dcPin) + legacy.ConfigurePinOut(resetPin) + legacy.ConfigurePinOut(csPin) + legacy.ConfigurePinOut(enPin) + legacy.ConfigurePinOut(rwPin) + }, } } // Configure initializes the display with default configuration func (d *Device) Configure(cfg Config) { + if d.configurePins == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } if cfg.Width == 0 { cfg.Width = 128 } @@ -73,11 +85,7 @@ func (d *Device) Configure(cfg Config) { } // configure GPIO pins - d.dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.enPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.rwPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) + d.configurePins() // reset the device d.resetPin.High() @@ -278,7 +286,7 @@ func (d *Device) Data(data uint8) { // Tx sends data to the display func (d *Device) Tx(data []byte, isCommand bool) { - d.dcPin.Set(!isCommand) + d.dcPin(!isCommand) d.csPin.Low() d.bus.Tx(data, nil) d.csPin.High() diff --git a/st7735/st7735.go b/st7735/st7735.go index 6f15781c2..235bcd66d 100644 --- a/st7735/st7735.go +++ b/st7735/st7735.go @@ -5,12 +5,13 @@ 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/internal/pin" "tinygo.org/x/drivers/pixel" ) @@ -39,10 +40,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 +66,23 @@ 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 pin.Output) 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 pin.Output) DeviceOf[T] { + legacy.ConfigurePinOut(dcPin) + legacy.ConfigurePinOut(resetPin) + legacy.ConfigurePinOut(csPin) + legacy.ConfigurePinOut(blPin) return DeviceOf[T]{ bus: bus, - dcPin: dcPin, - resetPin: resetPin, - csPin: csPin, - blPin: blPin, + dcPin: dcPin.Set, + resetPin: resetPin.Set, + csPin: csPin.Set, + blPin: blPin.Set, } } @@ -423,7 +424,7 @@ func (d *DeviceOf[T]) Data(data uint8) { // Tx sends data to the display func (d *DeviceOf[T]) Tx(data []byte, isCommand bool) { - d.dcPin.Set(!isCommand) + d.dcPin(!isCommand) d.bus.Tx(data, nil) } diff --git a/st7789/st7789.go b/st7789/st7789.go index 5db2402ba..7602b5254 100644 --- a/st7789/st7789.go +++ b/st7789/st7789.go @@ -7,13 +7,14 @@ package st7789 // import "tinygo.org/x/drivers/st7789" import ( "image/color" - "machine" "math" "time" "errors" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" "tinygo.org/x/drivers/pixel" ) @@ -46,10 +47,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 columnOffsetCfg int16 @@ -83,23 +84,27 @@ type Config struct { } // New creates a new ST7789 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 pin.Output) Device { return NewOf[pixel.RGB565BE](bus, resetPin, dcPin, csPin, blPin) } // NewOf creates a new ST7789 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 pin.Output) DeviceOf[T] { + legacy.ConfigurePinOut(dcPin) + legacy.ConfigurePinOut(resetPin) + legacy.ConfigurePinOut(csPin) + legacy.ConfigurePinOut(blPin) + var cs drivers.PinOutput + if !legacy.PinIsNoPin(csPin) { + cs = csPin.Set + } return DeviceOf[T]{ bus: bus, - dcPin: dcPin, - resetPin: resetPin, - csPin: csPin, - blPin: blPin, + dcPin: dcPin.Set, + resetPin: resetPin.Set, + csPin: cs, + blPin: blPin.Set, } } @@ -229,7 +234,7 @@ func (d *DeviceOf[T]) sendCommand(command uint8, data []byte) error { // startWrite must be called at the beginning of all exported methods to set the // chip select pin low. func (d *DeviceOf[T]) startWrite() { - if d.csPin != machine.NoPin { + if d.csPin != nil { d.csPin.Low() } } @@ -237,7 +242,7 @@ func (d *DeviceOf[T]) startWrite() { // endWrite must be called at the end of all exported methods to set the chip // select pin high. func (d *DeviceOf[T]) endWrite() { - if d.csPin != machine.NoPin { + if d.csPin != nil { d.csPin.High() } } diff --git a/sx127x/sx127x.go b/sx127x/sx127x.go index f2c2a8737..e820f3ab3 100644 --- a/sx127x/sx127x.go +++ b/sx127x/sx127x.go @@ -6,10 +6,10 @@ package sx127x import ( "errors" - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/pin" "tinygo.org/x/drivers/lora" ) @@ -22,7 +22,7 @@ const ( // Device wraps an SPI connection to a SX127x device. type Device struct { spi drivers.SPI // SPI bus for module communication - rstPin machine.Pin // GPIO for reset + rstPin drivers.PinOutput // GPIO for reset radioEventChan chan lora.RadioEvent // Channel for Receiving events loraConf lora.Config // Current Lora configuration controller RadioController // to manage interactions with the radio @@ -43,10 +43,10 @@ func (d *Device) GetRadioEventChan() chan lora.RadioEvent { } // New creates a new SX127x connection. The SPI bus must already be configured. -func New(spi drivers.SPI, rstPin machine.Pin) *Device { +func New(spi drivers.SPI, rstPin pin.Output) *Device { k := Device{ spi: spi, - rstPin: rstPin, + rstPin: rstPin.Set, radioEventChan: make(chan lora.RadioEvent, RADIOEVENTCHAN_SIZE), spiTxBuf: make([]byte, SPI_BUFFER_SIZE), spiRxBuf: make([]byte, SPI_BUFFER_SIZE), diff --git a/uc8151/uc8151.go b/uc8151/uc8151.go index 38c48f9a0..6da886641 100644 --- a/uc8151/uc8151.go +++ b/uc8151/uc8151.go @@ -8,10 +8,11 @@ package uc8151 // import "tinygo.org/x/drivers/uc8151" import ( "errors" "image/color" - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" "tinygo.org/x/drivers/pixel" ) @@ -31,10 +32,10 @@ type Config struct { type Device struct { bus drivers.SPI - cs machine.Pin - dc machine.Pin - rst machine.Pin - busy machine.Pin + cs drivers.PinOutput + dc drivers.PinOutput + rst drivers.PinOutput + isBusy drivers.PinInput width int16 height int16 buffer []uint8 @@ -49,17 +50,17 @@ type Device struct { type Speed uint8 // New returns a new uc8151 driver. Pass in a fully configured SPI bus. -func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device { - csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - busyPin.Configure(machine.PinConfig{Mode: machine.PinInput}) +func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device { + legacy.ConfigurePinOut(csPin) + legacy.ConfigurePinOut(dcPin) + legacy.ConfigurePinOut(rstPin) + legacy.ConfigurePinInput(busyPin) return Device{ - bus: bus, - cs: csPin, - dc: dcPin, - rst: rstPin, - busy: busyPin, + bus: bus, + cs: csPin.Set, + dc: dcPin.Set, + rst: rstPin.Set, + isBusy: busyPin.Get, } } @@ -313,14 +314,14 @@ func (d *Device) ClearDisplay() { // WaitUntilIdle waits until the display is ready func (d *Device) WaitUntilIdle() { - for !d.busy.Get() { + for !d.isBusy() { time.Sleep(10 * time.Millisecond) } } // IsBusy returns the busy status of the display func (d *Device) IsBusy() bool { - return d.busy.Get() + return d.isBusy() } // ClearBuffer sets the buffer to 0xFF (white) diff --git a/waveshare-epd/epd1in54/epd1in54.go b/waveshare-epd/epd1in54/epd1in54.go index 120b356ef..4db9d7628 100644 --- a/waveshare-epd/epd1in54/epd1in54.go +++ b/waveshare-epd/epd1in54/epd1in54.go @@ -12,6 +12,10 @@ import ( "image/color" "machine" "time" + + "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" ) type Config struct { @@ -22,14 +26,14 @@ type Config struct { } type Device struct { - bus *machine.SPI - cs machine.Pin - dc machine.Pin - rst machine.Pin - busy machine.Pin - - buffer []uint8 - rotation Rotation + bus *machine.SPI + cs drivers.PinOutput + dc drivers.PinOutput + rst drivers.PinOutput + isBusy drivers.PinInput + configurePins func() + buffer []uint8 + rotation Rotation } type Rotation uint8 @@ -79,22 +83,28 @@ var partialRefresh = [159]uint8{ } // New returns a new epd1in54 driver. Pass in a fully configured SPI bus. -func New(bus *machine.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device { +func New(bus *machine.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device { return Device{ buffer: make([]uint8, (uint32(Width)*uint32(Height))/8), bus: bus, - cs: csPin, - dc: dcPin, - rst: rstPin, - busy: busyPin, + cs: csPin.Set, + dc: dcPin.Set, + rst: rstPin.Set, + isBusy: busyPin.Get, + configurePins: func() { + legacy.ConfigurePinOut(csPin) + legacy.ConfigurePinOut(dcPin) + legacy.ConfigurePinOut(rstPin) + legacy.ConfigurePinInput(busyPin) + }, } } func (d *Device) LDirInit(cfg Config) { - d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.rst.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.dc.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.busy.Configure(machine.PinConfig{Mode: machine.PinInput}) + if d.configurePins == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } + d.configurePins() d.bus.Configure(machine.SPIConfig{ Frequency: 2000000, @@ -150,10 +160,10 @@ func (d *Device) LDirInit(cfg Config) { } func (d *Device) HDirInit(cfg Config) { - d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.rst.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.dc.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.busy.Configure(machine.PinConfig{Mode: machine.PinInput}) + if d.configurePins == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } + d.configurePins() d.bus.Configure(machine.SPIConfig{ Frequency: 2000000, @@ -369,7 +379,7 @@ func (d *Device) Clear() { // WaitUntilIdle waits until the display is ready func (d *Device) WaitUntilIdle() { - for d.busy.Get() { + for d.isBusy() { time.Sleep(100 * time.Millisecond) } time.Sleep(200 * time.Millisecond) @@ -377,7 +387,7 @@ func (d *Device) WaitUntilIdle() { // IsBusy returns the busy status of the display func (d *Device) IsBusy() bool { - return d.busy.Get() + return d.isBusy() } // ClearBuffer sets the buffer to 0xFF (white) diff --git a/waveshare-epd/epd2in13/epd2in13.go b/waveshare-epd/epd2in13/epd2in13.go index e49fd4274..408af5e26 100644 --- a/waveshare-epd/epd2in13/epd2in13.go +++ b/waveshare-epd/epd2in13/epd2in13.go @@ -6,10 +6,11 @@ package epd2in13 // import "tinygo.org/x/drivers/waveshare-epd/epd2in13" import ( "errors" "image/color" - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" ) type Config struct { @@ -21,10 +22,10 @@ type Config struct { type Device struct { bus drivers.SPI - cs machine.Pin - dc machine.Pin - rst machine.Pin - busy machine.Pin + cs drivers.PinOutput + dc drivers.PinOutput + rst drivers.PinOutput + isBusy drivers.PinInput logicalWidth int16 width int16 height int16 @@ -53,17 +54,17 @@ var lutPartialUpdate = [30]uint8{ } // New returns a new epd2in13x driver. Pass in a fully configured SPI bus. -func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device { - csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - busyPin.Configure(machine.PinConfig{Mode: machine.PinInput}) +func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device { + legacy.ConfigurePinOut(csPin) + legacy.ConfigurePinOut(dcPin) + legacy.ConfigurePinOut(rstPin) + legacy.ConfigurePinInput(busyPin) return Device{ - bus: bus, - cs: csPin, - dc: dcPin, - rst: rstPin, - busy: busyPin, + bus: bus, + cs: csPin.Set, + dc: dcPin.Set, + rst: rstPin.Set, + isBusy: busyPin.Get, } } @@ -298,14 +299,14 @@ func (d *Device) setMemoryPointer(x int16, y int16) { // WaitUntilIdle waits until the display is ready func (d *Device) WaitUntilIdle() { - for d.busy.Get() { + for d.isBusy() { time.Sleep(100 * time.Millisecond) } } // IsBusy returns the busy status of the display func (d *Device) IsBusy() bool { - return d.busy.Get() + return d.isBusy() } // ClearBuffer sets the buffer to 0xFF (white) diff --git a/waveshare-epd/epd2in13x/epd2in13x.go b/waveshare-epd/epd2in13x/epd2in13x.go index b84f5a512..8469f0b47 100644 --- a/waveshare-epd/epd2in13x/epd2in13x.go +++ b/waveshare-epd/epd2in13x/epd2in13x.go @@ -6,10 +6,11 @@ package epd2in13x // import "tinygo.org/x/drivers/waveshare-epd/epd2in13x" import ( "errors" "image/color" - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" ) type Config struct { @@ -20,10 +21,10 @@ type Config struct { type Device struct { bus drivers.SPI - cs machine.Pin - dc machine.Pin - rst machine.Pin - busy machine.Pin + cs drivers.PinOutput + dc drivers.PinOutput + rst drivers.PinOutput + isBusy drivers.PinInput width int16 height int16 buffer [][]uint8 @@ -33,17 +34,17 @@ type Device struct { type Color uint8 // New returns a new epd2in13x driver. Pass in a fully configured SPI bus. -func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device { - csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - busyPin.Configure(machine.PinConfig{Mode: machine.PinInput}) +func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device { + legacy.ConfigurePinOut(csPin) + legacy.ConfigurePinOut(dcPin) + legacy.ConfigurePinOut(rstPin) + legacy.ConfigurePinInput(busyPin) return Device{ - bus: bus, - cs: csPin, - dc: dcPin, - rst: rstPin, - busy: busyPin, + bus: bus, + cs: csPin.Set, + dc: dcPin.Set, + rst: rstPin.Set, + isBusy: busyPin.Get, } } @@ -277,14 +278,14 @@ func (d *Device) ClearDisplay() { // WaitUntilIdle waits until the display is ready func (d *Device) WaitUntilIdle() { - for !d.busy.Get() { + for !d.isBusy() { time.Sleep(100 * time.Millisecond) } } // IsBusy returns the busy status of the display func (d *Device) IsBusy() bool { - return d.busy.Get() + return d.isBusy() } // ClearBuffer sets the buffer to 0xFF (white) diff --git a/waveshare-epd/epd2in66b/dev.go b/waveshare-epd/epd2in66b/dev.go index cadff9c04..32cebd80a 100644 --- a/waveshare-epd/epd2in66b/dev.go +++ b/waveshare-epd/epd2in66b/dev.go @@ -5,7 +5,6 @@ package epd2in66b import ( "image/color" - "machine" "time" "tinygo.org/x/drivers" @@ -18,19 +17,12 @@ const ( const Baudrate = 4_000_000 // 4 MHz -type Config struct { - ResetPin machine.Pin - DataPin machine.Pin - ChipSelectPin machine.Pin - BusyPin machine.Pin -} - type Device struct { - bus drivers.SPI - cs machine.Pin - dc machine.Pin - rst machine.Pin - busy machine.Pin + bus drivers.SPI + cs drivers.PinOutput + dc drivers.PinOutput + rst drivers.PinOutput + isBusy drivers.PinInput blackBuffer []byte redBuffer []byte @@ -50,21 +42,6 @@ func New(bus drivers.SPI) Device { } } -// Configure configures the device and its pins. -func (d *Device) Configure(c Config) error { - d.cs = c.ChipSelectPin - d.dc = c.DataPin - d.rst = c.ResetPin - d.busy = c.BusyPin - - d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.dc.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.rst.Configure(machine.PinConfig{Mode: machine.PinOutput}) - d.busy.Configure(machine.PinConfig{Mode: machine.PinInput}) - - return nil -} - func (d *Device) Size() (x, y int16) { return displayWidth, displayHeight } @@ -229,7 +206,7 @@ func (d *Device) WaitUntilIdle() { // give it some time to get busy time.Sleep(50 * time.Millisecond) - for d.busy.Get() { // high = busy + for d.isBusy() { // high = busy time.Sleep(10 * time.Millisecond) } diff --git a/waveshare-epd/epd2in66b/dev_baremetal.go b/waveshare-epd/epd2in66b/dev_baremetal.go new file mode 100644 index 000000000..cb8356e41 --- /dev/null +++ b/waveshare-epd/epd2in66b/dev_baremetal.go @@ -0,0 +1,30 @@ +//go:build baremetal + +package epd2in66b + +import "machine" + +type Config struct { + ResetPin machine.Pin + DataPin machine.Pin + ChipSelectPin machine.Pin + BusyPin machine.Pin +} + +// Configure configures the device and its pins. +func (d *Device) Configure(c Config) error { + cs := c.ChipSelectPin + dc := c.DataPin + rst := c.ResetPin + busy := c.BusyPin + + cs.Configure(machine.PinConfig{Mode: machine.PinOutput}) + dc.Configure(machine.PinConfig{Mode: machine.PinOutput}) + rst.Configure(machine.PinConfig{Mode: machine.PinOutput}) + busy.Configure(machine.PinConfig{Mode: machine.PinInput}) + d.cs = cs.Set + d.dc = dc.Set + d.rst = rst.Set + d.isBusy = busy.Get + return nil +} diff --git a/waveshare-epd/epd2in9/epd2in9.go b/waveshare-epd/epd2in9/epd2in9.go index 994cd7971..b90f5f371 100644 --- a/waveshare-epd/epd2in9/epd2in9.go +++ b/waveshare-epd/epd2in9/epd2in9.go @@ -13,10 +13,11 @@ package epd2in9 // import "tinygo.org/x/drivers/waveshare-epd/epd2in9" import ( "image/color" - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" ) type Config struct { @@ -28,10 +29,10 @@ type Config struct { type Device struct { bus drivers.SPI - cs machine.Pin - dc machine.Pin - rst machine.Pin - busy machine.Pin + cs drivers.PinOutput + dc drivers.PinOutput + rst drivers.PinOutput + isBusy drivers.PinInput logicalWidth int16 width int16 height int16 @@ -61,17 +62,17 @@ var lutPartialUpdate = [30]uint8{ } // New returns a new epd2in9 driver. Pass in a fully configured SPI bus. -func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device { - csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - busyPin.Configure(machine.PinConfig{Mode: machine.PinInput}) +func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device { + legacy.ConfigurePinOut(csPin) + legacy.ConfigurePinOut(dcPin) + legacy.ConfigurePinOut(rstPin) + legacy.ConfigurePinInput(busyPin) return Device{ - bus: bus, - cs: csPin, - dc: dcPin, - rst: rstPin, - busy: busyPin, + bus: bus, + cs: csPin.Set, + dc: dcPin.Set, + rst: rstPin.Set, + isBusy: busyPin.Get, } } @@ -245,14 +246,14 @@ func (d *Device) setMemoryPointer(x int16, y int16) { // WaitUntilIdle waits until the display is ready func (d *Device) WaitUntilIdle() { - for d.busy.Get() { + for d.isBusy() { time.Sleep(100 * time.Millisecond) } } // IsBusy returns the busy status of the display func (d *Device) IsBusy() bool { - return d.busy.Get() + return d.isBusy() } // ClearBuffer sets the buffer to 0xFF (white) diff --git a/waveshare-epd/epd4in2/epd4in2.go b/waveshare-epd/epd4in2/epd4in2.go index 902d21e2b..772b834bf 100644 --- a/waveshare-epd/epd4in2/epd4in2.go +++ b/waveshare-epd/epd4in2/epd4in2.go @@ -10,10 +10,11 @@ package epd4in2 import ( "image/color" - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" + "tinygo.org/x/drivers/internal/pin" ) type Config struct { @@ -25,10 +26,10 @@ type Config struct { type Device struct { bus drivers.SPI - cs machine.Pin - dc machine.Pin - rst machine.Pin - busy machine.Pin + cs drivers.PinOutput + dc drivers.PinOutput + rst drivers.PinOutput + isBusy drivers.PinInput logicalWidth int16 width int16 height int16 @@ -40,17 +41,17 @@ type Device struct { type Rotation uint8 // New returns a new epd4in2 driver. Pass in a fully configured SPI bus. -func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device { - csPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput}) - busyPin.Configure(machine.PinConfig{Mode: machine.PinInput}) +func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device { + legacy.ConfigurePinOut(csPin) + legacy.ConfigurePinOut(dcPin) + legacy.ConfigurePinOut(rstPin) + legacy.ConfigurePinInput(busyPin) return Device{ - bus: bus, - cs: csPin, - dc: dcPin, - rst: rstPin, - busy: busyPin, + bus: bus, + cs: csPin.Set, + dc: dcPin.Set, + rst: rstPin.Set, + isBusy: busyPin.Get, } } @@ -311,14 +312,14 @@ func (d *Device) ClearDisplay() { // WaitUntilIdle waits until the display is ready func (d *Device) WaitUntilIdle() { - for d.busy.Get() { + for d.isBusy() { time.Sleep(100 * time.Millisecond) } } // IsBusy returns the busy status of the display func (d *Device) IsBusy() bool { - return d.busy.Get() + return d.isBusy() } // ClearBuffer sets the buffer to 0xFF (white)