Skip to content

Commit 5a02fe0

Browse files
aykevldeadprogram
authored andcommitted
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
1 parent 5ecefde commit 5a02fe0

File tree

2 files changed

+20
-18
lines changed

2 files changed

+20
-18
lines changed

apa102/apa102.go

+19-17
Original file line numberDiff line numberDiff line change
@@ -27,44 +27,46 @@ var startFrame = []byte{0x00, 0x00, 0x00, 0x00}
2727
type Device struct {
2828
bus drivers.SPI
2929
Order int
30+
buf [4]byte
3031
}
3132

3233
// New returns a new APA102 driver. Pass in a fully configured SPI bus.
33-
func New(b drivers.SPI) Device {
34-
return Device{bus: b, Order: BGR}
34+
func New(b drivers.SPI) *Device {
35+
return &Device{bus: b, Order: BGR}
3536
}
3637

3738
// NewSoftwareSPI returns a new APA102 driver that will use a software based
3839
// implementation of the SPI protocol.
39-
func NewSoftwareSPI(sckPin, sdoPin machine.Pin, delay uint32) Device {
40+
func NewSoftwareSPI(sckPin, sdoPin machine.Pin, delay uint32) *Device {
4041
return New(&bbSPI{SCK: sckPin, SDO: sdoPin, Delay: delay})
4142
}
4243

4344
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
4445
// The A value (Alpha channel) is used for brightness, set to 0xff (255) for maximum.
45-
func (d Device) WriteColors(cs []color.RGBA) (n int, err error) {
46+
func (d *Device) WriteColors(cs []color.RGBA) (n int, err error) {
4647
d.startFrame()
4748

4849
// write data
4950
for _, c := range cs {
5051
// brightness is scaled to 5 bit value
51-
d.bus.Transfer(0xe0 | (c.A >> 3))
52+
d.buf[0] = 0xe0 | (c.A >> 3)
5253

5354
// set the colors
5455
switch d.Order {
5556
case BRG:
56-
d.bus.Transfer(c.B)
57-
d.bus.Transfer(c.R)
58-
d.bus.Transfer(c.G)
57+
d.buf[1] = c.B
58+
d.buf[2] = c.R
59+
d.buf[3] = c.G
5960
case GRB:
60-
d.bus.Transfer(c.G)
61-
d.bus.Transfer(c.R)
62-
d.bus.Transfer(c.B)
61+
d.buf[1] = c.G
62+
d.buf[2] = c.R
63+
d.buf[3] = c.B
6364
case BGR:
64-
d.bus.Transfer(c.B)
65-
d.bus.Transfer(c.G)
66-
d.bus.Transfer(c.R)
65+
d.buf[1] = c.B
66+
d.buf[2] = c.G
67+
d.buf[3] = c.R
6768
}
69+
d.bus.Tx(d.buf[:], nil)
6870
}
6971

7072
d.endFrame(len(cs))
@@ -73,7 +75,7 @@ func (d Device) WriteColors(cs []color.RGBA) (n int, err error) {
7375
}
7476

7577
// Write the raw bytes using the APA102 protocol.
76-
func (d Device) Write(buf []byte) (n int, err error) {
78+
func (d *Device) Write(buf []byte) (n int, err error) {
7779
d.startFrame()
7880
d.bus.Tx(buf, nil)
7981
d.endFrame(len(buf) / 4)
@@ -82,14 +84,14 @@ func (d Device) Write(buf []byte) (n int, err error) {
8284
}
8385

8486
// startFrame sends the start bytes for a strand of LEDs.
85-
func (d Device) startFrame() {
87+
func (d *Device) startFrame() {
8688
d.bus.Tx(startFrame, nil)
8789
}
8890

8991
// endFrame sends the end frame marker with one extra bit per LED so
9092
// long strands of LEDs receive the necessary termination for updates.
9193
// See https://cpldcpu.wordpress.com/2014/11/30/understanding-the-apa102-superled/
92-
func (d Device) endFrame(count int) {
94+
func (d *Device) endFrame(count int) {
9395
for i := 0; i < count/16; i++ {
9496
d.bus.Transfer(0xff)
9597
}

examples/apa102/itsybitsy-m0/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
)
1414

1515
var (
16-
apa apa102.Device
16+
apa *apa102.Device
1717

1818
pwm = machine.TCC0
1919
leds = make([]color.RGBA, 1)

0 commit comments

Comments
 (0)