tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/apa102/apa102.go (about)

     1  // Package apa102 implements a driver for the APA102 SPI LED.
     2  //
     3  // Datasheet: https://cdn-shop.adafruit.com/product-files/2343/APA102C.pdf
     4  package apa102 // import "tinygo.org/x/drivers/apa102"
     5  
     6  import (
     7  	"image/color"
     8  	"machine"
     9  
    10  	"tinygo.org/x/drivers"
    11  )
    12  
    13  const (
    14  	// BGR aka "Blue Green Red" is the current APA102 LED color order.
    15  	BGR = iota
    16  
    17  	// BRG aka "Blue Red Green" is the typical APA102 color order from 2015-2017.
    18  	BRG
    19  
    20  	// GRB aka "Green Red Blue" is the typical APA102 color order from pre-2015.
    21  	GRB
    22  )
    23  
    24  var startFrame = []byte{0x00, 0x00, 0x00, 0x00}
    25  
    26  // Device wraps APA102 SPI LEDs.
    27  type Device struct {
    28  	bus   drivers.SPI
    29  	Order int
    30  	buf   [4]byte
    31  }
    32  
    33  // New returns a new APA102 driver. Pass in a fully configured SPI bus.
    34  func New(b drivers.SPI) *Device {
    35  	return &Device{bus: b, Order: BGR}
    36  }
    37  
    38  // NewSoftwareSPI returns a new APA102 driver that will use a software based
    39  // implementation of the SPI protocol.
    40  func NewSoftwareSPI(sckPin, sdoPin machine.Pin, delay uint32) *Device {
    41  	return New(&bbSPI{SCK: sckPin, SDO: sdoPin, Delay: delay})
    42  }
    43  
    44  // WriteColors writes the given RGBA color slice out using the APA102 protocol.
    45  // The A value (Alpha channel) is used for brightness, set to 0xff (255) for maximum.
    46  func (d *Device) WriteColors(cs []color.RGBA) (n int, err error) {
    47  	d.startFrame()
    48  
    49  	// write data
    50  	for _, c := range cs {
    51  		// brightness is scaled to 5 bit value
    52  		d.buf[0] = 0xe0 | (c.A >> 3)
    53  
    54  		// set the colors
    55  		switch d.Order {
    56  		case BRG:
    57  			d.buf[1] = c.B
    58  			d.buf[2] = c.R
    59  			d.buf[3] = c.G
    60  		case GRB:
    61  			d.buf[1] = c.G
    62  			d.buf[2] = c.R
    63  			d.buf[3] = c.B
    64  		case BGR:
    65  			d.buf[1] = c.B
    66  			d.buf[2] = c.G
    67  			d.buf[3] = c.R
    68  		}
    69  		d.bus.Tx(d.buf[:], nil)
    70  	}
    71  
    72  	d.endFrame(len(cs))
    73  
    74  	return len(cs), nil
    75  }
    76  
    77  // Write the raw bytes using the APA102 protocol.
    78  func (d *Device) Write(buf []byte) (n int, err error) {
    79  	d.startFrame()
    80  	d.bus.Tx(buf, nil)
    81  	d.endFrame(len(buf) / 4)
    82  
    83  	return len(buf), nil
    84  }
    85  
    86  // startFrame sends the start bytes for a strand of LEDs.
    87  func (d *Device) startFrame() {
    88  	d.bus.Tx(startFrame, nil)
    89  }
    90  
    91  // endFrame sends the end frame marker with one extra bit per LED so
    92  // long strands of LEDs receive the necessary termination for updates.
    93  // See https://cpldcpu.wordpress.com/2014/11/30/understanding-the-apa102-superled/
    94  func (d *Device) endFrame(count int) {
    95  	for i := 0; i < count/16; i++ {
    96  		d.bus.Transfer(0xff)
    97  	}
    98  }