mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-04 15:07:46 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f367eabcf4 |
@@ -5,6 +5,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"image/color"
|
"image/color"
|
||||||
"machine"
|
"machine"
|
||||||
|
|
||||||
|
"tinygo.org/x/drivers"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errUnknownClockSpeed = errors.New("ws2812: unknown CPU clock speed")
|
var errUnknownClockSpeed = errors.New("ws2812: unknown CPU clock speed")
|
||||||
@@ -38,3 +40,77 @@ func (d Device) WriteColors(buf []color.RGBA) error {
|
|||||||
}
|
}
|
||||||
return nil
|
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