apa102: use 4-byte buffer to improve speed

Instead of sending the APA102 data byte by byte, it's more efficient to
send multiple bytes at once (especially when the SPI peripheral uses
DMA). This requires the apa102.Device object to be a pointer receiver to
avoid excessive heap allocations.

This commit also just happens to work around a hardware bug on the
nrf52832:
https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52832_Rev2%2FERR%2FnRF52832%2FRev2%2Flatest%2Fanomaly_832_58.html&anchor=anomaly_832_58
This commit is contained in:
Ayke van Laethem
2021-05-30 01:21:10 +02:00
committed by Ron Evans
parent 5ecefde991
commit 5a02fe068b
2 changed files with 20 additions and 18 deletions
+19 -17
View File
@@ -27,44 +27,46 @@ var startFrame = []byte{0x00, 0x00, 0x00, 0x00}
type Device struct {
bus drivers.SPI
Order int
buf [4]byte
}
// New returns a new APA102 driver. Pass in a fully configured SPI bus.
func New(b drivers.SPI) Device {
return Device{bus: b, Order: BGR}
func New(b drivers.SPI) *Device {
return &Device{bus: b, Order: BGR}
}
// 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 {
func NewSoftwareSPI(sckPin, sdoPin machine.Pin, delay uint32) *Device {
return New(&bbSPI{SCK: sckPin, SDO: sdoPin, Delay: delay})
}
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
// The A value (Alpha channel) is used for brightness, set to 0xff (255) for maximum.
func (d Device) WriteColors(cs []color.RGBA) (n int, err error) {
func (d *Device) WriteColors(cs []color.RGBA) (n int, err error) {
d.startFrame()
// write data
for _, c := range cs {
// brightness is scaled to 5 bit value
d.bus.Transfer(0xe0 | (c.A >> 3))
d.buf[0] = 0xe0 | (c.A >> 3)
// set the colors
switch d.Order {
case BRG:
d.bus.Transfer(c.B)
d.bus.Transfer(c.R)
d.bus.Transfer(c.G)
d.buf[1] = c.B
d.buf[2] = c.R
d.buf[3] = c.G
case GRB:
d.bus.Transfer(c.G)
d.bus.Transfer(c.R)
d.bus.Transfer(c.B)
d.buf[1] = c.G
d.buf[2] = c.R
d.buf[3] = c.B
case BGR:
d.bus.Transfer(c.B)
d.bus.Transfer(c.G)
d.bus.Transfer(c.R)
d.buf[1] = c.B
d.buf[2] = c.G
d.buf[3] = c.R
}
d.bus.Tx(d.buf[:], nil)
}
d.endFrame(len(cs))
@@ -73,7 +75,7 @@ func (d Device) WriteColors(cs []color.RGBA) (n int, err error) {
}
// Write the raw bytes using the APA102 protocol.
func (d Device) Write(buf []byte) (n int, err error) {
func (d *Device) Write(buf []byte) (n int, err error) {
d.startFrame()
d.bus.Tx(buf, nil)
d.endFrame(len(buf) / 4)
@@ -82,14 +84,14 @@ func (d Device) Write(buf []byte) (n int, err error) {
}
// startFrame sends the start bytes for a strand of LEDs.
func (d Device) startFrame() {
func (d *Device) startFrame() {
d.bus.Tx(startFrame, nil)
}
// endFrame sends the end frame marker with one extra bit per LED so
// long strands of LEDs receive the necessary termination for updates.
// See https://cpldcpu.wordpress.com/2014/11/30/understanding-the-apa102-superled/
func (d Device) endFrame(count int) {
func (d *Device) endFrame(count int) {
for i := 0; i < count/16; i++ {
d.bus.Transfer(0xff)
}
+1 -1
View File
@@ -13,7 +13,7 @@ import (
)
var (
apa apa102.Device
apa *apa102.Device
pwm = machine.TCC0
leds = make([]color.RGBA, 1)