From 5a02fe068be905dca08757e84613eeb935a60819 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 30 May 2021 01:21:10 +0200 Subject: [PATCH] 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 --- apa102/apa102.go | 36 +++++++++++++++------------- examples/apa102/itsybitsy-m0/main.go | 2 +- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/apa102/apa102.go b/apa102/apa102.go index 4a8ab9a..98253bf 100644 --- a/apa102/apa102.go +++ b/apa102/apa102.go @@ -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) } diff --git a/examples/apa102/itsybitsy-m0/main.go b/examples/apa102/itsybitsy-m0/main.go index 1cfdb70..ac6d8c6 100644 --- a/examples/apa102/itsybitsy-m0/main.go +++ b/examples/apa102/itsybitsy-m0/main.go @@ -13,7 +13,7 @@ import ( ) var ( - apa apa102.Device + apa *apa102.Device pwm = machine.TCC0 leds = make([]color.RGBA, 1)