mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-05 23:43:41 +00:00
5a02fe068b
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
99 lines
2.3 KiB
Go
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)
|
|
}
|
|
}
|