mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-22 07:29:01 +00:00
ws2812: generate assembly instead of handwriting it
Previously, a new implementation had to be written for each processor
speed. This is tedious and error-prone, therefore I've rewritten all of
the assembly code using a tool that will calculate exactly how many NOP
instructions are required.
I've also switched from build tags to a switch statement over all
processor speeds.
All in all, this means that:
- Chips with a supported CPU performance will automatically be
supported (no build tags necessary).
- Adding a new chip is as easy as adding the MHz count to the
generator script and to the switch statement.
Right now this is only for the Cortex-M, but it's certainly feasible to
extend this to other architectures such as the AVR.
This commit is contained in:
committed by
Ron Evans
parent
9a3bfd4826
commit
b3af3f594e
@@ -0,0 +1,354 @@
|
||||
// +build cortexm
|
||||
|
||||
package ws2812
|
||||
|
||||
// Warning: autogenerated file. Instead of modifying this file, change
|
||||
// gen-ws2812-arm.go and run "go generate".
|
||||
|
||||
import (
|
||||
"device"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
func (d Device) writeByte16(c byte) {
|
||||
portSet, maskSet := d.Pin.PortMaskSet()
|
||||
portClear, maskClear := d.Pin.PortMaskClear()
|
||||
|
||||
// Timings:
|
||||
// T0H: 4 - 6 cycles or 250.0ns - 375.0ns
|
||||
// T1H: 9 - 11 cycles or 562.5ns - 687.5ns
|
||||
// TLD: 8 - cycles or 500.0ns -
|
||||
mask := interrupt.Disable()
|
||||
value := uint32(c) << 24
|
||||
device.AsmFull(`
|
||||
1: @ send_bit
|
||||
str {maskSet}, {portSet} @ [2] T0H and T0L start here
|
||||
lsls {value}, #1 @ [1]
|
||||
bcs.n 2f @ [1/3] skip_store
|
||||
str {maskClear}, {portClear} @ [2] T0H -> T0L transition
|
||||
2: @ skip_store
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
str {maskClear}, {portClear} @ [2] T1H -> T1L transition
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
subs {i}, #1 @ [1]
|
||||
bne.n 1b @ [1/3] send_bit
|
||||
`, map[string]interface{}{
|
||||
"value": value,
|
||||
"i": 8,
|
||||
"maskSet": maskSet,
|
||||
"portSet": portSet,
|
||||
"maskClear": maskClear,
|
||||
"portClear": portClear,
|
||||
})
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
|
||||
func (d Device) writeByte48(c byte) {
|
||||
portSet, maskSet := d.Pin.PortMaskSet()
|
||||
portClear, maskClear := d.Pin.PortMaskClear()
|
||||
|
||||
// Timings:
|
||||
// T0H: 10 - 12 cycles or 208.3ns - 250.0ns
|
||||
// T1H: 27 - 29 cycles or 562.5ns - 604.2ns
|
||||
// TLD: 22 - cycles or 458.3ns -
|
||||
mask := interrupt.Disable()
|
||||
value := uint32(c) << 24
|
||||
device.AsmFull(`
|
||||
1: @ send_bit
|
||||
str {maskSet}, {portSet} @ [2] T0H and T0L start here
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
lsls {value}, #1 @ [1]
|
||||
bcs.n 2f @ [1/3] skip_store
|
||||
str {maskClear}, {portClear} @ [2] T0H -> T0L transition
|
||||
2: @ skip_store
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
str {maskClear}, {portClear} @ [2] T1H -> T1L transition
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
subs {i}, #1 @ [1]
|
||||
bne.n 1b @ [1/3] send_bit
|
||||
`, map[string]interface{}{
|
||||
"value": value,
|
||||
"i": 8,
|
||||
"maskSet": maskSet,
|
||||
"portSet": portSet,
|
||||
"maskClear": maskClear,
|
||||
"portClear": portClear,
|
||||
})
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
|
||||
func (d Device) writeByte64(c byte) {
|
||||
portSet, maskSet := d.Pin.PortMaskSet()
|
||||
portClear, maskClear := d.Pin.PortMaskClear()
|
||||
|
||||
// Timings:
|
||||
// T0H: 13 - 15 cycles or 203.1ns - 234.4ns
|
||||
// T1H: 36 - 38 cycles or 562.5ns - 593.8ns
|
||||
// TLD: 29 - cycles or 453.1ns -
|
||||
mask := interrupt.Disable()
|
||||
value := uint32(c) << 24
|
||||
device.AsmFull(`
|
||||
1: @ send_bit
|
||||
str {maskSet}, {portSet} @ [2] T0H and T0L start here
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
lsls {value}, #1 @ [1]
|
||||
bcs.n 2f @ [1/3] skip_store
|
||||
str {maskClear}, {portClear} @ [2] T0H -> T0L transition
|
||||
2: @ skip_store
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
str {maskClear}, {portClear} @ [2] T1H -> T1L transition
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
subs {i}, #1 @ [1]
|
||||
bne.n 1b @ [1/3] send_bit
|
||||
`, map[string]interface{}{
|
||||
"value": value,
|
||||
"i": 8,
|
||||
"maskSet": maskSet,
|
||||
"portSet": portSet,
|
||||
"maskClear": maskClear,
|
||||
"portClear": portClear,
|
||||
})
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
|
||||
func (d Device) writeByte120(c byte) {
|
||||
portSet, maskSet := d.Pin.PortMaskSet()
|
||||
portClear, maskClear := d.Pin.PortMaskClear()
|
||||
|
||||
// Timings:
|
||||
// T0H: 24 - 26 cycles or 200.0ns - 216.7ns
|
||||
// T1H: 66 - 68 cycles or 550.0ns - 566.7ns
|
||||
// TLD: 54 - cycles or 450.0ns -
|
||||
mask := interrupt.Disable()
|
||||
value := uint32(c) << 24
|
||||
device.AsmFull(`
|
||||
1: @ send_bit
|
||||
str {maskSet}, {portSet} @ [2] T0H and T0L start here
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
lsls {value}, #1 @ [1]
|
||||
bcs.n 2f @ [1/3] skip_store
|
||||
str {maskClear}, {portClear} @ [2] T0H -> T0L transition
|
||||
2: @ skip_store
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
str {maskClear}, {portClear} @ [2] T1H -> T1L transition
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
subs {i}, #1 @ [1]
|
||||
bne.n 1b @ [1/3] send_bit
|
||||
`, map[string]interface{}{
|
||||
"value": value,
|
||||
"i": 8,
|
||||
"maskSet": maskSet,
|
||||
"portSet": portSet,
|
||||
"maskClear": maskClear,
|
||||
"portClear": portClear,
|
||||
})
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
Reference in New Issue
Block a user