mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-12 19:03:42 +00:00
apply suggestions from team
This commit is contained in:
+6
-3
@@ -5,9 +5,9 @@ package apa102 // import "tinygo.org/x/drivers/apa102"
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"image/color"
|
"image/color"
|
||||||
"machine"
|
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
|
"tinygo.org/x/drivers/internal/legacy"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -37,8 +37,11 @@ func New(b drivers.SPI) *Device {
|
|||||||
|
|
||||||
// NewSoftwareSPI returns a new APA102 driver that will use a software based
|
// NewSoftwareSPI returns a new APA102 driver that will use a software based
|
||||||
// implementation of the SPI protocol.
|
// implementation of the SPI protocol.
|
||||||
func NewSoftwareSPI(sckPin, sdoPin machine.Pin, delay uint32) *Device {
|
func NewSoftwareSPI(sckPin, sdoPin legacy.PinOutput, delay uint32) *Device {
|
||||||
return New(&bbSPI{SCK: sckPin, SDO: sdoPin, Delay: delay})
|
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.
|
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
|
||||||
|
|||||||
+14
-12
@@ -1,6 +1,8 @@
|
|||||||
package apa102
|
package apa102
|
||||||
|
|
||||||
import "machine"
|
import (
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
|
)
|
||||||
|
|
||||||
// bbSPI is a dumb bit-bang implementation of SPI protocol that is hardcoded
|
// 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.
|
// 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
|
// 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.
|
// this more generic and include it in the TinyGo "machine" package instead.
|
||||||
type bbSPI struct {
|
type bbSPI struct {
|
||||||
SCK machine.Pin
|
SCK drivers.PinOutput
|
||||||
SDO machine.Pin
|
SDO drivers.PinOutput
|
||||||
Delay uint32
|
Delay uint32
|
||||||
|
config func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure sets up the SCK and SDO pins as outputs and sets them low
|
// Configure sets up the SCK and SDO pins as outputs and sets them low
|
||||||
func (s *bbSPI) Configure() {
|
func (s *bbSPI) Configure() {
|
||||||
s.SCK.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
s.config()
|
||||||
s.SDO.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
s.SCK(false)
|
||||||
s.SCK.Low()
|
s.SDO(false)
|
||||||
s.SDO.Low()
|
|
||||||
if s.Delay == 0 {
|
if s.Delay == 0 {
|
||||||
s.Delay = 1
|
s.Delay = 1
|
||||||
}
|
}
|
||||||
@@ -47,19 +49,19 @@ func (s *bbSPI) Transfer(b byte) (byte, error) {
|
|||||||
for i := uint8(0); i < 8; i++ {
|
for i := uint8(0); i < 8; i++ {
|
||||||
|
|
||||||
// half clock cycle high to start
|
// half clock cycle high to start
|
||||||
s.SCK.High()
|
s.SCK(true)
|
||||||
s.delay()
|
s.delay()
|
||||||
|
|
||||||
// write the value to SDO (MSB first)
|
// write the value to SDO (MSB first)
|
||||||
if b&(1<<(7-i)) == 0 {
|
if b&(1<<(7-i)) == 0 {
|
||||||
s.SDO.Low()
|
s.SDO(false)
|
||||||
} else {
|
} else {
|
||||||
s.SDO.High()
|
s.SDO(true)
|
||||||
}
|
}
|
||||||
s.delay()
|
s.delay()
|
||||||
|
|
||||||
// half clock cycle low
|
// half clock cycle low
|
||||||
s.SCK.Low()
|
s.SCK(false)
|
||||||
s.delay()
|
s.delay()
|
||||||
|
|
||||||
// for actual SPI would try to read the SDI value here
|
// for actual SPI would try to read the SDI value here
|
||||||
|
|||||||
+20
-17
@@ -1,31 +1,35 @@
|
|||||||
package bmi160
|
package bmi160
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"machine"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
|
"tinygo.org/x/drivers/internal/legacy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DeviceSPI is the SPI interface to a BMI160 accelerometer/gyroscope. There is
|
// DeviceSPI is the SPI interface to a BMI160 accelerometer/gyroscope. There is
|
||||||
// also an I2C interface, but it is not yet supported.
|
// also an I2C interface, but it is not yet supported.
|
||||||
type DeviceSPI struct {
|
type DeviceSPI struct {
|
||||||
// Chip select pin
|
// Chip select pin
|
||||||
CSB machine.Pin
|
CSB drivers.PinOutput
|
||||||
|
|
||||||
buf [7]byte
|
buf [7]byte
|
||||||
|
|
||||||
// SPI bus (requires chip select to be usable).
|
// 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
|
// NewSPI returns a new device driver. The pin and SPI interface are not
|
||||||
// touched, provide a fully configured SPI object and call Configure to start
|
// touched, provide a fully configured SPI object and call Configure to start
|
||||||
// using this device.
|
// using this device.
|
||||||
func NewSPI(csb machine.Pin, spi drivers.SPI) *DeviceSPI {
|
func NewSPI(csb legacy.PinOutput, spi drivers.SPI) *DeviceSPI {
|
||||||
return &DeviceSPI{
|
return &DeviceSPI{
|
||||||
CSB: csb, // chip select
|
CSB: csb.Set, // chip select
|
||||||
Bus: spi,
|
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
|
// configures the BMI160, but it does not configure the SPI interface (it is
|
||||||
// assumed to be up and running).
|
// assumed to be up and running).
|
||||||
func (d *DeviceSPI) Configure() error {
|
func (d *DeviceSPI) Configure() error {
|
||||||
d.CSB.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
d.CSB(true)
|
||||||
d.CSB.High()
|
|
||||||
|
|
||||||
// The datasheet recommends doing a register read from address 0x7F to get
|
// The datasheet recommends doing a register read from address 0x7F to get
|
||||||
// SPI communication going:
|
// SPI communication going:
|
||||||
@@ -86,9 +89,9 @@ func (d *DeviceSPI) ReadTemperature() (temperature int32, err error) {
|
|||||||
data[0] = 0x80 | reg_TEMPERATURE_0
|
data[0] = 0x80 | reg_TEMPERATURE_0
|
||||||
data[1] = 0
|
data[1] = 0
|
||||||
data[2] = 0
|
data[2] = 0
|
||||||
d.CSB.Low()
|
d.CSB(false)
|
||||||
err = d.Bus.Tx(data, data)
|
err = d.Bus.Tx(data, data)
|
||||||
d.CSB.High()
|
d.CSB(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -123,9 +126,9 @@ func (d *DeviceSPI) ReadAcceleration() (x int32, y int32, z int32, err error) {
|
|||||||
for i := 1; i < len(data); i++ {
|
for i := 1; i < len(data); i++ {
|
||||||
data[i] = 0
|
data[i] = 0
|
||||||
}
|
}
|
||||||
d.CSB.Low()
|
d.CSB(false)
|
||||||
err = d.Bus.Tx(data, data)
|
err = d.Bus.Tx(data, data)
|
||||||
d.CSB.High()
|
d.CSB(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -153,9 +156,9 @@ func (d *DeviceSPI) ReadRotation() (x int32, y int32, z int32, err error) {
|
|||||||
for i := 1; i < len(data); i++ {
|
for i := 1; i < len(data); i++ {
|
||||||
data[i] = 0
|
data[i] = 0
|
||||||
}
|
}
|
||||||
d.CSB.Low()
|
d.CSB(false)
|
||||||
err = d.Bus.Tx(data, data)
|
err = d.Bus.Tx(data, data)
|
||||||
d.CSB.High()
|
d.CSB(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -201,9 +204,9 @@ func (d *DeviceSPI) readRegister(address uint8) uint8 {
|
|||||||
data := d.buf[:2]
|
data := d.buf[:2]
|
||||||
data[0] = 0x80 | address
|
data[0] = 0x80 | address
|
||||||
data[1] = 0
|
data[1] = 0
|
||||||
d.CSB.Low()
|
d.CSB(false)
|
||||||
d.Bus.Tx(data, data)
|
d.Bus.Tx(data, data)
|
||||||
d.CSB.High()
|
d.CSB(true)
|
||||||
return data[1]
|
return data[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +220,7 @@ func (d *DeviceSPI) writeRegister(address, data uint8) {
|
|||||||
buf[0] = address
|
buf[0] = address
|
||||||
buf[1] = data
|
buf[1] = data
|
||||||
|
|
||||||
d.CSB.Low()
|
d.CSB(false)
|
||||||
d.Bus.Tx(buf, buf)
|
d.Bus.Tx(buf, buf)
|
||||||
d.CSB.High()
|
d.CSB(true)
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-7
@@ -2,22 +2,23 @@
|
|||||||
package buzzer // import "tinygo.org/x/drivers/buzzer"
|
package buzzer // import "tinygo.org/x/drivers/buzzer"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"machine"
|
|
||||||
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
|
"tinygo.org/x/drivers/internal/legacy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Device wraps a GPIO connection to a buzzer.
|
// Device wraps a GPIO connection to a buzzer.
|
||||||
type Device struct {
|
type Device struct {
|
||||||
pin machine.Pin
|
pin drivers.PinOutput
|
||||||
High bool
|
High bool
|
||||||
BPM float64
|
BPM float64
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new buzzer driver given which pin to use
|
// New returns a new buzzer driver given which pin to use
|
||||||
func New(pin machine.Pin) Device {
|
func New(pin legacy.PinOutput) Device {
|
||||||
return Device{
|
return Device{
|
||||||
pin: pin,
|
pin: pin.Set,
|
||||||
High: false,
|
High: false,
|
||||||
BPM: 96.0,
|
BPM: 96.0,
|
||||||
}
|
}
|
||||||
@@ -25,14 +26,14 @@ func New(pin machine.Pin) Device {
|
|||||||
|
|
||||||
// On sets the buzzer to a high state.
|
// On sets the buzzer to a high state.
|
||||||
func (l *Device) On() (err error) {
|
func (l *Device) On() (err error) {
|
||||||
l.pin.Set(true)
|
l.pin(true)
|
||||||
l.High = true
|
l.High = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Off sets the buzzer to a low state.
|
// Off sets the buzzer to a low state.
|
||||||
func (l *Device) Off() (err error) {
|
func (l *Device) Off() (err error) {
|
||||||
l.pin.Set(false)
|
l.pin(false)
|
||||||
l.High = false
|
l.High = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
//go:build !baremetal
|
||||||
|
|
||||||
|
package legacy
|
||||||
|
|
||||||
|
func configurePinOut(p PinOutput) {}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package drivers
|
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
|
// PinOutput is hardware abstraction for a pin which outputs a
|
||||||
// digital signal (high or low voltage).
|
// digital signal (high or low voltage).
|
||||||
type PinOutput func(level bool)
|
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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user