mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-03 06:27:47 +00:00
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.
This commit is contained in:
committed by
Ron Evans
parent
e376785596
commit
df64ce8f50
@@ -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
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
// +build !digispark
|
||||
// +build !digispark,!arduino
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
package ws2812 // import "tinygo.org/x/drivers/ws2812"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"image/color"
|
||||
"machine"
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user