Files
Patricio Whittingslow 09fd01340b add simplest driver ports
2025-11-11 10:42:28 +01:00

103 lines
2.5 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"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
)
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 pin.Output, delay uint32) *Device {
return New(&bbSPI{SCK: sckPin.Set, SDO: sdoPin.Set, Delay: delay, configurePins: func() {
legacy.ConfigurePinOut(sckPin)
legacy.ConfigurePinOut(sdoPin)
}})
}
// 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)
}
}