Files
drivers/apa102/apa102.go
Ayke van Laethem 5a02fe068b 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
2021-05-30 22:39:50 +02:00

99 lines
2.3 KiB
Go

// Package apa102 implements a driver for the APA102 SPI LED.
//
// Datasheet: https://cdn-shop.adafruit.com/product-files/2343/APA102C.pdf
package apa102 // import "tinygo.org/x/drivers/apa102"
import (
"image/color"
"machine"
"tinygo.org/x/drivers"
)
const (
// BGR aka "Blue Green Red" is the current APA102 LED color order.
BGR = iota
// BRG aka "Blue Red Green" is the typical APA102 color order from 2015-2017.
BRG
// GRB aka "Green Red Blue" is the typical APA102 color order from pre-2015.
GRB
)
var startFrame = []byte{0x00, 0x00, 0x00, 0x00}
// Device wraps APA102 SPI LEDs.
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}
}
// 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})
}
// 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) {
d.startFrame()
// write data
for _, c := range cs {
// brightness is scaled to 5 bit value
d.buf[0] = 0xe0 | (c.A >> 3)
// set the colors
switch d.Order {
case BRG:
d.buf[1] = c.B
d.buf[2] = c.R
d.buf[3] = c.G
case GRB:
d.buf[1] = c.G
d.buf[2] = c.R
d.buf[3] = c.B
case BGR:
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))
return len(cs), nil
}
// Write the raw bytes using the APA102 protocol.
func (d *Device) Write(buf []byte) (n int, err error) {
d.startFrame()
d.bus.Tx(buf, nil)
d.endFrame(len(buf) / 4)
return len(buf), nil
}
// startFrame sends the start bytes for a strand of LEDs.
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) {
for i := 0; i < count/16; i++ {
d.bus.Transfer(0xff)
}
}