mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 18:48:41 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f367eabcf4 |
@@ -5,6 +5,8 @@ import (
|
||||
"errors"
|
||||
"image/color"
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
var errUnknownClockSpeed = errors.New("ws2812: unknown CPU clock speed")
|
||||
@@ -38,3 +40,77 @@ func (d Device) WriteColors(buf []color.RGBA) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeviceSPI wraps a SPI object for driving a string of WS2812 LEDs.
|
||||
type DeviceSPI struct {
|
||||
Bus drivers.SPI
|
||||
|
||||
// Use a buffer embedded in the device struct so that at most one allocation
|
||||
// happens at NewSPI and no allocation during transmission.
|
||||
buf []byte
|
||||
}
|
||||
|
||||
// NewSPI returns a WS2812 driver using a SPI bus. This SPI bus must already be
|
||||
// configured at exactly 4MHz otherwise WS2812 won't work properly with it.
|
||||
//
|
||||
// The advantage of using a SPI bus over bitbanging is that it doesn't require
|
||||
// custom assembly for each new platform and that it may avoid needing to
|
||||
// disable interrupts while sending color data if the SPI peripheral uses DMA.
|
||||
// The disadvantage is of course that it is limited in which pins can be used
|
||||
// for WS2812 output.
|
||||
func NewSPI(bus drivers.SPI) *DeviceSPI {
|
||||
return &DeviceSPI{
|
||||
Bus: bus,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteColors wries the given color slice out using the WS2812 protocol.
|
||||
// Colors are sent out in the usual GRB format.
|
||||
func (d *DeviceSPI) WriteColors(buf []color.RGBA) error {
|
||||
// Each color needs 15 bytes: 5 SPI bits per WS2812 bit with 3*8 WS2812 bits
|
||||
// per color means 120 SPI bits. In addition to that, an extra 0 byte seems
|
||||
// to be necessary on nRF5x chips to avoid having the SDO line pulled high
|
||||
// at the end of the transfer.
|
||||
if len(d.buf) < len(buf)*15+1 {
|
||||
d.buf = make([]byte, len(buf)*15+1)
|
||||
}
|
||||
|
||||
for i, color := range buf {
|
||||
bitBuf := makeSPIBits(color.G)
|
||||
copy(d.buf[i*15+0:], bitBuf[:])
|
||||
bitBuf = makeSPIBits(color.R)
|
||||
copy(d.buf[i*15+5:], bitBuf[:])
|
||||
bitBuf = makeSPIBits(color.B)
|
||||
copy(d.buf[i*15+10:], bitBuf[:])
|
||||
}
|
||||
return d.Bus.Tx(d.buf, nil)
|
||||
}
|
||||
|
||||
func makeSPIBits(b byte) [5]byte {
|
||||
// Create a 40 bit bitstring from this one byte.
|
||||
var bitstring uint64
|
||||
for i := 0; i < 8; i++ {
|
||||
bitstring <<= 5
|
||||
if b&0x80 != 0 {
|
||||
// 0b11100 means the output is high for 750ns (three high bits at
|
||||
// 4MHz) and low for 500ns (two low bits). This outputs a 1 bit in
|
||||
// the custom WS2812 protocol.
|
||||
bitstring |= 0b11100 // T1H (0b111) + TLD (0b00)
|
||||
} else {
|
||||
// 0b10000 means the output is high for 250ns (one high bit at 4MHz)
|
||||
// and low for 1000ns (four low bits at 4MHz). This outputs a 0 bit
|
||||
// in the custom WS2812 protocol.
|
||||
bitstring |= 0b10000 // T0H (0b100) + TLD (0b00)
|
||||
}
|
||||
b <<= 1
|
||||
}
|
||||
|
||||
// Create a 5 byte array from this bitstring.
|
||||
bitstring <<= 7
|
||||
var buf [5]byte
|
||||
for i := 0; i < 5; i++ {
|
||||
buf[i] = byte(bitstring >> 40)
|
||||
bitstring <<= 8
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user