mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-03 06:27:47 +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 (
|
||||
"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.
|
||||
|
||||
+14
-12
@@ -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
|
||||
|
||||
+20
-17
@@ -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)
|
||||
}
|
||||
|
||||
+8
-7
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -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