// 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) } }