Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions internal/legacy/pinconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package legacy

import (
"errors"

"tinygo.org/x/drivers/internal/pin"
)

// The pingconfig group of files serve to abstract away
// pin configuration calls on the machine.Pin type.
// It was observed this way of developing drivers was
// non-portable and unusable on "big" Go projects so
// future projects should NOT configure pins in driver code.
// Users must configure pins before passing them as arguments
// to drivers.

// 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")
)
15 changes: 15 additions & 0 deletions internal/legacy/pinconfig_go.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build !tinygo

package legacy

import "tinygo.org/x/drivers/internal/pin"

// This file compiles for non-tinygo builds
// for use with "big" or "upstream" Go where
// there is no machine package.

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 }
10 changes: 10 additions & 0 deletions internal/legacy/pinconfig_nopulls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//go:build baremetal && fe310

package legacy

import "machine"

const (
pulldown = machine.PinInput
pullup = machine.PinInput
)
13 changes: 13 additions & 0 deletions internal/legacy/pinconfig_pulls.go
Original file line number Diff line number Diff line change
@@ -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
)
37 changes: 37 additions & 0 deletions internal/legacy/pinconfig_tinygo.go
Original file line number Diff line number Diff line change
@@ -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})
}
}
28 changes: 28 additions & 0 deletions internal/pin/internalpin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package pin

import "tinygo.org/x/drivers"

// This file contains interface-style Pin HAL definition.
// It serves to eliminate machine.Pin from driver constructors
// so that drivers can be used in "big" Go projects where
// there is no machine package.

// 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)
}
60 changes: 60 additions & 0 deletions pin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package drivers

// TinyGo Pin HAL (exported)
//
// Pins are represented as function types instead of interfaces because they are:
// - Faster on MCUs
// - Conceptually simpler than an interface
// - Very likely easier to teach than interface:
// - Cleaner at call sites (e.g., isBusy := d.isBusy() vs d.isBusy.Get()).
// - Enable inline funcs at the usage site- less boilerplate than defining
// an interface type + method (see example below).
// - Less prone to “method creep”: the API surface stays small while
// advanced behavior can be composed in user code.
// Example: a pin that can act as both output and input without adding methods.
//
// var pinIsOutput bool
// var po PinOutput = func(b bool) {
// if !pinIsOutput {
// pin.Configure(outputMode)
// pinIsOutput = true
// }
// pin.Set(b)
// }
// var pi PinInput = func() bool {
// if pinIsOutput {
// pin.Configure(inputMode)
// pinIsOutput = false
// }
// return pin.Get()
// }
//
// See https://github.com/tinygo-org/drivers/pull/753 for detailed commentary.

// 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 (setPin PinOutput) High() {
setPin(true)
}

// Low sets the underlying pin's level to low. This is equivalent to calling PinOutput(false).
func (setPin PinOutput) Low() {
setPin(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)
40 changes: 23 additions & 17 deletions uc8151/uc8151.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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
Expand All @@ -49,17 +50,22 @@ 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})
// Pins passed in must be configured beforehand.
func New(bus drivers.SPI, csPin, dcPin, rstPin pin.Output, busyPin pin.Input) Device {
// For backwards compatibility.
// This driver used to configure pins,
// so leave in to not break users.
// May be removed in future so try not to depend on it!
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,
}
}

Expand Down Expand Up @@ -313,14 +319,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)
Expand Down