From 06e4c0267f699ab316f0c5064ba3f05f40e050cf Mon Sep 17 00:00:00 2001 From: soypat Date: Tue, 8 Apr 2025 17:08:08 -0300 Subject: [PATCH 1/6] add PinInput and PinOutput HAL --- pins.go | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 pins.go diff --git a/pins.go b/pins.go new file mode 100644 index 000000000..a7662cb5f --- /dev/null +++ b/pins.go @@ -0,0 +1,9 @@ +package drivers + +// PinInput is hardware abstraction for a pin which receives a +// digital signal and reads it (high or low voltage). +type PinInput func() (level bool) + +// PinOutput is hardware abstraction for a pin which outputs a +// digital signal (high or low voltage). +type PinOutput func(level bool) From f3945b1d588c93a51ebe4c6d04dc7743ccab2116 Mon Sep 17 00:00:00 2001 From: soypat Date: Wed, 30 Apr 2025 15:18:21 -0300 Subject: [PATCH 2/6] apply suggestions from team --- apa102/apa102.go | 9 +- apa102/softspi.go | 26 +- bmi160/bmi160.go | 37 ++- buzzer/buzzer.go | 15 +- internal/legacy/pinhal.go | 29 ++ internal/legacy/pinhal_baremetal.go | 12 + internal/legacy/pinhal_os.go | 5 + pins.go | 8 +- st7735/st7735v2.go | 475 ++++++++++++++++++++++++++++ 9 files changed, 573 insertions(+), 43 deletions(-) create mode 100644 internal/legacy/pinhal.go create mode 100644 internal/legacy/pinhal_baremetal.go create mode 100644 internal/legacy/pinhal_os.go create mode 100644 st7735/st7735v2.go diff --git a/apa102/apa102.go b/apa102/apa102.go index 98253bfdf..f383f7320 100644 --- a/apa102/apa102.go +++ b/apa102/apa102.go @@ -5,9 +5,9 @@ package apa102 // import "tinygo.org/x/drivers/apa102" import ( "image/color" - "machine" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" ) const ( @@ -37,8 +37,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 legacy.PinOutput, delay uint32) *Device { + return New(&bbSPI{SCK: sckPin.Set, SDO: sdoPin.Set, Delay: delay, config: 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..20ca87ad7 100644 --- a/apa102/softspi.go +++ b/apa102/softspi.go @@ -1,6 +1,8 @@ package apa102 -import "machine" +import ( + "tinygo.org/x/drivers" +) // 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,17 +10,17 @@ 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 + config 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}) - s.SCK.Low() - s.SDO.Low() + s.config() + s.SCK(false) + s.SDO(false) if s.Delay == 0 { s.Delay = 1 } @@ -47,19 +49,19 @@ func (s *bbSPI) Transfer(b byte) (byte, error) { for i := uint8(0); i < 8; i++ { // half clock cycle high to start - s.SCK.High() + s.SCK(true) s.delay() // write the value to SDO (MSB first) if b&(1<<(7-i)) == 0 { - s.SDO.Low() + s.SDO(false) } else { - s.SDO.High() + s.SDO(true) } s.delay() // half clock cycle low - s.SCK.Low() + s.SCK(false) s.delay() // for actual SPI would try to read the SDI value here diff --git a/bmi160/bmi160.go b/bmi160/bmi160.go index cd3e88237..3bb788c49 100644 --- a/bmi160/bmi160.go +++ b/bmi160/bmi160.go @@ -1,31 +1,35 @@ 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 // SPI bus (requires chip select to be usable). - Bus drivers.SPI + Bus drivers.SPI + config 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 legacy.PinOutput, spi drivers.SPI) *DeviceSPI { return &DeviceSPI{ - CSB: csb, // chip select + CSB: csb.Set, // chip select Bus: spi, + config: func() { + legacy.ConfigurePinOut(csb) + }, } } @@ -33,8 +37,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(true) // The datasheet recommends doing a register read from address 0x7F to get // SPI communication going: @@ -86,9 +89,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(false) err = d.Bus.Tx(data, data) - d.CSB.High() + d.CSB(true) if err != nil { return } @@ -123,9 +126,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(false) err = d.Bus.Tx(data, data) - d.CSB.High() + d.CSB(true) if err != nil { return } @@ -153,9 +156,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(false) err = d.Bus.Tx(data, data) - d.CSB.High() + d.CSB(true) if err != nil { return } @@ -201,9 +204,9 @@ func (d *DeviceSPI) readRegister(address uint8) uint8 { data := d.buf[:2] data[0] = 0x80 | address data[1] = 0 - d.CSB.Low() + d.CSB(false) d.Bus.Tx(data, data) - d.CSB.High() + d.CSB(true) return data[1] } @@ -217,7 +220,7 @@ func (d *DeviceSPI) writeRegister(address, data uint8) { buf[0] = address buf[1] = data - d.CSB.Low() + d.CSB(false) d.Bus.Tx(buf, buf) - d.CSB.High() + d.CSB(true) } diff --git a/buzzer/buzzer.go b/buzzer/buzzer.go index 04c112953..b7f92f6ae 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/legacy" ) // 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 legacy.PinOutput) 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(true) l.High = true return } // Off sets the buzzer to a low state. func (l *Device) Off() (err error) { - l.pin.Set(false) + l.pin(false) l.High = false return } diff --git a/internal/legacy/pinhal.go b/internal/legacy/pinhal.go new file mode 100644 index 000000000..5052a4dd4 --- /dev/null +++ b/internal/legacy/pinhal.go @@ -0,0 +1,29 @@ +package legacy + +// PinOutput 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. Pros and cons +// of both approaches have been discussed in the [relevant issue]. PinOutput should only be used +// to expose an initialization function of a driver that receives pins of this type. Ideally +// driver developers should also expose the initialization with drivers.Pin type: +// +// func New(p1, p2, p3 legacy.PinOutput) *Device { +// return NewWithPinfuncs(p1.Set, p2.Set, p3.Set) +// } +// +// func NewWithPinfuncs(p1, p2, p3 drivers.PinOutput) *Device { +// return &Device{p1:p1, p2:p2, p3:p3} +// } +// +// [relevant issue]: https://github.com/tinygo-org/drivers/pull/749/files +type PinOutput interface { + Set(level bool) +} + +// 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(p PinOutput) { + configurePinOut(p) +} diff --git a/internal/legacy/pinhal_baremetal.go b/internal/legacy/pinhal_baremetal.go new file mode 100644 index 000000000..ac7c31f69 --- /dev/null +++ b/internal/legacy/pinhal_baremetal.go @@ -0,0 +1,12 @@ +//go:build baremetal + +package legacy + +import "machine" + +func configurePinOut(p PinOutput) { + machinePin, ok := p.(machine.Pin) + if ok { + machinePin.Configure(machine.PinConfig{Mode: machine.PinOutput}) + } +} diff --git a/internal/legacy/pinhal_os.go b/internal/legacy/pinhal_os.go new file mode 100644 index 000000000..14d12db4c --- /dev/null +++ b/internal/legacy/pinhal_os.go @@ -0,0 +1,5 @@ +//go:build !baremetal + +package legacy + +func configurePinOut(p PinOutput) {} diff --git a/pins.go b/pins.go index a7662cb5f..ae81ac103 100644 --- a/pins.go +++ b/pins.go @@ -1,9 +1,9 @@ package drivers -// PinInput is hardware abstraction for a pin which receives a -// digital signal and reads it (high or low voltage). -type PinInput func() (level bool) - // PinOutput is hardware abstraction for a pin which outputs a // digital signal (high or low voltage). type PinOutput func(level bool) + +// PinInput is hardware abstraction for a pin which receives a +// digital signal and reads it (high or low voltage). +type PinInput func() (level bool) diff --git a/st7735/st7735v2.go b/st7735/st7735v2.go new file mode 100644 index 000000000..91a27f3d2 --- /dev/null +++ b/st7735/st7735v2.go @@ -0,0 +1,475 @@ +// Package st7735 implements a driver for the ST7735 TFT displays, it comes in various screen sizes. +// +// Datasheet: https://www.crystalfontz.com/controllers/Sitronix/ST7735R/319/ +package st7735 // import "tinygo.org/x/drivers/st7735" + +import ( + "image/color" + "time" + + "errors" + + "tinygo.org/x/drivers" + "tinygo.org/x/drivers/pixel" +) + +type Model uint8 + +// Pixel formats supported by the st7735 driver. +type Color interface { + pixel.RGB444BE | pixel.RGB565BE + + pixel.BaseColor +} + +var ( + errOutOfBounds = errors.New("rectangle coordinates outside display area") +) + +// Device wraps an SPI connection. +type Device = DeviceOf[pixel.RGB565BE] + +// DeviceOf is a generic version of Device, which supports different pixel +// formats. +type DeviceOf[T Color] struct { + bus drivers.SPI + dcPin drivers.PinOutput + resetPin drivers.PinOutput + csPin drivers.PinOutput + blPin drivers.PinOutput + width int16 + height int16 + columnOffset int16 + rowOffset int16 + rotation drivers.Rotation + batchLength int16 + model Model + isBGR bool + batchData pixel.Image[T] // "image" with width, height of (batchLength, 1) +} + +// Config is the configuration for the display +type Config struct { + Width int16 + Height int16 + Rotation drivers.Rotation + Model Model + RowOffset int16 + ColumnOffset int16 +} + +// New creates a new ST7735 connection. The SPI wire must already be configured. +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 drivers.PinOutput) DeviceOf[T] { + return DeviceOf[T]{ + bus: bus, + dcPin: dcPin, + resetPin: resetPin, + csPin: csPin, + blPin: blPin, + } +} + +func (d *DeviceOf[T]) Reset() { + d.resetPin(true) + time.Sleep(5 * time.Millisecond) + d.resetPin(false) + time.Sleep(20 * time.Millisecond) + d.resetPin(true) + time.Sleep(150 * time.Millisecond) +} + +// Configure initializes the display with default configuration +func (d *DeviceOf[T]) Configure(cfg Config) { + d.model = cfg.Model + if cfg.Width != 0 { + d.width = cfg.Width + } else { + if d.model == MINI80x160 { + d.width = 80 + } else { + d.width = 128 + } + } + if cfg.Height != 0 { + d.height = cfg.Height + } else { + d.height = 160 + } + d.rotation = cfg.Rotation + d.rowOffset = cfg.RowOffset + d.columnOffset = cfg.ColumnOffset + + d.batchLength = d.width + if d.height > d.width { + d.batchLength = d.height + } + d.batchLength += d.batchLength & 1 + d.batchData = pixel.NewImage[T](int(d.batchLength), 1) + + d.Reset() + + // Common initialization + d.Command(SWRESET) + time.Sleep(150 * time.Millisecond) + d.Command(SLPOUT) + time.Sleep(500 * time.Millisecond) + d.Command(FRMCTR1) + d.Data(0x01) + d.Data(0x2C) + d.Data(0x2D) + d.Command(FRMCTR2) + d.Data(0x01) + d.Data(0x2C) + d.Data(0x2D) + d.Command(FRMCTR3) + d.Data(0x01) + d.Data(0x2C) + d.Data(0x2D) + d.Data(0x01) + d.Data(0x2C) + d.Data(0x2D) + d.Command(INVCTR) + d.Data(0x07) + d.Command(PWCTR1) + d.Data(0xA2) + d.Data(0x02) + d.Data(0x84) + d.Command(PWCTR2) + d.Data(0xC5) + d.Command(PWCTR3) + d.Data(0x0A) + d.Data(0x00) + d.Command(PWCTR4) + d.Data(0x8A) + d.Data(0x2A) + d.Command(PWCTR5) + d.Data(0x8A) + d.Data(0xEE) + d.Command(VMCTR1) + d.Data(0x0E) + + // Set the color format depending on the generic type. + d.Command(COLMOD) + var zeroColor T + switch any(zeroColor).(type) { + case pixel.RGB444BE: + d.Data(0x03) // 12 bits per pixel + default: + d.Data(0x05) // 16 bits per pixel + } + + if d.model == GREENTAB { + d.InvertColors(false) + } else if d.model == MINI80x160 { + d.isBGR = true + d.InvertColors(true) + } + + // common color adjustment + d.Command(GMCTRP1) + + d.Data(0x02) + d.Data(0x1C) + d.Data(0x07) + d.Data(0x12) + d.Data(0x37) + d.Data(0x32) + d.Data(0x29) + d.Data(0x2D) + d.Data(0x29) + d.Data(0x25) + d.Data(0x2B) + d.Data(0x39) + d.Data(0x00) + d.Data(0x01) + d.Data(0x03) + d.Data(0x10) + d.Command(GMCTRN1) + d.Data(0x03) + d.Data(0x1D) + d.Data(0x07) + d.Data(0x06) + d.Data(0x2E) + d.Data(0x2C) + d.Data(0x29) + d.Data(0x2D) + d.Data(0x2E) + d.Data(0x2E) + d.Data(0x37) + d.Data(0x3F) + d.Data(0x00) + d.Data(0x00) + d.Data(0x02) + d.Data(0x10) + + d.Command(NORON) + time.Sleep(10 * time.Millisecond) + d.Command(DISPON) + time.Sleep(500 * time.Millisecond) + + if cfg.Model == MINI80x160 { + d.Command(MADCTL) + d.Data(0xC0) + } + + d.SetRotation(d.rotation) + + d.blPin(true) +} + +// Display does nothing, there's no buffer as it might be too big for some boards +func (d *DeviceOf[T]) Display() error { + return nil +} + +// SetPixel sets a pixel in the screen +func (d *DeviceOf[T]) SetPixel(x int16, y int16, c color.RGBA) { + w, h := d.Size() + if x < 0 || y < 0 || x >= w || y >= h { + return + } + d.FillRectangle(x, y, 1, 1, c) +} + +// setWindow prepares the screen to be modified at a given rectangle +func (d *DeviceOf[T]) setWindow(x, y, w, h int16) { + if d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180 { + x += d.columnOffset + y += d.rowOffset + } else { + x += d.rowOffset + y += d.columnOffset + } + d.Tx([]uint8{CASET}, true) + d.Tx([]uint8{uint8(x >> 8), uint8(x), uint8((x + w - 1) >> 8), uint8(x + w - 1)}, false) + d.Tx([]uint8{RASET}, true) + d.Tx([]uint8{uint8(y >> 8), uint8(y), uint8((y + h - 1) >> 8), uint8(y + h - 1)}, false) + d.Command(RAMWR) +} + +// SetScrollWindow sets an area to scroll with fixed top and bottom parts of the display +func (d *DeviceOf[T]) SetScrollArea(topFixedArea, bottomFixedArea int16) { + // TODO: this code is broken, see the st7789 and ili9341 implementations for + // how to do this correctly. + d.Command(VSCRDEF) + d.Tx([]uint8{ + uint8(topFixedArea >> 8), uint8(topFixedArea), + uint8(d.height - topFixedArea - bottomFixedArea>>8), uint8(d.height - topFixedArea - bottomFixedArea), + uint8(bottomFixedArea >> 8), uint8(bottomFixedArea)}, + false) +} + +// SetScroll sets the vertical scroll address of the display. +func (d *DeviceOf[T]) SetScroll(line int16) { + d.Command(VSCRSADD) + d.Tx([]uint8{uint8(line >> 8), uint8(line)}, false) +} + +// SpotScroll returns the display to its normal state +func (d *DeviceOf[T]) StopScroll() { + d.Command(NORON) +} + +// FillRectangle fills a rectangle at a given coordinates with a color +func (d *DeviceOf[T]) FillRectangle(x, y, width, height int16, c color.RGBA) error { + k, i := d.Size() + if x < 0 || y < 0 || width <= 0 || height <= 0 || + x >= k || (x+width) > k || y >= i || (y+height) > i { + return errors.New("rectangle coordinates outside display area") + } + d.setWindow(x, y, width, height) + + d.batchData.FillSolidColor(pixel.NewColor[T](c.R, c.G, c.B)) + i = width * height + for i > 0 { + if i >= d.batchLength { + d.Tx(d.batchData.RawBuffer(), false) + } else { + d.Tx(d.batchData.Rescale(int(i), 1).RawBuffer(), false) + } + i -= d.batchLength + } + return nil +} + +// DrawRGBBitmap8 copies an RGB bitmap to the internal buffer at given coordinates +// +// Deprecated: use DrawBitmap instead. +func (d *DeviceOf[T]) DrawRGBBitmap8(x, y int16, data []uint8, w, h int16) error { + k, i := d.Size() + if x < 0 || y < 0 || w <= 0 || h <= 0 || + x >= k || (x+w) > k || y >= i || (y+h) > i { + return errOutOfBounds + } + d.setWindow(x, y, w, h) + d.Tx(data, false) + return nil +} + +// DrawBitmap copies the bitmap to the internal buffer on the screen at the +// given coordinates. It returns once the image data has been sent completely. +func (d *DeviceOf[T]) DrawBitmap(x, y int16, bitmap pixel.Image[T]) error { + width, height := bitmap.Size() + return d.DrawRGBBitmap8(x, y, bitmap.RawBuffer(), int16(width), int16(height)) +} + +// FillRectangle fills a rectangle at a given coordinates with a buffer +func (d *DeviceOf[T]) FillRectangleWithBuffer(x, y, width, height int16, buffer []color.RGBA) error { + k, l := d.Size() + if x < 0 || y < 0 || width <= 0 || height <= 0 || + x >= k || (x+width) > k || y >= l || (y+height) > l { + return errors.New("rectangle coordinates outside display area") + } + k = width * height + l = int16(len(buffer)) + if k != l { + return errors.New("buffer length does not match with rectangle size") + } + + d.setWindow(x, y, width, height) + + offset := int16(0) + for k > 0 { + for i := int16(0); i < d.batchLength; i++ { + if offset+i < l { + c := buffer[offset+i] + d.batchData.Set(int(i), 0, pixel.NewColor[T](c.R, c.G, c.B)) + } + } + if k >= d.batchLength { + d.Tx(d.batchData.RawBuffer(), false) + } else { + d.Tx(d.batchData.Rescale(int(k), 1).RawBuffer(), false) + } + k -= d.batchLength + offset += d.batchLength + } + return nil +} + +// DrawFastVLine draws a vertical line faster than using SetPixel +func (d *DeviceOf[T]) DrawFastVLine(x, y0, y1 int16, c color.RGBA) { + if y0 > y1 { + y0, y1 = y1, y0 + } + d.FillRectangle(x, y0, 1, y1-y0+1, c) +} + +// DrawFastHLine draws a horizontal line faster than using SetPixel +func (d *DeviceOf[T]) DrawFastHLine(x0, x1, y int16, c color.RGBA) { + if x0 > x1 { + x0, x1 = x1, x0 + } + d.FillRectangle(x0, y, x1-x0+1, 1, c) +} + +// FillScreen fills the screen with a given color +func (d *DeviceOf[T]) FillScreen(c color.RGBA) { + if d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180 { + d.FillRectangle(0, 0, d.width, d.height, c) + } else { + d.FillRectangle(0, 0, d.height, d.width, c) + } +} + +// Rotation returns the currently configured rotation. +func (d *DeviceOf[T]) Rotation() drivers.Rotation { + return d.rotation +} + +// SetRotation changes the rotation of the device (clock-wise) +func (d *DeviceOf[T]) SetRotation(rotation drivers.Rotation) error { + d.rotation = rotation + madctl := uint8(0) + switch rotation % 4 { + case drivers.Rotation0: + madctl = MADCTL_MX | MADCTL_MY + case drivers.Rotation90: + madctl = MADCTL_MY | MADCTL_MV + case drivers.Rotation180: + // nothing to do + case drivers.Rotation270: + madctl = MADCTL_MX | MADCTL_MV + } + if d.isBGR { + madctl |= MADCTL_BGR + } + d.Command(MADCTL) + d.Data(madctl) + return nil +} + +// Command sends a command to the display +func (d *DeviceOf[T]) Command(command uint8) { + d.Tx([]byte{command}, true) +} + +// Command sends a data to the display +func (d *DeviceOf[T]) Data(data uint8) { + d.Tx([]byte{data}, false) +} + +func (d *DeviceOf[T]) TxData(data []byte) error { + return d.Tx(data, false) +} + +// Tx sends data to the display +func (d *DeviceOf[T]) Tx(data []byte, isCommand bool) error { + d.dcPin(!isCommand) + return d.bus.Tx(data, nil) +} + +// Size returns the current size of the display. +func (d *DeviceOf[T]) Size() (w, h int16) { + if d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180 { + return d.width, d.height + } + return d.height, d.width +} + +// EnableBacklight enables or disables the backlight +func (d *DeviceOf[T]) EnableBacklight(enable bool) { + if enable { + d.blPin(true) + } else { + d.blPin(false) + } +} + +// Set the sleep mode for this LCD panel. When sleeping, the panel uses a lot +// less power. The LCD won't display an image anymore, but the memory contents +// will be kept. +func (d *DeviceOf[T]) Sleep(sleepEnabled bool) error { + if sleepEnabled { + // Shut down LCD panel. + d.Command(SLPIN) + time.Sleep(5 * time.Millisecond) // 5ms required by the datasheet + } else { + // Turn the LCD panel back on. + d.Command(SLPOUT) + // The st7735 datasheet says it is necessary to wait 120ms before + // sending another command. + time.Sleep(120 * time.Millisecond) + } + return nil +} + +// InverColors inverts the colors of the screen +func (d *DeviceOf[T]) InvertColors(invert bool) { + if invert { + d.Command(INVON) + } else { + d.Command(INVOFF) + } +} + +// IsBGR changes the color mode (RGB/BGR) +func (d *DeviceOf[T]) IsBGR(bgr bool) { + d.isBGR = bgr +} From 8f0fbaa945a7b521496666536f71b08b204baa5e Mon Sep 17 00:00:00 2001 From: soypat Date: Wed, 2 Jul 2025 22:03:58 -0300 Subject: [PATCH 3/6] more drivers are now cross platform --- apa102/softspi.go | 4 + bmi160/bmi160.go | 44 +++---- easystepper/easystepper.go | 171 +++++++++------------------ easystepper/easystepper_baremetal.go | 83 +++++++++++++ easystepper/easystepper_go.go | 26 ++++ ft6336/ft6336.go | 15 ++- gc9a01/gc9a01.go | 50 ++++---- hcsr04/hcsr04.go | 35 ++++-- internal/legacy/pinhal.go | 43 ++++++- internal/legacy/pinhal_baremetal.go | 18 ++- internal/legacy/pinhal_os.go | 5 +- max6675/max6675.go | 12 +- max72xx/max72xx.go | 26 ++-- mcp2515/mcp2515.go | 51 ++++---- pcd8544/pcd8544.go | 28 ++--- shiftregister/shiftregister.go | 41 ++++--- ssd1289/pinbus.go | 2 + ssd1289/ssd1289.go | 66 ++++++----- ssd1331/ssd1331.go | 27 +++-- ssd1351/ssd1351.go | 59 +++++---- 20 files changed, 481 insertions(+), 325 deletions(-) create mode 100644 easystepper/easystepper_baremetal.go create mode 100644 easystepper/easystepper_go.go diff --git a/apa102/softspi.go b/apa102/softspi.go index 20ca87ad7..abb4be509 100644 --- a/apa102/softspi.go +++ b/apa102/softspi.go @@ -2,6 +2,7 @@ package apa102 import ( "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" ) // bbSPI is a dumb bit-bang implementation of SPI protocol that is hardcoded @@ -18,6 +19,9 @@ type bbSPI struct { // Configure sets up the SCK and SDO pins as outputs and sets them low func (s *bbSPI) Configure() { + if s.config == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } s.config() s.SCK(false) s.SDO(false) diff --git a/bmi160/bmi160.go b/bmi160/bmi160.go index 3bb788c49..7898e30e8 100644 --- a/bmi160/bmi160.go +++ b/bmi160/bmi160.go @@ -11,12 +11,12 @@ import ( // also an I2C interface, but it is not yet supported. type DeviceSPI struct { // Chip select pin - CSB drivers.PinOutput + csb drivers.PinOutput buf [7]byte // SPI bus (requires chip select to be usable). - Bus drivers.SPI + bus drivers.SPI config func() } @@ -25,8 +25,8 @@ type DeviceSPI struct { // using this device. func NewSPI(csb legacy.PinOutput, spi drivers.SPI) *DeviceSPI { return &DeviceSPI{ - CSB: csb.Set, // chip select - Bus: spi, + csb: csb.Set, // chip select + bus: spi, config: func() { legacy.ConfigurePinOut(csb) }, @@ -37,7 +37,11 @@ func NewSPI(csb legacy.PinOutput, 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(true) + if d.config == nil { + return legacy.ErrConfigBeforeInstantiated + } + d.config() + d.csb(true) // The datasheet recommends doing a register read from address 0x7F to get // SPI communication going: @@ -89,9 +93,9 @@ func (d *DeviceSPI) ReadTemperature() (temperature int32, err error) { data[0] = 0x80 | reg_TEMPERATURE_0 data[1] = 0 data[2] = 0 - d.CSB(false) - err = d.Bus.Tx(data, data) - d.CSB(true) + d.csb(false) + err = d.bus.Tx(data, data) + d.csb(true) if err != nil { return } @@ -126,9 +130,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(false) - err = d.Bus.Tx(data, data) - d.CSB(true) + d.csb(false) + err = d.bus.Tx(data, data) + d.csb(true) if err != nil { return } @@ -156,9 +160,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(false) - err = d.Bus.Tx(data, data) - d.CSB(true) + d.csb(false) + err = d.bus.Tx(data, data) + d.csb(true) if err != nil { return } @@ -204,9 +208,9 @@ func (d *DeviceSPI) readRegister(address uint8) uint8 { data := d.buf[:2] data[0] = 0x80 | address data[1] = 0 - d.CSB(false) - d.Bus.Tx(data, data) - d.CSB(true) + d.csb(false) + d.bus.Tx(data, data) + d.csb(true) return data[1] } @@ -220,7 +224,7 @@ func (d *DeviceSPI) writeRegister(address, data uint8) { buf[0] = address buf[1] = data - d.CSB(false) - d.Bus.Tx(buf, buf) - d.CSB(true) + d.csb(false) + d.bus.Tx(buf, buf) + d.csb(true) } diff --git a/easystepper/easystepper.go b/easystepper/easystepper.go index b6b93f830..899ff1ebb 100644 --- a/easystepper/easystepper.go +++ b/easystepper/easystepper.go @@ -1,10 +1,12 @@ +//go:build baremetal + // Package easystepper provides a simple driver to rotate a 4-wire stepper motor. 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 +32,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 +46,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) { @@ -126,7 +65,7 @@ func (d *Device) Move(steps int32) { // Off turns off all motor pins func (d *Device) Off() { for _, pin := range d.pins { - pin.Low() + pin(false) } } @@ -187,28 +126,28 @@ func (d *Device) stepMotor(step uint8) { func (d *Device) stepMotor4(step uint8) { switch step { case 0: - d.pins[0].High() - d.pins[1].Low() - d.pins[2].High() - d.pins[3].Low() + d.pins[0](true) + d.pins[1](false) + d.pins[2](true) + d.pins[3](false) break case 1: - d.pins[0].Low() - d.pins[1].High() - d.pins[2].High() - d.pins[3].Low() + d.pins[0](false) + d.pins[1](true) + d.pins[2](true) + d.pins[3](false) break case 2: - d.pins[0].Low() - d.pins[1].High() - d.pins[2].Low() - d.pins[3].High() + d.pins[0](false) + d.pins[1](true) + d.pins[2](false) + d.pins[3](true) break case 3: - d.pins[0].High() - d.pins[1].Low() - d.pins[2].Low() - d.pins[3].High() + d.pins[0](true) + d.pins[1](false) + d.pins[2](false) + d.pins[3](true) break } d.stepNumber = step @@ -218,45 +157,45 @@ func (d *Device) stepMotor4(step uint8) { func (d *Device) stepMotor8(step uint8) { switch step { case 0: - d.pins[0].High() - d.pins[2].Low() - d.pins[1].Low() - d.pins[3].Low() + d.pins[0](true) + d.pins[2](false) + d.pins[1](false) + d.pins[3](false) case 1: - d.pins[0].High() - d.pins[2].High() - d.pins[1].Low() - d.pins[3].Low() + d.pins[0](true) + d.pins[2](true) + d.pins[1](false) + d.pins[3](false) case 2: - d.pins[0].Low() - d.pins[2].High() - d.pins[1].Low() - d.pins[3].Low() + d.pins[0](false) + d.pins[2](true) + d.pins[1](false) + d.pins[3](false) case 3: - d.pins[0].Low() - d.pins[2].High() - d.pins[1].High() - d.pins[3].Low() + d.pins[0](false) + d.pins[2](true) + d.pins[1](true) + d.pins[3](false) case 4: - d.pins[0].Low() - d.pins[2].Low() - d.pins[1].High() - d.pins[3].Low() + d.pins[0](false) + d.pins[2](false) + d.pins[1](true) + d.pins[3](false) case 5: - d.pins[0].Low() - d.pins[2].Low() - d.pins[1].High() - d.pins[3].High() + d.pins[0](false) + d.pins[2](false) + d.pins[1](true) + d.pins[3](true) case 6: - d.pins[0].Low() - d.pins[2].Low() - d.pins[1].Low() - d.pins[3].High() + d.pins[0](false) + d.pins[2](false) + d.pins[1](false) + d.pins[3](true) case 7: - d.pins[0].High() - d.pins[2].Low() - d.pins[1].Low() - d.pins[3].High() + d.pins[0](true) + d.pins[2](false) + d.pins[1](false) + d.pins[3](true) } d.stepNumber = step } diff --git a/easystepper/easystepper_baremetal.go b/easystepper/easystepper_baremetal.go new file mode 100644 index 000000000..ae1a7cfb8 --- /dev/null +++ b/easystepper/easystepper_baremetal.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/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/ft6336/ft6336.go b/ft6336/ft6336.go index 6b2032d43..09e2ad2dd 100644 --- a/ft6336/ft6336.go +++ b/ft6336/ft6336.go @@ -5,8 +5,6 @@ package ft6336 import ( - "machine" - "tinygo.org/x/drivers" "tinygo.org/x/drivers/internal/legacy" "tinygo.org/x/drivers/touch" @@ -17,16 +15,18 @@ type Device struct { bus drivers.I2C buf []byte Address uint8 - intPin machine.Pin + config 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 legacy.PinInput) *Device { return &Device{ bus: i2c, buf: make([]byte, 11), Address: Address, - intPin: intPin, + config: func() { + legacy.ConfigurePinInputPulldown(intPin) + }, } } @@ -36,8 +36,11 @@ type Config struct { // Configure the FT6336 device. func (d *Device) Configure(config Config) error { + if d.config == nil { + return legacy.ErrConfigBeforeInstantiated + } d.write1Byte(0xA4, 0x00) - d.intPin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) + d.config() return nil } diff --git a/gc9a01/gc9a01.go b/gc9a01/gc9a01.go index 1426241b2..ef9d2be2b 100644 --- a/gc9a01/gc9a01.go +++ b/gc9a01/gc9a01.go @@ -5,12 +5,12 @@ package gc9a01 // import "tinygo.org/x/drivers/gc9a01" import ( "image/color" - "machine" "time" "errors" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" ) // Rotation controls the rotation used by the display. @@ -22,10 +22,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,27 +52,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 { - 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 legacy.PinOutput) 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, } } // Reset the Device func (d *Device) Reset() { - d.resetPin.High() + d.resetPin(true) time.Sleep(100 * time.Millisecond) - d.resetPin.Low() + d.resetPin(false) time.Sleep(100 * time.Millisecond) - d.resetPin.High() + d.resetPin(true) time.Sleep(100 * time.Millisecond) } @@ -226,20 +226,20 @@ 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) } // Rx reads data from the display func (d *Device) Rx(command uint8, data []byte) { - d.dcPin.Low() - d.csPin.Low() + d.dcPin(false) + d.csPin(false) d.bus.Transfer(command) - d.dcPin.High() + d.dcPin(true) for i := range data { data[i], _ = d.bus.Transfer(0xFF) } - d.csPin.High() + d.csPin(true) } // Size returns the current size of the display. @@ -250,9 +250,9 @@ func (d *Device) Size() (w, h int16) { // EnableBacklight enables or disables the backlight func (d *Device) EnableBacklight(enable bool) { if enable { - d.blPin.High() + d.blPin(true) } else { - d.blPin.Low() + d.blPin(false) } } @@ -563,5 +563,5 @@ func (d *Device) Configure(cfg Config) { d.Command(DISPON) time.Sleep(20 * time.Millisecond) - d.blPin.High() + d.blPin(true) } diff --git a/hcsr04/hcsr04.go b/hcsr04/hcsr04.go index 703fa7850..77c56ee73 100644 --- a/hcsr04/hcsr04.go +++ b/hcsr04/hcsr04.go @@ -5,30 +5,39 @@ package hcsr04 import ( - "machine" "time" + + "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" ) 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 + config func() } // New returns a new ultrasonic driver given 2 pins -func New(trigger, echo machine.Pin) Device { +func New(trigger legacy.PinOutput, echo legacy.PinInput) Device { return Device{ - trigger: trigger, - echo: echo, + trigger: trigger.Set, + echo: echo.Get, + config: 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.config == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } + d.config() } // ReadDistance returns the distance of the object in mm @@ -45,14 +54,14 @@ 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(false) time.Sleep(2 * time.Microsecond) - d.trigger.High() + d.trigger(true) time.Sleep(10 * time.Microsecond) - d.trigger.Low() + d.trigger(false) i := uint8(0) for { - if d.echo.Get() { + if d.echo() { t = time.Now() break } @@ -66,7 +75,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/internal/legacy/pinhal.go b/internal/legacy/pinhal.go index 5052a4dd4..20dad4e20 100644 --- a/internal/legacy/pinhal.go +++ b/internal/legacy/pinhal.go @@ -1,5 +1,7 @@ package legacy +import "errors" + // PinOutput 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. Pros and cons // of both approaches have been discussed in the [relevant issue]. PinOutput should only be used @@ -19,11 +21,48 @@ type PinOutput interface { Set(level bool) } +// PinInput represents a pin hardware abstraction layer. See [PinOutput] for +// more information on why this is "legacy". +type PinInput interface { + Get() (level bool) +} + // 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(p PinOutput) { - configurePinOut(p) +func ConfigurePinOut(po PinOutput) { + 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 PinInput) { + 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 PinInput) { + 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 PinInput) { + configurePinInputPullup(pi) +} + +var ( + ErrConfigBeforeInstantiated = errors.New("device must be instantiated with New before calling Configure method") +) diff --git a/internal/legacy/pinhal_baremetal.go b/internal/legacy/pinhal_baremetal.go index ac7c31f69..70fe0f41b 100644 --- a/internal/legacy/pinhal_baremetal.go +++ b/internal/legacy/pinhal_baremetal.go @@ -5,8 +5,24 @@ package legacy import "machine" func configurePinOut(p PinOutput) { + configurePin(p, machine.PinInputPulldown) +} + +func configurePinInputPulldown(p PinInput) { + configurePin(p, machine.PinInputPulldown) +} + +func configurePinInput(p PinInput) { + configurePin(p, machine.PinInput) +} + +func configurePinInputPullup(p PinInput) { + configurePin(p, machine.PinInputPullup) +} + +func configurePin(p any, mode machine.PinMode) { machinePin, ok := p.(machine.Pin) if ok { - machinePin.Configure(machine.PinConfig{Mode: machine.PinOutput}) + machinePin.Configure(machine.PinConfig{Mode: mode}) } } diff --git a/internal/legacy/pinhal_os.go b/internal/legacy/pinhal_os.go index 14d12db4c..394f42f7e 100644 --- a/internal/legacy/pinhal_os.go +++ b/internal/legacy/pinhal_os.go @@ -2,4 +2,7 @@ package legacy -func configurePinOut(p PinOutput) {} +func configurePinOut(p PinOutput) {} +func configurePinInput(p PinInput) {} +func configurePinInputPulldown(p PinInput) {} +func configurePinInputPullup(p PinInput) {} diff --git a/max6675/max6675.go b/max6675/max6675.go index eb67596b6..ce4b09db3 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/legacy" ) // 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 legacy.PinOutput) *Device { return &Device{ bus: bus, - cs: cs, + cs: cs.Set, } } @@ -34,11 +34,11 @@ func (d *Device) Read() (float32, error) { value uint16 ) - d.cs.Low() + d.cs(false) if err := d.bus.Tx([]byte{0, 0}, read); err != nil { return 0, err } - d.cs.High() + d.cs(true) // datasheet: Bit D2 is normally low and goes high if the thermocouple input is open. if read[1]&0x04 == 0x04 { diff --git a/max72xx/max72xx.go b/max72xx/max72xx.go index ca6c81929..19232b5fa 100644 --- a/max72xx/max72xx.go +++ b/max72xx/max72xx.go @@ -3,31 +3,35 @@ package max72xx import ( - "machine" - "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" ) type Device struct { - bus drivers.SPI - cs machine.Pin + bus drivers.SPI + cs drivers.PinOutput + config 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 legacy.PinOutput) *Device { return &Device{ bus: bus, - cs: cs, + cs: cs.Set, + config: func() { + legacy.ConfigurePinOut(cs) + }, } } // Configure setups the pins. func (driver *Device) Configure() { - outPutConfig := machine.PinConfig{Mode: machine.PinOutput} - - driver.cs.Configure(outPutConfig) + if driver.config == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } + driver.config() } // SetScanLimit sets the scan limit. Maximum is 8. @@ -89,8 +93,8 @@ func (driver *Device) writeByte(data byte) { // WriteCommand write data to a given register. func (driver *Device) WriteCommand(register, data byte) { - driver.cs.Low() + driver.cs(false) driver.writeByte(register) driver.writeByte(data) - driver.cs.High() + driver.cs(true) } diff --git a/mcp2515/mcp2515.go b/mcp2515/mcp2515.go index 2b636b19b..db520eac3 100644 --- a/mcp2515/mcp2515.go +++ b/mcp2515/mcp2515.go @@ -8,18 +8,19 @@ package mcp2515 // import "tinygo.org/x/drivers/mcp2515" import ( "errors" "fmt" - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" ) // Device wraps MCP2515 SPI CAN Module. type Device struct { spi SPI - cs machine.Pin + cs drivers.PinOutput msg *CANMsg mcpMode byte + config func() } // CANMsg stores CAN message fields. @@ -36,15 +37,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 legacy.PinOutput) *Device { d := &Device{ spi: SPI{ bus: b, tx: make([]byte, 0, bufferSize), rx: make([]byte, 0, bufferSize), }, - cs: csPin, + cs: csPin.Set, msg: &CANMsg{}, + config: func() { + legacy.ConfigurePinOut(csPin) + }, } return d @@ -52,7 +56,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.config == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } + d.config() } const beginTimeoutValue int = 10 @@ -159,9 +166,9 @@ func (d *Device) init(speed, clock byte) error { // Reset resets mcp2515. func (d *Device) Reset() error { - d.cs.Low() + d.cs(false) _, err := d.spi.readWrite(mcpReset) - d.cs.High() + d.cs(true) // time.Sleep(time.Microsecond * 4) if err != nil { return err @@ -449,8 +456,8 @@ func (d *Device) readMsg() error { func (d *Device) readRxBuffer(loadAddr uint8) error { msg := d.msg - d.cs.Low() - defer d.cs.High() + d.cs(false) + defer d.cs(true) _, err := d.spi.readWrite(loadAddr) if err != nil { return err @@ -517,8 +524,8 @@ func (d *Device) getNextFreeTxBuf() (uint8, uint8, error) { } func (d *Device) writeCANMsg(bufNum uint8, canid uint32, ext, rtrBit, dlc uint8, data []byte) error { - d.cs.Low() - defer d.cs.High() + d.cs(false) + defer d.cs(true) _, err := d.spi.readWrite(txSidhToLoad(bufNum)) if err != nil { return err @@ -537,7 +544,7 @@ func (d *Device) writeCANMsg(bufNum uint8, canid uint32, ext, rtrBit, dlc uint8, } // Since cs.Low and cs.High are executed in d.startTransmission, // it is necessary to set cs.High once to separate the instruction of mcp2515. - d.cs.High() + d.cs(true) err = d.startTransmission(bufNum) if err != nil { @@ -605,9 +612,9 @@ func (s *SPI) setTxBufData(canid uint32, ext, rtrBit, dlc uint8, data []byte) er } func (d *Device) startTransmission(bufNum uint8) error { - d.cs.Low() + d.cs(false) _, err := d.spi.readWrite(txSidhToRTS(bufNum)) - d.cs.High() + d.cs(true) if err != nil { return err } @@ -701,8 +708,8 @@ func txSidhToLoad(i uint8) uint8 { } func (d *Device) setRegister(addr, value byte) error { - d.cs.Low() - defer d.cs.High() + d.cs(false) + defer d.cs(true) _, err := d.spi.readWrite(mcpWrite) if err != nil { return err @@ -721,8 +728,8 @@ func (d *Device) setRegister(addr, value byte) error { } func (d *Device) readRegister(addr byte) (byte, error) { - d.cs.Low() - defer d.cs.High() + d.cs(false) + defer d.cs(true) _, err := d.spi.readWrite(mcpRead) if err != nil { return 0, err @@ -740,8 +747,8 @@ func (d *Device) readRegister(addr byte) (byte, error) { } func (d *Device) modifyRegister(addr, mask, data byte) error { - d.cs.Low() - defer d.cs.High() + d.cs(false) + defer d.cs(true) _, err := d.spi.readWrite(mcpBitMod) if err != nil { return err @@ -783,8 +790,8 @@ func (d *Device) requestNewMode(newMode byte) error { } func (d *Device) readStatus() (byte, error) { - d.cs.Low() - defer d.cs.High() + d.cs(false) + defer d.cs(true) _, err := d.spi.readWrite(mcpReadStatus) if err != nil { return 0, err diff --git a/pcd8544/pcd8544.go b/pcd8544/pcd8544.go index 3a843cbab..699d59e87 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/legacy" ) // 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 legacy.PinOutput) *Device { return &Device{ bus: bus, - dcPin: dcPin, - rstPin: rstPin, - scePin: scePin, + dcPin: dcPin.Set, + rstPin: rstPin.Set, + scePin: scePin.Set, } } @@ -54,9 +54,9 @@ func (d *Device) Configure(cfg Config) { d.bufferSize = d.width * d.height / 8 d.buffer = make([]byte, d.bufferSize) - d.rstPin.Low() + d.rstPin(false) time.Sleep(100 * time.Nanosecond) - d.rstPin.High() + d.rstPin(true) d.SendCommand(FUNCTIONSET | EXTENDEDINSTRUCTION) // H = 1 d.SendCommand(SETVOP | 0x3f) // 0x3f : Vop6 = 0, Vop5 to Vop0 = 1 d.SendCommand(SETTEMP | 0x03) // Experimentally determined @@ -91,13 +91,13 @@ func (d *Device) Display() error { // sendDataCommand sends image data or a command to the screen func (d *Device) sendDataCommand(isCommand bool, data uint8) { if isCommand { - d.dcPin.Low() + d.dcPin(false) } else { - d.dcPin.High() + d.dcPin(true) } - d.scePin.Low() + d.scePin(false) d.bus.Transfer(data) - d.scePin.High() + d.scePin(true) } // SetPixel enables or disables a pixel in the buffer diff --git a/shiftregister/shiftregister.go b/shiftregister/shiftregister.go index 95f1f20c9..d19d46da1 100644 --- a/shiftregister/shiftregister.go +++ b/shiftregister/shiftregister.go @@ -2,7 +2,8 @@ package shiftregister import ( - "machine" + "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" ) type NumberBit int8 @@ -16,9 +17,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,35 +31,40 @@ 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 legacy.PinOutput) *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}) - d.latch.High() + if d.config == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } + d.latch(true) } // WriteMask applies mask's bits to register's outputs pin // mask's MSB set Q1, LSB set Q8 (for 8 bits mask) func (d *Device) WriteMask(mask uint32) { d.mask = mask // Keep the mask for individual addressing - d.latch.Low() + d.latch(false) for i := 0; i < int(d.bits); i++ { - d.clock.Low() - d.out.Set(mask&1 != 0) + d.clock(false) + d.out(mask&1 != 0) mask = mask >> 1 - d.clock.High() + d.clock(true) } - d.latch.High() + d.latch(true) } // GetShiftPin return an individually addressable pin 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..e95a3754a 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,32 +29,31 @@ 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, } + legacy.ConfigurePinOut(rs) + legacy.ConfigurePinOut(wr) + legacy.ConfigurePinOut(cs) + legacy.ConfigurePinOut(rst) - 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() + cs.Set(true) + rst.Set(true) + wr.Set(true) return d } func (d *Device) lcdWriteCom(cmd Command) { - d.rs.Low() + d.rs(false) d.lcdWriteBusInt(uint16(cmd)) } func (d *Device) lcdWriteDataInt(data uint16) { - d.rs.High() + d.rs(true) d.lcdWriteBusInt(data) } @@ -61,8 +63,8 @@ func (d *Device) lcdWriteComData(cmd Command, data uint16) { } func (d *Device) tx() { - d.wr.Low() - d.wr.High() + d.wr(false) + d.wr(true) } func (d *Device) lcdWriteBusInt(data uint16) { @@ -71,13 +73,13 @@ func (d *Device) lcdWriteBusInt(data uint16) { } func (d *Device) Configure() { - d.rst.High() + d.rst(true) time.Sleep(time.Millisecond * 5) - d.rst.Low() + d.rst(false) time.Sleep(time.Millisecond * 15) - d.rst.High() + d.rst(true) time.Sleep(time.Millisecond * 15) - d.cs.Low() + d.cs(false) //Power supply setting d.lcdWriteComData(POWERCONTROL1, 0xA8A4) @@ -137,7 +139,7 @@ func (d *Device) Configure() { //MUX 319 --> Number of lines in display d.lcdWriteComData(DRIVEROUTPUTCONTROL, 0x233F) - d.cs.High() + d.cs(true) } @@ -167,25 +169,25 @@ func (d *Device) SetPixel(x, y int16, c color.RGBA) { encoded := encodeColor(c) - d.cs.Low() + d.cs(false) d.setXY(uint16(x), uint16(y), uint16(x), uint16(y)) - d.rs.High() + d.rs(true) d.lcdWriteBusInt(encoded) - d.cs.High() + d.cs(true) } func (d *Device) FillRect(x, y, w, h int16, c color.RGBA) { encoded := encodeColor(c) - d.cs.Low() + d.cs(false) d.setXY(uint16(x), uint16(y), uint16(x+(w-1)), uint16(y+(h-1))) - d.rs.High() + d.rs(true) d.bus.Set(encoded) for i := int64(0); i < int64(w)*int64(h); i++ { d.tx() } - d.cs.High() - d.rs.Low() + d.cs(true) + d.rs(false) } diff --git a/ssd1331/ssd1331.go b/ssd1331/ssd1331.go index 0717c48e1..07614fe39 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, } } @@ -69,11 +70,11 @@ func (d *Device) Configure(cfg Config) { d.batchData = make([]uint8, d.batchLength*2) // reset the device - d.resetPin.High() + d.resetPin(true) time.Sleep(100 * time.Millisecond) - d.resetPin.Low() + d.resetPin(false) time.Sleep(100 * time.Millisecond) - d.resetPin.High() + d.resetPin(true) time.Sleep(200 * time.Millisecond) // Initialization @@ -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..072b04859 100644 --- a/ssd1351/ssd1351.go +++ b/ssd1351/ssd1351.go @@ -6,10 +6,10 @@ package ssd1351 // import "tinygo.org/x/drivers/ssd1351" import ( "errors" "image/color" - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" ) var ( @@ -20,11 +20,12 @@ 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 + dcPin drivers.PinOutput + resetPin drivers.PinOutput + csPin drivers.PinOutput + enPin drivers.PinOutput + rwPin drivers.PinOutput + config func() width int16 height int16 rowOffset int16 @@ -41,19 +42,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 legacy.PinOutput) 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, + config: 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.config == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } if cfg.Width == 0 { cfg.Width = 128 } @@ -73,23 +84,19 @@ 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.config() // reset the device - d.resetPin.High() + d.resetPin(true) time.Sleep(100 * time.Millisecond) - d.resetPin.Low() + d.resetPin(false) time.Sleep(100 * time.Millisecond) - d.resetPin.High() + d.resetPin(true) time.Sleep(200 * time.Millisecond) - d.rwPin.Low() - d.dcPin.Low() - d.enPin.High() + d.rwPin(false) + d.dcPin(false) + d.enPin(true) // Initialization d.Command(SET_COMMAND_LOCK) @@ -278,10 +285,10 @@ 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.csPin.Low() + d.dcPin(!isCommand) + d.csPin(false) d.bus.Tx(data, nil) - d.csPin.High() + d.csPin(true) } // Size returns the current size of the display From 368145209733fc65ca41a73b0b5bc2a52c8c3b1c Mon Sep 17 00:00:00 2001 From: soypat Date: Tue, 8 Jul 2025 21:11:46 -0300 Subject: [PATCH 4/6] finish rewrites! --- easystepper/easystepper.go | 2 - internal/legacy/pinhal.go | 7 + internal/legacy/pinhal_baremetal.go | 5 + internal/legacy/pinhal_os.go | 1 + st7735/st7735.go | 44 +-- st7735/st7735v2.go | 475 ----------------------- st7789/st7789.go | 62 +-- sx127x/sx127x.go | 12 +- uc8151/uc8151.go | 48 +-- waveshare-epd/epd1in54/epd1in54.go | 67 ++-- waveshare-epd/epd2in13/epd2in13.go | 50 +-- waveshare-epd/epd2in13x/epd2in13x.go | 50 +-- waveshare-epd/epd2in66b/dev.go | 57 +-- waveshare-epd/epd2in66b/dev_baremetal.go | 30 ++ waveshare-epd/epd2in9/epd2in9.go | 50 +-- waveshare-epd/epd4in2/epd4in2.go | 50 +-- 16 files changed, 283 insertions(+), 727 deletions(-) delete mode 100644 st7735/st7735v2.go create mode 100644 waveshare-epd/epd2in66b/dev_baremetal.go diff --git a/easystepper/easystepper.go b/easystepper/easystepper.go index 899ff1ebb..4b1626f8d 100644 --- a/easystepper/easystepper.go +++ b/easystepper/easystepper.go @@ -1,5 +1,3 @@ -//go:build baremetal - // Package easystepper provides a simple driver to rotate a 4-wire stepper motor. package easystepper // import "tinygo.org/x/drivers/easystepper" diff --git a/internal/legacy/pinhal.go b/internal/legacy/pinhal.go index 20dad4e20..fe7a64c44 100644 --- a/internal/legacy/pinhal.go +++ b/internal/legacy/pinhal.go @@ -63,6 +63,13 @@ func ConfigurePinInputPullup(pi PinInput) { 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_baremetal.go b/internal/legacy/pinhal_baremetal.go index 70fe0f41b..ee4b09a03 100644 --- a/internal/legacy/pinhal_baremetal.go +++ b/internal/legacy/pinhal_baremetal.go @@ -20,6 +20,11 @@ func configurePinInputPullup(p PinInput) { configurePin(p, machine.PinInputPullup) } +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 { diff --git a/internal/legacy/pinhal_os.go b/internal/legacy/pinhal_os.go index 394f42f7e..457c24b74 100644 --- a/internal/legacy/pinhal_os.go +++ b/internal/legacy/pinhal_os.go @@ -6,3 +6,4 @@ func configurePinOut(p PinOutput) {} func configurePinInput(p PinInput) {} func configurePinInputPulldown(p PinInput) {} func configurePinInputPullup(p PinInput) {} +func pinIsNoPin(a any) bool { return false } diff --git a/st7735/st7735.go b/st7735/st7735.go index 6f15781c2..51a556f36 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,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 legacy.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 legacy.PinOutput) 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, } } @@ -114,11 +114,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(true) time.Sleep(5 * time.Millisecond) - d.resetPin.Low() + d.resetPin(false) time.Sleep(20 * time.Millisecond) - d.resetPin.High() + d.resetPin(true) time.Sleep(150 * time.Millisecond) // Common initialization @@ -226,7 +226,7 @@ func (d *DeviceOf[T]) Configure(cfg Config) { d.SetRotation(d.rotation) - d.blPin.High() + d.blPin(true) } // Display does nothing, there's no buffer as it might be too big for some boards @@ -423,7 +423,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) } @@ -438,9 +438,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(true) } else { - d.blPin.Low() + d.blPin(false) } } diff --git a/st7735/st7735v2.go b/st7735/st7735v2.go deleted file mode 100644 index 91a27f3d2..000000000 --- a/st7735/st7735v2.go +++ /dev/null @@ -1,475 +0,0 @@ -// Package st7735 implements a driver for the ST7735 TFT displays, it comes in various screen sizes. -// -// Datasheet: https://www.crystalfontz.com/controllers/Sitronix/ST7735R/319/ -package st7735 // import "tinygo.org/x/drivers/st7735" - -import ( - "image/color" - "time" - - "errors" - - "tinygo.org/x/drivers" - "tinygo.org/x/drivers/pixel" -) - -type Model uint8 - -// Pixel formats supported by the st7735 driver. -type Color interface { - pixel.RGB444BE | pixel.RGB565BE - - pixel.BaseColor -} - -var ( - errOutOfBounds = errors.New("rectangle coordinates outside display area") -) - -// Device wraps an SPI connection. -type Device = DeviceOf[pixel.RGB565BE] - -// DeviceOf is a generic version of Device, which supports different pixel -// formats. -type DeviceOf[T Color] struct { - bus drivers.SPI - dcPin drivers.PinOutput - resetPin drivers.PinOutput - csPin drivers.PinOutput - blPin drivers.PinOutput - width int16 - height int16 - columnOffset int16 - rowOffset int16 - rotation drivers.Rotation - batchLength int16 - model Model - isBGR bool - batchData pixel.Image[T] // "image" with width, height of (batchLength, 1) -} - -// Config is the configuration for the display -type Config struct { - Width int16 - Height int16 - Rotation drivers.Rotation - Model Model - RowOffset int16 - ColumnOffset int16 -} - -// New creates a new ST7735 connection. The SPI wire must already be configured. -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 drivers.PinOutput) DeviceOf[T] { - return DeviceOf[T]{ - bus: bus, - dcPin: dcPin, - resetPin: resetPin, - csPin: csPin, - blPin: blPin, - } -} - -func (d *DeviceOf[T]) Reset() { - d.resetPin(true) - time.Sleep(5 * time.Millisecond) - d.resetPin(false) - time.Sleep(20 * time.Millisecond) - d.resetPin(true) - time.Sleep(150 * time.Millisecond) -} - -// Configure initializes the display with default configuration -func (d *DeviceOf[T]) Configure(cfg Config) { - d.model = cfg.Model - if cfg.Width != 0 { - d.width = cfg.Width - } else { - if d.model == MINI80x160 { - d.width = 80 - } else { - d.width = 128 - } - } - if cfg.Height != 0 { - d.height = cfg.Height - } else { - d.height = 160 - } - d.rotation = cfg.Rotation - d.rowOffset = cfg.RowOffset - d.columnOffset = cfg.ColumnOffset - - d.batchLength = d.width - if d.height > d.width { - d.batchLength = d.height - } - d.batchLength += d.batchLength & 1 - d.batchData = pixel.NewImage[T](int(d.batchLength), 1) - - d.Reset() - - // Common initialization - d.Command(SWRESET) - time.Sleep(150 * time.Millisecond) - d.Command(SLPOUT) - time.Sleep(500 * time.Millisecond) - d.Command(FRMCTR1) - d.Data(0x01) - d.Data(0x2C) - d.Data(0x2D) - d.Command(FRMCTR2) - d.Data(0x01) - d.Data(0x2C) - d.Data(0x2D) - d.Command(FRMCTR3) - d.Data(0x01) - d.Data(0x2C) - d.Data(0x2D) - d.Data(0x01) - d.Data(0x2C) - d.Data(0x2D) - d.Command(INVCTR) - d.Data(0x07) - d.Command(PWCTR1) - d.Data(0xA2) - d.Data(0x02) - d.Data(0x84) - d.Command(PWCTR2) - d.Data(0xC5) - d.Command(PWCTR3) - d.Data(0x0A) - d.Data(0x00) - d.Command(PWCTR4) - d.Data(0x8A) - d.Data(0x2A) - d.Command(PWCTR5) - d.Data(0x8A) - d.Data(0xEE) - d.Command(VMCTR1) - d.Data(0x0E) - - // Set the color format depending on the generic type. - d.Command(COLMOD) - var zeroColor T - switch any(zeroColor).(type) { - case pixel.RGB444BE: - d.Data(0x03) // 12 bits per pixel - default: - d.Data(0x05) // 16 bits per pixel - } - - if d.model == GREENTAB { - d.InvertColors(false) - } else if d.model == MINI80x160 { - d.isBGR = true - d.InvertColors(true) - } - - // common color adjustment - d.Command(GMCTRP1) - - d.Data(0x02) - d.Data(0x1C) - d.Data(0x07) - d.Data(0x12) - d.Data(0x37) - d.Data(0x32) - d.Data(0x29) - d.Data(0x2D) - d.Data(0x29) - d.Data(0x25) - d.Data(0x2B) - d.Data(0x39) - d.Data(0x00) - d.Data(0x01) - d.Data(0x03) - d.Data(0x10) - d.Command(GMCTRN1) - d.Data(0x03) - d.Data(0x1D) - d.Data(0x07) - d.Data(0x06) - d.Data(0x2E) - d.Data(0x2C) - d.Data(0x29) - d.Data(0x2D) - d.Data(0x2E) - d.Data(0x2E) - d.Data(0x37) - d.Data(0x3F) - d.Data(0x00) - d.Data(0x00) - d.Data(0x02) - d.Data(0x10) - - d.Command(NORON) - time.Sleep(10 * time.Millisecond) - d.Command(DISPON) - time.Sleep(500 * time.Millisecond) - - if cfg.Model == MINI80x160 { - d.Command(MADCTL) - d.Data(0xC0) - } - - d.SetRotation(d.rotation) - - d.blPin(true) -} - -// Display does nothing, there's no buffer as it might be too big for some boards -func (d *DeviceOf[T]) Display() error { - return nil -} - -// SetPixel sets a pixel in the screen -func (d *DeviceOf[T]) SetPixel(x int16, y int16, c color.RGBA) { - w, h := d.Size() - if x < 0 || y < 0 || x >= w || y >= h { - return - } - d.FillRectangle(x, y, 1, 1, c) -} - -// setWindow prepares the screen to be modified at a given rectangle -func (d *DeviceOf[T]) setWindow(x, y, w, h int16) { - if d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180 { - x += d.columnOffset - y += d.rowOffset - } else { - x += d.rowOffset - y += d.columnOffset - } - d.Tx([]uint8{CASET}, true) - d.Tx([]uint8{uint8(x >> 8), uint8(x), uint8((x + w - 1) >> 8), uint8(x + w - 1)}, false) - d.Tx([]uint8{RASET}, true) - d.Tx([]uint8{uint8(y >> 8), uint8(y), uint8((y + h - 1) >> 8), uint8(y + h - 1)}, false) - d.Command(RAMWR) -} - -// SetScrollWindow sets an area to scroll with fixed top and bottom parts of the display -func (d *DeviceOf[T]) SetScrollArea(topFixedArea, bottomFixedArea int16) { - // TODO: this code is broken, see the st7789 and ili9341 implementations for - // how to do this correctly. - d.Command(VSCRDEF) - d.Tx([]uint8{ - uint8(topFixedArea >> 8), uint8(topFixedArea), - uint8(d.height - topFixedArea - bottomFixedArea>>8), uint8(d.height - topFixedArea - bottomFixedArea), - uint8(bottomFixedArea >> 8), uint8(bottomFixedArea)}, - false) -} - -// SetScroll sets the vertical scroll address of the display. -func (d *DeviceOf[T]) SetScroll(line int16) { - d.Command(VSCRSADD) - d.Tx([]uint8{uint8(line >> 8), uint8(line)}, false) -} - -// SpotScroll returns the display to its normal state -func (d *DeviceOf[T]) StopScroll() { - d.Command(NORON) -} - -// FillRectangle fills a rectangle at a given coordinates with a color -func (d *DeviceOf[T]) FillRectangle(x, y, width, height int16, c color.RGBA) error { - k, i := d.Size() - if x < 0 || y < 0 || width <= 0 || height <= 0 || - x >= k || (x+width) > k || y >= i || (y+height) > i { - return errors.New("rectangle coordinates outside display area") - } - d.setWindow(x, y, width, height) - - d.batchData.FillSolidColor(pixel.NewColor[T](c.R, c.G, c.B)) - i = width * height - for i > 0 { - if i >= d.batchLength { - d.Tx(d.batchData.RawBuffer(), false) - } else { - d.Tx(d.batchData.Rescale(int(i), 1).RawBuffer(), false) - } - i -= d.batchLength - } - return nil -} - -// DrawRGBBitmap8 copies an RGB bitmap to the internal buffer at given coordinates -// -// Deprecated: use DrawBitmap instead. -func (d *DeviceOf[T]) DrawRGBBitmap8(x, y int16, data []uint8, w, h int16) error { - k, i := d.Size() - if x < 0 || y < 0 || w <= 0 || h <= 0 || - x >= k || (x+w) > k || y >= i || (y+h) > i { - return errOutOfBounds - } - d.setWindow(x, y, w, h) - d.Tx(data, false) - return nil -} - -// DrawBitmap copies the bitmap to the internal buffer on the screen at the -// given coordinates. It returns once the image data has been sent completely. -func (d *DeviceOf[T]) DrawBitmap(x, y int16, bitmap pixel.Image[T]) error { - width, height := bitmap.Size() - return d.DrawRGBBitmap8(x, y, bitmap.RawBuffer(), int16(width), int16(height)) -} - -// FillRectangle fills a rectangle at a given coordinates with a buffer -func (d *DeviceOf[T]) FillRectangleWithBuffer(x, y, width, height int16, buffer []color.RGBA) error { - k, l := d.Size() - if x < 0 || y < 0 || width <= 0 || height <= 0 || - x >= k || (x+width) > k || y >= l || (y+height) > l { - return errors.New("rectangle coordinates outside display area") - } - k = width * height - l = int16(len(buffer)) - if k != l { - return errors.New("buffer length does not match with rectangle size") - } - - d.setWindow(x, y, width, height) - - offset := int16(0) - for k > 0 { - for i := int16(0); i < d.batchLength; i++ { - if offset+i < l { - c := buffer[offset+i] - d.batchData.Set(int(i), 0, pixel.NewColor[T](c.R, c.G, c.B)) - } - } - if k >= d.batchLength { - d.Tx(d.batchData.RawBuffer(), false) - } else { - d.Tx(d.batchData.Rescale(int(k), 1).RawBuffer(), false) - } - k -= d.batchLength - offset += d.batchLength - } - return nil -} - -// DrawFastVLine draws a vertical line faster than using SetPixel -func (d *DeviceOf[T]) DrawFastVLine(x, y0, y1 int16, c color.RGBA) { - if y0 > y1 { - y0, y1 = y1, y0 - } - d.FillRectangle(x, y0, 1, y1-y0+1, c) -} - -// DrawFastHLine draws a horizontal line faster than using SetPixel -func (d *DeviceOf[T]) DrawFastHLine(x0, x1, y int16, c color.RGBA) { - if x0 > x1 { - x0, x1 = x1, x0 - } - d.FillRectangle(x0, y, x1-x0+1, 1, c) -} - -// FillScreen fills the screen with a given color -func (d *DeviceOf[T]) FillScreen(c color.RGBA) { - if d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180 { - d.FillRectangle(0, 0, d.width, d.height, c) - } else { - d.FillRectangle(0, 0, d.height, d.width, c) - } -} - -// Rotation returns the currently configured rotation. -func (d *DeviceOf[T]) Rotation() drivers.Rotation { - return d.rotation -} - -// SetRotation changes the rotation of the device (clock-wise) -func (d *DeviceOf[T]) SetRotation(rotation drivers.Rotation) error { - d.rotation = rotation - madctl := uint8(0) - switch rotation % 4 { - case drivers.Rotation0: - madctl = MADCTL_MX | MADCTL_MY - case drivers.Rotation90: - madctl = MADCTL_MY | MADCTL_MV - case drivers.Rotation180: - // nothing to do - case drivers.Rotation270: - madctl = MADCTL_MX | MADCTL_MV - } - if d.isBGR { - madctl |= MADCTL_BGR - } - d.Command(MADCTL) - d.Data(madctl) - return nil -} - -// Command sends a command to the display -func (d *DeviceOf[T]) Command(command uint8) { - d.Tx([]byte{command}, true) -} - -// Command sends a data to the display -func (d *DeviceOf[T]) Data(data uint8) { - d.Tx([]byte{data}, false) -} - -func (d *DeviceOf[T]) TxData(data []byte) error { - return d.Tx(data, false) -} - -// Tx sends data to the display -func (d *DeviceOf[T]) Tx(data []byte, isCommand bool) error { - d.dcPin(!isCommand) - return d.bus.Tx(data, nil) -} - -// Size returns the current size of the display. -func (d *DeviceOf[T]) Size() (w, h int16) { - if d.rotation == drivers.Rotation0 || d.rotation == drivers.Rotation180 { - return d.width, d.height - } - return d.height, d.width -} - -// EnableBacklight enables or disables the backlight -func (d *DeviceOf[T]) EnableBacklight(enable bool) { - if enable { - d.blPin(true) - } else { - d.blPin(false) - } -} - -// Set the sleep mode for this LCD panel. When sleeping, the panel uses a lot -// less power. The LCD won't display an image anymore, but the memory contents -// will be kept. -func (d *DeviceOf[T]) Sleep(sleepEnabled bool) error { - if sleepEnabled { - // Shut down LCD panel. - d.Command(SLPIN) - time.Sleep(5 * time.Millisecond) // 5ms required by the datasheet - } else { - // Turn the LCD panel back on. - d.Command(SLPOUT) - // The st7735 datasheet says it is necessary to wait 120ms before - // sending another command. - time.Sleep(120 * time.Millisecond) - } - return nil -} - -// InverColors inverts the colors of the screen -func (d *DeviceOf[T]) InvertColors(invert bool) { - if invert { - d.Command(INVON) - } else { - d.Command(INVOFF) - } -} - -// IsBGR changes the color mode (RGB/BGR) -func (d *DeviceOf[T]) IsBGR(bgr bool) { - d.isBGR = bgr -} diff --git a/st7789/st7789.go b/st7789/st7789.go index 5db2402ba..f4662082b 100644 --- a/st7789/st7789.go +++ b/st7789/st7789.go @@ -7,13 +7,13 @@ 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/pixel" ) @@ -46,10 +46,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 +83,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 legacy.PinOutput) 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 legacy.PinOutput) 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, } } @@ -139,11 +143,11 @@ func (d *DeviceOf[T]) Configure(cfg Config) { d.batchLength += d.batchLength & 1 // Reset the device - d.resetPin.High() + d.resetPin(true) time.Sleep(50 * time.Millisecond) - d.resetPin.Low() + d.resetPin(false) time.Sleep(50 * time.Millisecond) - d.resetPin.High() + d.resetPin(true) time.Sleep(50 * time.Millisecond) // Common initialization @@ -209,7 +213,7 @@ func (d *DeviceOf[T]) Configure(cfg Config) { time.Sleep(10 * time.Millisecond) // d.endWrite() - d.blPin.High() // Backlight ON + d.blPin(true) // Backlight ON } // Send a command with data to the display. It does not change the chip select @@ -217,9 +221,9 @@ func (d *DeviceOf[T]) Configure(cfg Config) { // meaning that data can be sent right away. func (d *DeviceOf[T]) sendCommand(command uint8, data []byte) error { d.cmdBuf[0] = command - d.dcPin.Low() + d.dcPin(false) err := d.bus.Tx(d.cmdBuf[:1], nil) - d.dcPin.High() + d.dcPin(true) if len(data) != 0 { err = d.bus.Tx(data, nil) } @@ -229,16 +233,16 @@ 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 { - d.csPin.Low() + if d.csPin != nil { + d.csPin(false) } } // 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 { - d.csPin.High() + if d.csPin != nil { + d.csPin(true) } } @@ -300,9 +304,9 @@ func (d *DeviceOf[T]) SyncToScanLine(scanline uint16) { func (d *DeviceOf[T]) GetScanLine() uint16 { d.startWrite() data := []uint8{0x00, 0x00} - d.dcPin.Low() + d.dcPin(false) d.bus.Transfer(GSCAN) - d.dcPin.High() + d.dcPin(true) for i := range data { data[i], _ = d.bus.Transfer(0xFF) } @@ -541,9 +545,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(true) } else { - d.blPin.Low() + d.blPin(false) } } diff --git a/sx127x/sx127x.go b/sx127x/sx127x.go index f2c2a8737..f12a4359f 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/legacy" "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 legacy.PinOutput) *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), @@ -67,9 +67,9 @@ func (d *Device) SetRadioController(rc RadioController) error { // Reset re-initialize the sx127x device func (d *Device) Reset() { - d.rstPin.Low() + d.rstPin(false) time.Sleep(100 * time.Millisecond) - d.rstPin.High() + d.rstPin(true) time.Sleep(100 * time.Millisecond) } diff --git a/uc8151/uc8151.go b/uc8151/uc8151.go index 38c48f9a0..bee7c564d 100644 --- a/uc8151/uc8151.go +++ b/uc8151/uc8151.go @@ -8,10 +8,10 @@ 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/pixel" ) @@ -31,10 +31,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 + busy drivers.PinInput width int16 height int16 buffer []uint8 @@ -49,17 +49,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 legacy.PinOutput, busyPin legacy.PinInput) 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, + cs: csPin.Set, + dc: dcPin.Set, + rst: rstPin.Set, + busy: busyPin.Get, } } @@ -133,9 +133,9 @@ func (d *Device) Configure(cfg Config) { // Reset resets the device func (d *Device) Reset() { - d.rst.Low() + d.rst(false) time.Sleep(10 * time.Millisecond) - d.rst.High() + d.rst(true) time.Sleep(10 * time.Millisecond) d.WaitUntilIdle() } @@ -152,18 +152,18 @@ func (d *Device) PowerOn() { // SendCommand sends a command to the display func (d *Device) SendCommand(command uint8) { - d.dc.Low() - d.cs.Low() + d.dc(false) + d.cs(false) d.bus.Transfer(command) - d.cs.High() + d.cs(true) } // SendData sends a data byte to the display func (d *Device) SendData(data ...uint8) { - d.dc.High() - d.cs.Low() + d.dc(true) + d.cs(false) d.bus.Tx(data, nil) - d.cs.High() + d.cs(true) } // SetPixel modifies the internal buffer in a single pixel. @@ -313,14 +313,14 @@ func (d *Device) ClearDisplay() { // WaitUntilIdle waits until the display is ready func (d *Device) WaitUntilIdle() { - for !d.busy.Get() { + for !d.busy() { time.Sleep(10 * time.Millisecond) } } // IsBusy returns the busy status of the display func (d *Device) IsBusy() bool { - return d.busy.Get() + return d.busy() } // ClearBuffer sets the buffer to 0xFF (white) diff --git a/waveshare-epd/epd1in54/epd1in54.go b/waveshare-epd/epd1in54/epd1in54.go index 120b356ef..86c7d9b06 100644 --- a/waveshare-epd/epd1in54/epd1in54.go +++ b/waveshare-epd/epd1in54/epd1in54.go @@ -12,6 +12,9 @@ import ( "image/color" "machine" "time" + + "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" ) type Config struct { @@ -22,12 +25,12 @@ type Config struct { } type Device struct { - bus *machine.SPI - cs machine.Pin - dc machine.Pin - rst machine.Pin - busy machine.Pin - + bus *machine.SPI + cs drivers.PinOutput + dc drivers.PinOutput + rst drivers.PinOutput + busy drivers.PinInput + config func() buffer []uint8 rotation Rotation } @@ -79,22 +82,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 legacy.PinOutput, busyPin legacy.PinInput) 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, + busy: busyPin.Get, + config: 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.config == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } + d.config() d.bus.Configure(machine.SPIConfig{ Frequency: 2000000, @@ -150,10 +159,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.config == nil { + panic(legacy.ErrConfigBeforeInstantiated) + } + d.config() d.bus.Configure(machine.SPIConfig{ Frequency: 2000000, @@ -231,11 +240,11 @@ func (d *Device) setLUT(lut [159]uint8) { // Reset resets the display. func (d *Device) Reset() { - d.rst.High() + d.rst(true) time.Sleep(20 * time.Millisecond) - d.rst.Low() + d.rst(false) time.Sleep(5 * time.Millisecond) - d.rst.High() + d.rst(true) time.Sleep(20 * time.Millisecond) } @@ -252,13 +261,13 @@ func (d *Device) SendData(data uint8) { // sendDataCommand sends image data or a command to the screen func (d *Device) sendDataCommand(isCommand bool, data uint8) { if isCommand { - d.dc.Low() + d.dc(false) } else { - d.dc.High() + d.dc(true) } - d.cs.Low() + d.cs(false) d.bus.Transfer(data) - d.cs.High() + d.cs(true) } // SetPixel modifies the internal buffer in a single pixel. @@ -369,7 +378,7 @@ func (d *Device) Clear() { // WaitUntilIdle waits until the display is ready func (d *Device) WaitUntilIdle() { - for d.busy.Get() { + for d.busy() { time.Sleep(100 * time.Millisecond) } time.Sleep(200 * time.Millisecond) @@ -377,7 +386,7 @@ func (d *Device) WaitUntilIdle() { // IsBusy returns the busy status of the display func (d *Device) IsBusy() bool { - return d.busy.Get() + return d.busy() } // ClearBuffer sets the buffer to 0xFF (white) @@ -420,5 +429,5 @@ func (d *Device) Sleep() { d.SendData(0x01) time.Sleep(200 * time.Millisecond) - d.rst.Low() + d.rst(false) } diff --git a/waveshare-epd/epd2in13/epd2in13.go b/waveshare-epd/epd2in13/epd2in13.go index e49fd4274..6fd089ac1 100644 --- a/waveshare-epd/epd2in13/epd2in13.go +++ b/waveshare-epd/epd2in13/epd2in13.go @@ -6,10 +6,10 @@ 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" ) type Config struct { @@ -21,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 + busy drivers.PinInput logicalWidth int16 width int16 height int16 @@ -53,17 +53,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 legacy.PinOutput, busyPin legacy.PinInput) 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, + cs: csPin.Set, + dc: dcPin.Set, + rst: rstPin.Set, + busy: busyPin.Get, } } @@ -91,9 +91,9 @@ func (d *Device) Configure(cfg Config) { d.buffer[i] = 0xFF } - d.cs.Low() - d.dc.Low() - d.rst.Low() + d.cs(false) + d.dc(false) + d.rst(false) d.Reset() @@ -119,9 +119,9 @@ func (d *Device) Configure(cfg Config) { // Reset resets the device func (d *Device) Reset() { - d.rst.Low() + d.rst(false) time.Sleep(200 * time.Millisecond) - d.rst.High() + d.rst(true) time.Sleep(200 * time.Millisecond) } @@ -155,13 +155,13 @@ func (d *Device) SendData(data uint8) { // sendDataCommand sends image data or a command to the screen func (d *Device) sendDataCommand(isCommand bool, data uint8) { if isCommand { - d.dc.Low() + d.dc(false) } else { - d.dc.High() + d.dc(true) } - d.cs.Low() + d.cs(false) d.bus.Transfer(data) - d.cs.High() + d.cs(true) } // SetLUT sets the look up tables for full or partial updates @@ -298,14 +298,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.busy() { time.Sleep(100 * time.Millisecond) } } // IsBusy returns the busy status of the display func (d *Device) IsBusy() bool { - return d.busy.Get() + return d.busy() } // ClearBuffer sets the buffer to 0xFF (white) diff --git a/waveshare-epd/epd2in13x/epd2in13x.go b/waveshare-epd/epd2in13x/epd2in13x.go index b84f5a512..ee3fc6176 100644 --- a/waveshare-epd/epd2in13x/epd2in13x.go +++ b/waveshare-epd/epd2in13x/epd2in13x.go @@ -6,10 +6,10 @@ 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" ) type Config struct { @@ -20,10 +20,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 + busy drivers.PinInput width int16 height int16 buffer [][]uint8 @@ -33,17 +33,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 legacy.PinOutput, busyPin legacy.PinInput) 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, + cs: csPin.Set, + dc: dcPin.Set, + rst: rstPin.Set, + busy: busyPin.Get, } } @@ -75,9 +75,9 @@ func (d *Device) Configure(cfg Config) { } } - d.cs.Low() - d.dc.Low() - d.rst.Low() + d.cs(false) + d.dc(false) + d.rst(false) d.Reset() @@ -99,9 +99,9 @@ func (d *Device) Configure(cfg Config) { // Reset resets the device func (d *Device) Reset() { - d.rst.Low() + d.rst(false) time.Sleep(200 * time.Millisecond) - d.rst.High() + d.rst(true) time.Sleep(200 * time.Millisecond) } @@ -126,13 +126,13 @@ func (d *Device) SendData(data uint8) { // sendDataCommand sends image data or a command to the screen func (d *Device) sendDataCommand(isCommand bool, data uint8) { if isCommand { - d.dc.Low() + d.dc(false) } else { - d.dc.High() + d.dc(true) } - d.cs.Low() + d.cs(false) d.bus.Transfer(data) - d.cs.High() + d.cs(true) } // SetPixel modifies the internal buffer in a single pixel. @@ -277,14 +277,14 @@ func (d *Device) ClearDisplay() { // WaitUntilIdle waits until the display is ready func (d *Device) WaitUntilIdle() { - for !d.busy.Get() { + for !d.busy() { time.Sleep(100 * time.Millisecond) } } // IsBusy returns the busy status of the display func (d *Device) IsBusy() bool { - return d.busy.Get() + return d.busy() } // ClearBuffer sets the buffer to 0xFF (white) diff --git a/waveshare-epd/epd2in66b/dev.go b/waveshare-epd/epd2in66b/dev.go index cadff9c04..86b93ae5f 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 + cs drivers.PinOutput + dc drivers.PinOutput + rst drivers.PinOutput + busy 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 } @@ -199,11 +176,11 @@ func (d *Device) setCursor(x, y uint16) error { } func (d *Device) hwReset() { - d.rst.High() + d.rst(true) time.Sleep(50 * time.Millisecond) - d.rst.Low() + d.rst(false) time.Sleep(2 * time.Millisecond) - d.rst.High() + d.rst(true) time.Sleep(50 * time.Millisecond) } @@ -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.busy() { // high = busy time.Sleep(10 * time.Millisecond) } @@ -255,26 +232,26 @@ func (d *Device) sendCommandSequence(seq []byte) error { } func (d *Device) sendCommandByte(b byte) error { - d.dc.Low() - d.cs.Low() + d.dc(false) + d.cs(false) _, err := d.bus.Transfer(b) - d.cs.High() + d.cs(true) return err } func (d *Device) sendDataByte(b byte) error { - d.dc.High() - d.cs.Low() + d.dc(true) + d.cs(false) _, err := d.bus.Transfer(b) - d.cs.High() + d.cs(true) return err } func (d *Device) sendData(b []byte) error { - d.dc.High() - d.cs.Low() + d.dc(true) + d.cs(false) err := d.bus.Tx(b, nil) - d.cs.High() + d.cs(true) return err } diff --git a/waveshare-epd/epd2in66b/dev_baremetal.go b/waveshare-epd/epd2in66b/dev_baremetal.go new file mode 100644 index 000000000..7fa4a561b --- /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.busy = busy.Get + return nil +} diff --git a/waveshare-epd/epd2in9/epd2in9.go b/waveshare-epd/epd2in9/epd2in9.go index 994cd7971..9abdb777a 100644 --- a/waveshare-epd/epd2in9/epd2in9.go +++ b/waveshare-epd/epd2in9/epd2in9.go @@ -13,10 +13,10 @@ 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" ) type Config struct { @@ -28,10 +28,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 + busy drivers.PinInput logicalWidth int16 width int16 height int16 @@ -61,17 +61,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 legacy.PinOutput, busyPin legacy.PinInput) 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, + cs: csPin.Set, + dc: dcPin.Set, + rst: rstPin.Set, + busy: busyPin.Get, } } @@ -99,9 +99,9 @@ func (d *Device) Configure(cfg Config) { d.buffer[i] = 0xFF } - d.cs.Low() - d.dc.Low() - d.rst.Low() + d.cs(false) + d.dc(false) + d.rst(false) d.Reset() @@ -127,9 +127,9 @@ func (d *Device) Configure(cfg Config) { // Reset resets the device func (d *Device) Reset() { - d.rst.Low() + d.rst(false) time.Sleep(200 * time.Millisecond) - d.rst.High() + d.rst(true) time.Sleep(200 * time.Millisecond) } @@ -152,13 +152,13 @@ func (d *Device) SendData(data uint8) { // sendDataCommand sends image data or a command to the screen func (d *Device) sendDataCommand(isCommand bool, data uint8) { if isCommand { - d.dc.Low() + d.dc(false) } else { - d.dc.High() + d.dc(true) } - d.cs.Low() + d.cs(false) d.bus.Transfer(data) - d.cs.High() + d.cs(true) } // SetLUT sets the look up tables for full or partial updates @@ -245,14 +245,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.busy() { time.Sleep(100 * time.Millisecond) } } // IsBusy returns the busy status of the display func (d *Device) IsBusy() bool { - return d.busy.Get() + return d.busy() } // ClearBuffer sets the buffer to 0xFF (white) diff --git a/waveshare-epd/epd4in2/epd4in2.go b/waveshare-epd/epd4in2/epd4in2.go index 902d21e2b..a73154570 100644 --- a/waveshare-epd/epd4in2/epd4in2.go +++ b/waveshare-epd/epd4in2/epd4in2.go @@ -10,10 +10,10 @@ package epd4in2 import ( "image/color" - "machine" "time" "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" ) type Config struct { @@ -25,10 +25,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 + busy drivers.PinInput logicalWidth int16 width int16 height int16 @@ -40,17 +40,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 legacy.PinOutput, busyPin legacy.PinInput) 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, + cs: csPin.Set, + dc: dcPin.Set, + rst: rstPin.Set, + busy: busyPin.Get, } } @@ -78,9 +78,9 @@ func (d *Device) Configure(cfg Config) { d.buffer[i] = 0xFF } - d.cs.Low() - d.dc.Low() - d.rst.Low() + d.cs(false) + d.dc(false) + d.rst(false) d.Reset() d.SendCommand(POWER_SETTING) @@ -104,9 +104,9 @@ func (d *Device) Configure(cfg Config) { // Reset resets the device func (d *Device) Reset() { - d.rst.Low() + d.rst(false) time.Sleep(200 * time.Millisecond) - d.rst.High() + d.rst(true) time.Sleep(200 * time.Millisecond) } @@ -145,13 +145,13 @@ func (d *Device) SendData(data uint8) { // sendDataCommand sends image data or a command to the screen func (d *Device) sendDataCommand(isCommand bool, data uint8) { if isCommand { - d.dc.Low() + d.dc(false) } else { - d.dc.High() + d.dc(true) } - d.cs.Low() + d.cs(false) d.bus.Transfer(data) - d.cs.High() + d.cs(true) } // SetLUT sets the look up tables for full or partial updates @@ -311,14 +311,14 @@ func (d *Device) ClearDisplay() { // WaitUntilIdle waits until the display is ready func (d *Device) WaitUntilIdle() { - for d.busy.Get() { + for d.busy() { time.Sleep(100 * time.Millisecond) } } // IsBusy returns the busy status of the display func (d *Device) IsBusy() bool { - return d.busy.Get() + return d.busy() } // ClearBuffer sets the buffer to 0xFF (white) From 9fb57e22fbd94c160ef1efe7a11f4e8e315f07e8 Mon Sep 17 00:00:00 2001 From: soypat Date: Wed, 9 Jul 2025 12:04:37 -0300 Subject: [PATCH 5/6] fix pullups not available on fe310 --- internal/legacy/pinhal_baremetal.go | 4 ++-- internal/legacy/pinhal_nopulls.go | 10 ++++++++++ internal/legacy/pinhal_pulls.go | 10 ++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 internal/legacy/pinhal_nopulls.go create mode 100644 internal/legacy/pinhal_pulls.go diff --git a/internal/legacy/pinhal_baremetal.go b/internal/legacy/pinhal_baremetal.go index ee4b09a03..b63e06dc9 100644 --- a/internal/legacy/pinhal_baremetal.go +++ b/internal/legacy/pinhal_baremetal.go @@ -9,7 +9,7 @@ func configurePinOut(p PinOutput) { } func configurePinInputPulldown(p PinInput) { - configurePin(p, machine.PinInputPulldown) + configurePin(p, pulldown) // some chips do not have pull down, in which case pulldown==machine.PinInput. } func configurePinInput(p PinInput) { @@ -17,7 +17,7 @@ func configurePinInput(p PinInput) { } func configurePinInputPullup(p PinInput) { - configurePin(p, machine.PinInputPullup) + configurePin(p, pullup) // some chips do not have pull up, in which case pullup==machine.PinInput. } func pinIsNoPin(a any) bool { 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..082e54636 --- /dev/null +++ b/internal/legacy/pinhal_pulls.go @@ -0,0 +1,10 @@ +//go:build baremetal && !fe310 + +package legacy + +import "machine" + +const ( + pulldown = machine.PinInputPulldown + pullup = machine.PinInputPullup +) From db544f4ca79e7ebb3d1b394f5b6db1326e701c71 Mon Sep 17 00:00:00 2001 From: soypat Date: Wed, 9 Jul 2025 12:13:17 -0300 Subject: [PATCH 6/6] bugfix: output misconfigured --- internal/legacy/pinhal_baremetal.go | 2 +- internal/legacy/pinhal_pulls.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/legacy/pinhal_baremetal.go b/internal/legacy/pinhal_baremetal.go index b63e06dc9..9fbf79b46 100644 --- a/internal/legacy/pinhal_baremetal.go +++ b/internal/legacy/pinhal_baremetal.go @@ -5,7 +5,7 @@ package legacy import "machine" func configurePinOut(p PinOutput) { - configurePin(p, machine.PinInputPulldown) + configurePin(p, machine.PinOutput) } func configurePinInputPulldown(p PinInput) { diff --git a/internal/legacy/pinhal_pulls.go b/internal/legacy/pinhal_pulls.go index 082e54636..d7e88c658 100644 --- a/internal/legacy/pinhal_pulls.go +++ b/internal/legacy/pinhal_pulls.go @@ -4,6 +4,9 @@ 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