mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-03 06:27:47 +00:00
Compare commits
1 Commits
dev
...
ws2812-xtensa-c
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b7da60f8f |
+30
-10
@@ -18,16 +18,8 @@ import (
|
|||||||
// the new assembly implementation - no fiddly timings to calculate and no nops
|
// the new assembly implementation - no fiddly timings to calculate and no nops
|
||||||
// to count!
|
// to count!
|
||||||
//
|
//
|
||||||
// Right now this is specific to Cortex-M chips and assume the following things:
|
// Using templates, this code targets various microcontroller architectures: ARM
|
||||||
// - Arithmetic operations (shift, add, sub) take up 1 clock cycle.
|
// (Cortex-M), RISC-V, and Xtensa.
|
||||||
// - The nop instruction also takes up 1 clock cycle.
|
|
||||||
// - Store instructions (to the GPIO pins) take up 2 clock cycles.
|
|
||||||
// - Branch instructions can take up 1 to 3 clock cycles. On the Cortex-M0, this
|
|
||||||
// depends on whether the branch is taken or not. On the M4, the documentation
|
|
||||||
// is less clear but it appears the instruction is still 1 to 3 cycles
|
|
||||||
// (possibly including some branch prediction).
|
|
||||||
// It is certainly possible to extend this to other architectures, such as AVR
|
|
||||||
// and RISC-V if needed.
|
|
||||||
//
|
//
|
||||||
// Here are two important resources. For the timings:
|
// Here are two important resources. For the timings:
|
||||||
// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
|
// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
|
||||||
@@ -104,6 +96,34 @@ var architectures = map[string]architectureImpl{
|
|||||||
@DELAY3
|
@DELAY3
|
||||||
addi %[i], %[i], -1 // [1]
|
addi %[i], %[i], -1 // [1]
|
||||||
bnez %[i], 1b // [1/3] send_bit
|
bnez %[i], 1b // [1/3] send_bit
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
"xtensa": {
|
||||||
|
// ESP8266 and ESP32
|
||||||
|
// Because I do not know the exact instruction timings, I'm going to
|
||||||
|
// assume that every instruction executes in one cycle. Branches and
|
||||||
|
// load/stores will probably be slower than that, but as long as all
|
||||||
|
// timings are only increased a little bit this should not be a problem.
|
||||||
|
buildTag: "xtensa",
|
||||||
|
minBaseCyclesT0H: 1 + 1 + 1, // shift + branch (not taken) + store
|
||||||
|
maxBaseCyclesT0H: 1 + 1 + 1, // shift + branch (not taken) + store
|
||||||
|
minBaseCyclesT1H: 1 + 1 + 1, // shift + branch (taken) + store
|
||||||
|
maxBaseCyclesT1H: 1 + 1 + 1, // shift + branch (taken) + store
|
||||||
|
minBaseCyclesTLD: 1 + 1 + 1, // subtraction + branch + store (in next cycle)
|
||||||
|
valueTemplate: "(uint32_t)c",
|
||||||
|
template: `
|
||||||
|
1: // send_bit
|
||||||
|
s32i %[maskSet], %[portSet] // [1] T0H and T1H start here
|
||||||
|
@DELAY1
|
||||||
|
slli %[value], %[value], 1 // [1] shift value to the left by 1
|
||||||
|
bbsi %[value], 8, 2f // [1] branch to skip_store if bit 8 is set
|
||||||
|
s32i %[maskClear], %[portClear] // [1] T0H -> T0L transition
|
||||||
|
2: // skip_store
|
||||||
|
@DELAY2
|
||||||
|
s32i %[maskClear], %[portClear] // [1] T1H -> T1L transition
|
||||||
|
@DELAY3
|
||||||
|
addi %[i], %[i], -1 // [1]
|
||||||
|
bnez %[i], 1b // [1] send_bit, T1H and T1L end here
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,602 @@
|
|||||||
|
//go:build xtensa
|
||||||
|
// +build xtensa
|
||||||
|
|
||||||
|
package ws2812
|
||||||
|
|
||||||
|
// Warning: autogenerated file. Instead of modifying this file, change
|
||||||
|
// gen-ws2812.go and run "go generate".
|
||||||
|
|
||||||
|
import "runtime/interrupt"
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
__attribute__((always_inline))
|
||||||
|
void ws2812_writeByte80(char c, uint32_t *portSet, uint32_t *portClear, uint32_t maskSet, uint32_t maskClear) {
|
||||||
|
// Timings:
|
||||||
|
// T0H: 28 - 28 cycles or 350.0ns - 350.0ns
|
||||||
|
// T1H: 84 - 84 cycles or 1050.0ns - 1050.0ns
|
||||||
|
// TLD: 92 - cycles or 1150.0ns -
|
||||||
|
uint32_t value = (uint32_t)c;
|
||||||
|
char i = 8;
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"1: // send_bit\n"
|
||||||
|
"\t s32i %[maskSet], %[portSet] // [1] T0H and T1H start here\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t slli %[value], %[value], 1 // [1] shift value to the left by 1\n"
|
||||||
|
"\t bbsi %[value], 8, 2f // [1] branch to skip_store if bit 8 is set\n"
|
||||||
|
"\t s32i %[maskClear], %[portClear] // [1] T0H -> T0L transition\n"
|
||||||
|
"\t2: // skip_store\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t s32i %[maskClear], %[portClear] // [1] T1H -> T1L transition\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t addi %[i], %[i], -1 // [1]\n"
|
||||||
|
"\t bnez %[i], 1b // [1] send_bit, T1H and T1L end here\n"
|
||||||
|
: [value]"+r"(value),
|
||||||
|
[i]"+r"(i)
|
||||||
|
: [maskSet]"r"(maskSet),
|
||||||
|
[portSet]"m"(*portSet),
|
||||||
|
[maskClear]"r"(maskClear),
|
||||||
|
[portClear]"m"(*portClear));
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((always_inline))
|
||||||
|
void ws2812_writeByte160(char c, uint32_t *portSet, uint32_t *portClear, uint32_t maskSet, uint32_t maskClear) {
|
||||||
|
// Timings:
|
||||||
|
// T0H: 56 - 56 cycles or 350.0ns - 350.0ns
|
||||||
|
// T1H: 168 - 168 cycles or 1050.0ns - 1050.0ns
|
||||||
|
// TLD: 184 - cycles or 1150.0ns -
|
||||||
|
uint32_t value = (uint32_t)c;
|
||||||
|
char i = 8;
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"1: // send_bit\n"
|
||||||
|
"\t s32i %[maskSet], %[portSet] // [1] T0H and T1H start here\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t slli %[value], %[value], 1 // [1] shift value to the left by 1\n"
|
||||||
|
"\t bbsi %[value], 8, 2f // [1] branch to skip_store if bit 8 is set\n"
|
||||||
|
"\t s32i %[maskClear], %[portClear] // [1] T0H -> T0L transition\n"
|
||||||
|
"\t2: // skip_store\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t s32i %[maskClear], %[portClear] // [1] T1H -> T1L transition\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t nop\n"
|
||||||
|
"\t addi %[i], %[i], -1 // [1]\n"
|
||||||
|
"\t bnez %[i], 1b // [1] send_bit, T1H and T1L end here\n"
|
||||||
|
: [value]"+r"(value),
|
||||||
|
[i]"+r"(i)
|
||||||
|
: [maskSet]"r"(maskSet),
|
||||||
|
[portSet]"m"(*portSet),
|
||||||
|
[maskClear]"r"(maskClear),
|
||||||
|
[portClear]"m"(*portClear));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
func (d Device) writeByte80(c byte) {
|
||||||
|
portSet, maskSet := d.Pin.PortMaskSet()
|
||||||
|
portClear, maskClear := d.Pin.PortMaskClear()
|
||||||
|
|
||||||
|
mask := interrupt.Disable()
|
||||||
|
C.ws2812_writeByte80(C.char(c), portSet, portClear, maskSet, maskClear)
|
||||||
|
|
||||||
|
interrupt.Restore(mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) writeByte160(c byte) {
|
||||||
|
portSet, maskSet := d.Pin.PortMaskSet()
|
||||||
|
portClear, maskClear := d.Pin.PortMaskClear()
|
||||||
|
|
||||||
|
mask := interrupt.Disable()
|
||||||
|
C.ws2812_writeByte160(C.char(c), portSet, portClear, maskSet, maskClear)
|
||||||
|
|
||||||
|
interrupt.Restore(mask)
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package ws2812 // import "tinygo.org/x/drivers/ws2812"
|
|||||||
|
|
||||||
//go:generate go run gen-ws2812.go -arch=cortexm 16 48 64 120 168
|
//go:generate go run gen-ws2812.go -arch=cortexm 16 48 64 120 168
|
||||||
//go:generate go run gen-ws2812.go -arch=tinygoriscv 160 320
|
//go:generate go run gen-ws2812.go -arch=tinygoriscv 160 320
|
||||||
|
//go:generate go run gen-ws2812.go -arch=xtensa 80 160
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|||||||
+2
-330
@@ -4,346 +4,18 @@
|
|||||||
package ws2812
|
package ws2812
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device"
|
|
||||||
"machine"
|
"machine"
|
||||||
"runtime/interrupt"
|
|
||||||
"unsafe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (d Device) WriteByte(c byte) error {
|
func (d Device) WriteByte(c byte) error {
|
||||||
portSet, maskSet := d.Pin.PortMaskSet()
|
|
||||||
portClear, maskClear := d.Pin.PortMaskClear()
|
|
||||||
mask := interrupt.Disable()
|
|
||||||
|
|
||||||
switch machine.CPUFrequency() {
|
switch machine.CPUFrequency() {
|
||||||
case 160e6: // 160MHz
|
case 160e6: // 160MHz
|
||||||
// See:
|
d.writeByte160(c)
|
||||||
// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
|
|
||||||
// Because I do not know the exact instruction timings, I'm going to
|
|
||||||
// assume that every instruction executes in one cycle. Branches and
|
|
||||||
// load/stores will probably be slower than that, but as long as all
|
|
||||||
// timings are only increased a little bit this should not be a problem
|
|
||||||
// (see above post).
|
|
||||||
// T0H: 40 cycles or 333.3ns
|
|
||||||
// T0L: 131 cycles or 1091.7ns
|
|
||||||
// +: 171 cycles or 1425.0ns
|
|
||||||
// T1H: 95 cycles or 791.7ns
|
|
||||||
// T1L: 75 cycles or 625.0ns
|
|
||||||
// +: 170 cycles or 1416.7ns
|
|
||||||
// Some documentation:
|
|
||||||
// http://cholla.mmto.org/esp8266/xtensa.html
|
|
||||||
// https://0x04.net/~mwk/doc/xtensa.pdf
|
|
||||||
device.AsmFull(`
|
|
||||||
1: // send_bit
|
|
||||||
s32i {maskSet}, {portSet}, 0 // [1] T0H and T1H start here
|
|
||||||
nop // [37]
|
|
||||||
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
|
|
||||||
slli {value}, {value}, 1 // [1] shift {value} to the left by 1
|
|
||||||
bbsi {value}, 8, 2f // [1] branch to skip_store if bit 8 is set
|
|
||||||
s32i {maskClear}, {portClear}, 0 // [1] T0H -> T0L transition
|
|
||||||
2: // skip_store
|
|
||||||
nop // [55]
|
|
||||||
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
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
s32i {maskClear}, {portClear}, 0 // [1] T1H -> T1L transition
|
|
||||||
nop // [72]
|
|
||||||
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
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
addi {i}, {i}, -1 // [1]
|
|
||||||
bnez {i}, 1b // [1] send_bit, T1H and T1L end here
|
|
||||||
|
|
||||||
// Restore original values after modifying them in the inline
|
|
||||||
// assembly. Not doing that would result in undefined behavior as
|
|
||||||
// the compiler doesn't know we're modifying these values.
|
|
||||||
movi.n {i}, 8
|
|
||||||
slli {value}, {value}, 8
|
|
||||||
`, map[string]interface{}{
|
|
||||||
// Note: casting pointers to uintptr here because of what might be
|
|
||||||
// an Xtensa backend bug with inline assembly.
|
|
||||||
"value": uint32(c),
|
|
||||||
"i": 8,
|
|
||||||
"maskSet": maskSet,
|
|
||||||
"portSet": uintptr(unsafe.Pointer(portSet)),
|
|
||||||
"maskClear": maskClear,
|
|
||||||
"portClear": uintptr(unsafe.Pointer(portClear)),
|
|
||||||
})
|
|
||||||
interrupt.Restore(mask)
|
|
||||||
return nil
|
return nil
|
||||||
case 80e6: // 80MHz
|
case 80e6: // 80MHz
|
||||||
// See docs for 160MHz.
|
d.writeByte80(c)
|
||||||
// T0H: 21 cycles or 262.5ns
|
|
||||||
// T0L: 67 cycles or 837.5ns
|
|
||||||
// +: 88 cycles or 1100.0ns
|
|
||||||
// T1H: 47 cycles or 587.5ns
|
|
||||||
// T1L: 39 cycles or 487.5ns
|
|
||||||
// +: 86 cycles or 1075.0ns
|
|
||||||
device.AsmFull(`
|
|
||||||
1: // send_bit
|
|
||||||
s32i {maskSet}, {portSet}, 0 // [1] T0H and T1H start here
|
|
||||||
nop // [18]
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
slli {value}, {value}, 1 // [1] shift {value} to the left by 1
|
|
||||||
bbsi {value}, 8, 2f // [1] branch to skip_store if bit 8 is set
|
|
||||||
s32i {maskClear}, {portClear}, 0 // [1] T0H -> T0L transition
|
|
||||||
2: // skip_store
|
|
||||||
nop // [27]
|
|
||||||
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
|
|
||||||
s32i {maskClear}, {portClear}, 0 // [1] T1H -> T1L transition
|
|
||||||
nop // [36]
|
|
||||||
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
|
|
||||||
addi {i}, {i}, -1 // [1]
|
|
||||||
bnez {i}, 1b // [1] send_bit, T1H and T1L end here
|
|
||||||
|
|
||||||
// Restore original values after modifying them in the inline
|
|
||||||
// assembly. Not doing that would result in undefined behavior as
|
|
||||||
// the compiler doesn't know we're modifying these values.
|
|
||||||
movi.n {i}, 8
|
|
||||||
slli {value}, {value}, 8
|
|
||||||
`, map[string]interface{}{
|
|
||||||
// Note: casting pointers to uintptr here because of what might be
|
|
||||||
// an Xtensa backend bug with inline assembly.
|
|
||||||
"value": uint32(c),
|
|
||||||
"i": 8,
|
|
||||||
"maskSet": maskSet,
|
|
||||||
"portSet": uintptr(unsafe.Pointer(portSet)),
|
|
||||||
"maskClear": maskClear,
|
|
||||||
"portClear": uintptr(unsafe.Pointer(portClear)),
|
|
||||||
})
|
|
||||||
interrupt.Restore(mask)
|
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
interrupt.Restore(mask)
|
|
||||||
return errUnknownClockSpeed
|
return errUnknownClockSpeed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user