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:
Ayke van Laethem
2020-10-16 14:00:14 +02:00
committed by Ron Evans
parent e376785596
commit df64ce8f50
8 changed files with 73 additions and 103 deletions
+2
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
+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
+3
View File
@@ -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
+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()