Compare commits

...

7 Commits

Author SHA1 Message Date
Ayke van Laethem f367eabcf4 ws2812: add SPI based implementation
This is useful because it allows using the WS2812 driver on platforms
without bitbanged WS2812 support and it may be the only option if
disabling interrupts is impossible or impractical.
2020-12-27 00:37:09 +01:00
Daniel Esteban c338348d2b Better interface "ReadTime" instead of "Time" for DS1307 2020-12-22 08:16:13 +01:00
deadprogram b6aa674b2a test: run unit tests against i2c drivers and any spi drivers without direct gpio
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-12-15 06:08:59 +01:00
deadprogram cc7079b0cd drivers/flash: restore previous calls directly to machine package until we implement SetClockSpeed()
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-12-15 06:08:59 +01:00
deadprogram 42a907035b spi: incorporate change from GH issue feedback
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-12-15 06:08:59 +01:00
deadprogram 46c9ba9595 spi: remove machine.SPI and replace with drivers.SPI interface for almost all SPI drivers
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-12-15 06:08:59 +01:00
Ayke van Laethem df64ce8f50 ws2812: make AVR support more robust
* Merge AVR support for Digispark and non-Digispark
* Fix the error "inline assembly requires more registers than available"
* Use a single file for all AVR targets, in a similar style as the
  Xtensa support.
