From 5f4806f0f9031554d80cacc17d9f66554b618861 Mon Sep 17 00:00:00 2001 From: Ron Evans Date: Tue, 28 Jan 2020 17:53:50 +0100 Subject: [PATCH] ws2812: work-arounds to allow Digispark to control WS2812 LEDs Signed-off-by: Ron Evans --- Makefile | 4 ++- examples/ws2812/digispark.go | 10 +++++++ examples/ws2812/main.go | 17 +++++++----- examples/ws2812/others.go | 9 ++++++ ws2812/ws2812_avr_digispark.go | 50 ++++++++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 examples/ws2812/digispark.go create mode 100644 examples/ws2812/others.go create mode 100644 ws2812/ws2812_avr_digispark.go diff --git a/Makefile b/Makefile index 6145c5d..edc5b7c 100644 --- a/Makefile +++ b/Makefile @@ -99,7 +99,9 @@ smoke-test: @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/webclient/main.go @md5sum ./build/test.hex - tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812/main.go + tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812 + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=digispark ./examples/ws2812 @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bme280/main.go @md5sum ./build/test.hex diff --git a/examples/ws2812/digispark.go b/examples/ws2812/digispark.go new file mode 100644 index 0000000..a4a58a5 --- /dev/null +++ b/examples/ws2812/digispark.go @@ -0,0 +1,10 @@ +// +build digispark + +package main + +import "machine" + +// This is the pin assignment for the Digispark only. +// Replace neo in the code below to match the pin +// that you are using if different. +var neo machine.Pin = 0 diff --git a/examples/ws2812/main.go b/examples/ws2812/main.go index ed568c4..32f52fc 100644 --- a/examples/ws2812/main.go +++ b/examples/ws2812/main.go @@ -1,8 +1,7 @@ -// Connects to an WS2812 RGB LED strip with 10 LEDS, such as -// on an Adafruit Circuit Playground Express board. +// Connects to an WS2812 RGB LED strip with 10 LEDS. // -// Replace machine.NEOPIXELS in the code below to match the pin -// that you are using, if you have a different board. +// See either the others.go or digispark.go files in this directory +// for the neopixels pin assignments. package main import ( @@ -13,12 +12,15 @@ import ( "tinygo.org/x/drivers/ws2812" ) +var leds [10]color.RGBA + func main() { - neo := machine.NEOPIXELS + led := machine.LED + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + neo.Configure(machine.PinConfig{Mode: machine.PinOutput}) ws := ws2812.New(neo) - leds := make([]color.RGBA, 10) rg := false for { @@ -33,7 +35,8 @@ func main() { } } - ws.WriteColors(leds) + ws.WriteColors(leds[:]) + led.Set(rg) time.Sleep(100 * time.Millisecond) } } diff --git a/examples/ws2812/others.go b/examples/ws2812/others.go new file mode 100644 index 0000000..d0ea7d2 --- /dev/null +++ b/examples/ws2812/others.go @@ -0,0 +1,9 @@ +// +build !digispark + +package main + +import "machine" + +// Replace neo in the code below to match the pin +// that you are using if different. +var neo machine.Pin = machine.NEOPIXELS diff --git a/ws2812/ws2812_avr_digispark.go b/ws2812/ws2812_avr_digispark.go new file mode 100644 index 0000000..c29c3a5 --- /dev/null +++ b/ws2812/ws2812_avr_digispark.go @@ -0,0 +1,50 @@ +// +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 +}