2020-10-16 18:36:32 +02:00
34 changed files with 292 additions and 206 deletions
+3
View File
@@ -12,6 +12,9 @@ jobs:
- run:
name: "Enforce Go Formatted Code"
command: make fmt-check
- run:
name: "Run unit tests"
command: make unit-test
- run:
name: "Run build and smoke tests"
command: make smoke-test
+12 -1
View File
@@ -132,6 +132,8 @@ smoke-test:
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812
@md5sum ./build/test.hex
ifneq ($(AVR), 0)
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/ws2812
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=digispark ./examples/ws2812
@md5sum ./build/test.hex
endif
@@ -158,4 +160,13 @@ endif
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis2mdl/main.go
@md5sum ./build/test.hex
test: clean fmt-check smoke-test
DRIVERS = $(wildcard */)
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
unit-test:
@go test -v $(addprefix ./,$(TESTS))
test: clean fmt-check unit-test smoke-test
+4 -10
View File
@@ -6,6 +6,8 @@ package apa102 // import "tinygo.org/x/drivers/apa102"
import (
"image/color"
"machine"
"tinygo.org/x/drivers"
)
const (
@@ -23,20 +25,12 @@ var startFrame = []byte{0x00, 0x00, 0x00, 0x00}
// Device wraps APA102 SPI LEDs.
type Device struct {
bus SPI
bus drivers.SPI
Order int
}
// The SPI interface specifies the minimum functionality that a bus
// implementation needs to provide for use by the APA102 driver. Hardware
// SPI from the TinyGo "machine" package implements this already.
type SPI interface {
Tx(w, r []byte) error
Transfer(b byte) (byte, error)
}
// New returns a new APA102 driver. Pass in a fully configured SPI bus.
func New(b SPI) Device {
func New(b drivers.SPI) Device {
return Device{bus: b, Order: BGR}
}
+7 -4
View File
@@ -1,8 +1,11 @@
package bmi160
import "machine"
import (
"machine"
"time"
import "time"
"tinygo.org/x/drivers"
)
// DeviceSPI is the SPI interface to a BMI160 accelerometer/gyroscope. There is
// also an I2C interface, but it is not yet supported.
@@ -11,13 +14,13 @@ type DeviceSPI struct {
CSB machine.Pin
// SPI bus (requires chip select to be usable).
Bus machine.SPI
Bus drivers.SPI
}
// NewSPI returns a new device driver. The pin and SPI interface are not
// touched, provide a fully configured SPI object and call Configure to start
// using this device.
func NewSPI(csb machine.Pin, spi machine.SPI) *DeviceSPI {
func NewSPI(csb machine.Pin, spi drivers.SPI) *DeviceSPI {
return &DeviceSPI{
CSB: csb, // chip select
Bus: spi,
+2 -2
View File
@@ -42,8 +42,8 @@ func (d *Device) SetTime(t time.Time) error {
return err
}
// Time returns the time and date
func (d *Device) Time() (time.Time, error) {
// ReadTime returns the date and time
func (d *Device) ReadTime() (time.Time, error) {
data := make([]byte, 8)
err := d.bus.ReadRegister(d.Address, uint8(TimeDate), data)
if err != nil {
+1 -1
View File
@@ -13,7 +13,7 @@ func main() {
rtc.SetTime(time.Date(2019, 5, 15, 20, 34, 12, 0, time.UTC))
for {
t, err := rtc.Time()
t, err := rtc.ReadTime()
if err != nil {
println("Error reading date:", err)
break
+6 -9
View File
@@ -41,15 +41,7 @@ var (
spi = machine.NINA_SPI
// this is the ESP chip that has the WIFININA firmware flashed on it
adaptor = &wifinina.Device{
SPI: spi,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
console = machine.UART0
adaptor *wifinina.Device
topic = "tinygo"
)
@@ -68,6 +60,11 @@ func main() {
})
// Init esp8266/esp32
adaptor = wifinina.New(spi,
machine.NINA_CS,
machine.NINA_ACK,
machine.NINA_GPIO0,
machine.NINA_RESETN)
adaptor.Configure()
connectToAP()
+6 -9
View File
@@ -40,15 +40,7 @@ var (
spi = machine.NINA_SPI
// this is the ESP chip that has the WIFININA firmware flashed on it
adaptor = &wifinina.Device{
SPI: spi,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
console = machine.UART0
adaptor *wifinina.Device
cl mqtt.Client
topicTx = "tinygo/tx"
@@ -75,6 +67,11 @@ func main() {
})
// Init esp8266/esp32
adaptor = wifinina.New(spi,
machine.NINA_CS,
machine.NINA_ACK,
machine.NINA_GPIO0,
machine.NINA_RESETN)
adaptor.Configure()
connectToAP()
+10 -13
View File
@@ -24,20 +24,9 @@ const ntpHost = "129.6.15.29"
const NTP_PACKET_SIZE = 48
var (
// this is the ESP chip that has the WIFININA firmware flashed on it
// these are the default pins for the Arduino Nano33 IoT.
adaptor = wifinina.Device{
SPI: machine.NINA_SPI,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
b = make([]byte, NTP_PACKET_SIZE)
console = machine.UART0
adaptor *wifinina.Device
b = make([]byte, NTP_PACKET_SIZE)
)
func main() {
@@ -50,6 +39,14 @@ func main() {
SDI: machine.NINA_SDI,
SCK: machine.NINA_SCK,
})
// these are the default pins for the Arduino Nano33 IoT.
adaptor = wifinina.New(machine.NINA_SPI,
machine.NINA_CS,
machine.NINA_ACK,
machine.NINA_GPIO0,
machine.NINA_RESETN)
adaptor.Configure()
// connect to access point
+6 -9
View File
@@ -35,15 +35,7 @@ var (
spi = machine.NINA_SPI
// this is the ESP chip that has the WIFININA firmware flashed on it
adaptor = &wifinina.Device{
SPI: spi,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
console = machine.UART0
adaptor *wifinina.Device
)
var buf = &bytes.Buffer{}
@@ -60,6 +52,11 @@ func main() {
SCK: machine.NINA_SCK,
})
adaptor = wifinina.New(spi,
machine.NINA_CS,
machine.NINA_ACK,
machine.NINA_GPIO0,
machine.NINA_RESETN)
adaptor.Configure()
connectToAP()
+9 -11
View File
@@ -22,19 +22,9 @@ const pass = ""
// IP address of the server aka "hub". Replace with your own info.
const hubIP = ""
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
var (
// this is the ESP chip that has the WIFININA firmware flashed on it
// these are the default pins for the Arduino Nano33 IoT.
adaptor = &wifinina.Device{
SPI: machine.NINA_SPI,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
adaptor *wifinina.Device
)
func main() {
@@ -47,6 +37,14 @@ func main() {
SDI: machine.NINA_SDI,
SCK: machine.NINA_SCK,
})
// these are the default pins for the Arduino Nano33 IoT.
// change these to connect to a different UART or pins for the ESP8266/ESP32
adaptor = wifinina.New(machine.NINA_SPI,
machine.NINA_CS,
machine.NINA_ACK,
machine.NINA_GPIO0,
machine.NINA_RESETN)
adaptor.Configure()
// connect to access point
+6 -9
View File
@@ -33,15 +33,7 @@ var (
spi = machine.NINA_SPI
// this is the ESP chip that has the WIFININA firmware flashed on it
adaptor = &wifinina.Device{
SPI: spi,
CS: machine.NINA_CS,
ACK: machine.NINA_ACK,
GPIO0: machine.NINA_GPIO0,
RESET: machine.NINA_RESETN,
}
console = machine.UART0
adaptor *wifinina.Device
)
var buf [256]byte
@@ -61,6 +53,11 @@ func main() {
SCK: machine.NINA_SCK,
})
adaptor = wifinina.New(spi,
machine.NINA_CS,
machine.NINA_ACK,
machine.NINA_GPIO0,
machine.NINA_RESETN)
adaptor.Configure()
connectToAP()
+9
View File
@@ -0,0 +1,9 @@
// +build arduino
package main
import "machine"
// Replace neo in the code below to match the pin
// that you are using if different.
var neo = machine.D2
+1 -1
View File
@@ -1,4 +1,4 @@
// +build !digispark
// +build !digispark,!arduino
package main
+2
View File
@@ -133,6 +133,8 @@ func (dev *Device) Configure(config *DeviceConfig) (err error) {
time.Sleep(30 * time.Microsecond)
// Speed up to max device frequency
// I propose a check here for max frequency, but not put that functionality directly into the driver.
// Either that or we have to change the signature of the SPI interface in the machine package itself.
if dev.attrs.MaxClockSpeedMHz > 0 {
err := dev.trans.setClockSpeed(uint32(dev.attrs.MaxClockSpeedMHz) * 1e6)
if err != nil {
+3 -1
View File
@@ -1,6 +1,8 @@
package flash
import "machine"
import (
"machine"
)
type transport interface {
configure(config *DeviceConfig)
+4 -2
View File
@@ -9,6 +9,8 @@ import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
)
type Config struct {
@@ -21,7 +23,7 @@ type Config struct {
}
type Device struct {
bus machine.SPI
bus drivers.SPI
a machine.Pin
b machine.Pin
c machine.Pin
@@ -52,7 +54,7 @@ type Device struct {
}
// New returns a new HUB75 driver. Pass in a fully configured SPI bus.
func New(b machine.SPI, latPin, oePin, aPin, bPin, cPin, dPin machine.Pin) Device {
func New(b drivers.SPI, latPin, oePin, aPin, bPin, cPin, dPin machine.Pin) Device {
aPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
bPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
cPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
+4 -2
View File
@@ -7,11 +7,13 @@ package mcp3008 // import "tinygo.org/x/drivers/mcp3008"
import (
"errors"
"machine"
"tinygo.org/x/drivers"
)
// Device wraps MCP3008 SPI ADC.
type Device struct {
bus machine.SPI
bus drivers.SPI
cs machine.Pin
tx []byte
rx []byte
@@ -32,7 +34,7 @@ type ADCPin struct {
}
// New returns a new MCP3008 driver. Pass in a fully configured SPI bus.
func New(b machine.SPI, csPin machine.Pin) *Device {
func New(b drivers.SPI, csPin machine.Pin) *Device {
d := &Device{bus: b,
cs: csPin,
tx: make([]byte, 3),
+4 -2
View File
@@ -9,11 +9,13 @@ import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
)
// Device wraps an SPI connection.
type Device struct {
bus machine.SPI
bus drivers.SPI
dcPin machine.Pin
rstPin machine.Pin
scePin machine.Pin
@@ -29,7 +31,7 @@ type Config struct {
}
// New creates a new PCD8544 connection. The SPI bus must already be configured.
func New(bus machine.SPI, dcPin, rstPin, scePin machine.Pin) *Device {
func New(bus drivers.SPI, dcPin, rstPin, scePin machine.Pin) *Device {
return &Device{
bus: bus,
dcPin: dcPin,
+13
View File
@@ -0,0 +1,13 @@
package drivers
// SPI represents a SPI bus. It is implemented by the machine.SPI type.
type SPI interface {
// Tx transmits the given buffer w and receives at the same time the buffer r.
// The two buffers must be the same length. The only exception is when w or r are nil,
// in which case Tx only transmits (without receiving) or only receives (while sending 0 bytes).
Tx(w, r []byte) error
// Transfer writes a single byte out on the SPI bus and receives a byte at the same time.
// If you want to transfer multiple bytes, it is more efficient to use Tx instead.
Transfer(b byte) (byte, error)
}
+2 -2
View File
@@ -37,7 +37,7 @@ type I2CBus struct {
}
type SPIBus struct {
wire machine.SPI
wire drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
@@ -62,7 +62,7 @@ func NewI2C(bus drivers.I2C) Device {
}
// NewSPI creates a new SSD1306 connection. The SPI wire must already be configured.
func NewSPI(bus machine.SPI, dcPin, resetPin, csPin machine.Pin) Device {
func NewSPI(bus drivers.SPI, dcPin, resetPin, csPin machine.Pin) Device {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
+4 -2
View File
@@ -10,6 +10,8 @@ import (
"errors"
"time"
"tinygo.org/x/drivers"
)
type Model uint8
@@ -17,7 +19,7 @@ type Rotation uint8
// Device wraps an SPI connection.
type Device struct {
bus machine.SPI
bus drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
@@ -35,7 +37,7 @@ type Config struct {
}
// New creates a new SSD1331 connection. The SPI wire must already be configured.
func New(bus machine.SPI, resetPin, dcPin, csPin machine.Pin) Device {
func New(bus drivers.SPI, resetPin, dcPin, csPin machine.Pin) Device {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
+4 -2
View File
@@ -9,6 +9,8 @@ import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
)
var (
@@ -18,7 +20,7 @@ var (
// Device wraps an SPI connection.
type Device struct {
bus machine.SPI
bus drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
@@ -40,7 +42,7 @@ type Config struct {
}
// New creates a new SSD1351 connection. The SPI wire must already be configured.
func New(bus machine.SPI, resetPin, dcPin, csPin, enPin, rwPin machine.Pin) Device {
func New(bus drivers.SPI, resetPin, dcPin, csPin, enPin, rwPin machine.Pin) Device {
return Device{
bus: bus,
dcPin: dcPin,
+4 -2
View File
@@ -10,6 +10,8 @@ import (
"time"
"errors"
"tinygo.org/x/drivers"
)
type Model uint8
@@ -17,7 +19,7 @@ type Rotation uint8
// Device wraps an SPI connection.
type Device struct {
bus machine.SPI
bus drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
@@ -44,7 +46,7 @@ type Config struct {
}
// New creates a new ST7735 connection. The SPI wire must already be configured.
func New(bus machine.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
+4 -3
View File
@@ -12,6 +12,8 @@ import (
"time"
"errors"
"tinygo.org/x/drivers"
)
type Rotation uint8
@@ -20,7 +22,7 @@ type FrameRate uint8
// Device wraps an SPI connection.
type Device struct {
bus machine.SPI
bus drivers.SPI
dcPin machine.Pin
resetPin machine.Pin
csPin machine.Pin
@@ -50,7 +52,7 @@ type Config struct {
}
// New creates a new ST7789 connection. The SPI wire must already be configured.
func New(bus machine.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
func New(bus drivers.SPI, resetPin, dcPin, csPin, blPin machine.Pin) Device {
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
resetPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
@@ -276,7 +278,6 @@ func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
return nil
}
// FillRectangle fills a rectangle at a given coordinates with a buffer
func (d *Device) FillRectangleWithBuffer(x, y, width, height int16, buffer []color.RGBA) error {
i, j := d.Size()
+4 -2
View File
@@ -9,6 +9,8 @@ import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
)
type Config struct {
@@ -19,7 +21,7 @@ type Config struct {
}
type Device struct {
bus machine.SPI
bus drivers.SPI
cs machine.Pin
dc machine.Pin
rst machine.Pin
@@ -51,7 +53,7 @@ var lutPartialUpdate = [30]uint8{
}
// New returns a new epd2in13x driver. Pass in a fully configured SPI bus.
func New(bus machine.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
+4 -2
View File
@@ -9,6 +9,8 @@ import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
)
type Config struct {
@@ -18,7 +20,7 @@ type Config struct {
}
type Device struct {
bus machine.SPI
bus drivers.SPI
cs machine.Pin
dc machine.Pin
rst machine.Pin
@@ -32,7 +34,7 @@ type Device struct {
type Color uint8
// New returns a new epd2in13x driver. Pass in a fully configured SPI bus.
func New(bus machine.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
+4 -2
View File
@@ -12,6 +12,8 @@ import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers"
)
type Config struct {
@@ -22,7 +24,7 @@ type Config struct {
}
type Device struct {
bus machine.SPI
bus drivers.SPI
cs machine.Pin
dc machine.Pin
rst machine.Pin
@@ -38,7 +40,7 @@ type Device struct {
type Rotation uint8
// New returns a new epd4in2 driver. Pass in a fully configured SPI bus.
func New(bus machine.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
func New(bus drivers.SPI, csPin, dcPin, rstPin, busyPin machine.Pin) Device {
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
dcPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
+13 -1
View File
@@ -13,6 +13,7 @@ import (
"machine"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/net"
)
@@ -258,7 +259,7 @@ type command struct {
}
type Device struct {
SPI machine.SPI
SPI drivers.SPI
CS machine.Pin
ACK machine.Pin
GPIO0 machine.Pin
@@ -268,6 +269,17 @@ type Device struct {
ssids [10]string
}
// New returns a new Wifinina driver.
func New(bus drivers.SPI, csPin, ackPin, gpio0Pin, resetPin machine.Pin) *Device {
return &Device{
SPI: bus,
CS: csPin,
ACK: ackPin,
GPIO0: gpio0Pin,
RESET: resetPin,
}
}
func (d *Device) Configure() {
net.UseDriver(d.NewDriver())
+79
View File
@@ -2,10 +2,15 @@
package ws2812 // import "tinygo.org/x/drivers/ws2812"
import (
"errors"
"image/color"
"machine"
"tinygo.org/x/drivers"
)
var errUnknownClockSpeed = errors.New("ws2812: unknown CPU clock speed")
// Device wraps a pin object for an easy driver interface.
type Device struct {
Pin machine.Pin
@@ -35,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
}
+58
View File
@@ -0,0 +1,58 @@
// +build avr
package ws2812
// This file implements the WS2812 protocol for AVR microcontrollers.
import (
"device/avr"
"machine"
)
// Send a single byte using the WS2812 protocol.
func (d Device) WriteByte(c byte) error {
// On AVR, the port is always the same for setting and clearing a register
// so use only one. This avoids the following error:
// error: inline assembly requires more registers than available
// Probably this is about pointer registers, which are very limited on AVR.
port, maskSet := d.Pin.PortMaskSet()
_, maskClear := d.Pin.PortMaskClear()
switch machine.CPUFrequency() {
case 16e6: // 16MHz
// See:
// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
// T0H: 4 cycles or 250ns
// T0L: 14 cycles or 875ns -> together 18 cycles or 1125ns
// T1H: 9 cycles or 562ns
// T1L: 8 cycles or 500ns -> together 17 cycles or 1062ns
avr.AsmFull(`
send_bit:
st {portSet}, {maskSet} ; [2] set output high
lsl {value} ; [1] shift off the next bit, store it in C
brcs skip_store ; [1/2] branch if this bit is high (long pulse)
st {portClear}, {maskClear} ; [2] set output low (short pulse)
skip_store:
nop ; [4] wait before changing the output again
nop
nop
nop
st {portClear}, {maskClear} ; [2] set output low (end of pulse)
nop ; [3]
nop
nop
subi {i}, 1 ; [1] subtract one (for the loop)
brne send_bit ; [1/2] send the next bit, if not at the end of the loop
`, map[string]interface{}{
"value": c,
"i": byte(8),
"maskSet": maskSet,
"portSet": port,
"maskClear": maskClear,
"portClear": port,
})
return nil
default:
return errUnknownClockSpeed
}
}
-49
View File
@@ -1,49 +0,0 @@
// +build atmega328p
package ws2812
// This file implements the WS2812 protocol for 16MHz AVR microcontrollers.
import (
"device/avr"
)
// Send a single byte using the WS2812 protocol.
func (d Device) WriteByte(c byte) error {
// For the AVR at 16MHz
portSet, maskSet := d.Pin.PortMaskSet()
portClear, maskClear := d.Pin.PortMaskClear()
// See:
// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
// T0H: 4 cycles or 250ns
// T0L: 14 cycles or 875ns -> together 18 cycles or 1125ns
// T1H: 9 cycles or 562ns
// T1L: 8 cycles or 500ns -> together 17 cycles or 1062ns
avr.AsmFull(`
send_bit:
st {portSet}, {maskSet} ; [2] set output high
lsl {value} ; [1] shift off the next bit, store it in C
brcs skip_store ; [1/2] branch if this bit is high (long pulse)
st {portClear}, {maskClear} ; [2] set output low (short pulse)
skip_store:
nop ; [4] wait before changing the output again
nop
nop
nop
st {portClear}, {maskClear} ; [2] set output low (end of pulse)
nop ; [3]
nop
nop
subi {i}, 1 ; [1] subtract one (for the loop)
brne send_bit ; [1/2] send the next bit, if not at the end of the loop
`, map[string]interface{}{
"value": c,
"i": 8,
"maskSet": maskSet,
"portSet": portSet,
"maskClear": maskClear,
"portClear": portClear,
})
return nil
}
-50
View File
@@ -1,50 +0,0 @@
// +build digispark
package ws2812
// This file implements the WS2812 protocol for 16.5MHz Digispark AVR microcontrollers.
// This is a slightly different implementation than the one for the atmega to work around a compiler bug.
import (
"device/avr"
)
// Send a single byte using the WS2812 protocol.
func (d Device) WriteByte(c byte) error {
// For the AVR at 16MHz
portSet, maskSet := d.Pin.PortMaskSet()
portClear, maskClear := d.Pin.PortMaskClear()
// See:
// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
// T0H: 4 cycles or 250ns
// T0L: 14 cycles or 875ns -> together 18 cycles or 1125ns
// T1H: 9 cycles or 562ns
// T1L: 8 cycles or 500ns -> together 17 cycles or 1062ns
avr.AsmFull(`
send_bit:
st {portSet}, {maskSet} ; [2] set output high
lsl {value} ; [1] shift off the next bit, store it in C
brcs skip_store ; [1/2] branch if this bit is high (long pulse)
st {portClear}, {maskClear} ; [2] set output low (short pulse)
skip_store:
nop ; [4] wait before changing the output again
nop
nop
nop
st {portClear}, {maskClear} ; [2] set output low (end of pulse)
nop ; [3]
nop
nop
subi {i}, 1 ; [1] subtract one (for the loop)
brne send_bit ; [1/2] send the next bit, if not at the end of the loop
`, map[string]interface{}{
"value": c,
"i": byte(8),
"maskSet": maskSet,
"portSet": portSet,
"maskClear": maskClear,
"portClear": portClear,
})
return nil
}
-3
View File
@@ -4,13 +4,10 @@ package ws2812
import (
"device"
"errors"
"machine"
"unsafe"
)
var errUnknownClockSpeed = errors.New("ws2812: unknown CPU clock speed")
func (d Device) WriteByte(c byte) error {
portSet, maskSet := d.Pin.PortMaskSet()
portClear, maskClear := d.Pin.PortMaskClear()