diff --git a/examples/ws2812/others.go b/examples/ws2812/others.go index d68d57e45..e41183434 100644 --- a/examples/ws2812/others.go +++ b/examples/ws2812/others.go @@ -1,4 +1,4 @@ -//go:build !digispark && !arduino && !arduino_uno && !xiao_esp32c3 +//go:build !digispark && !arduino && !arduino_uno && !xiao_esp32c3 && !teensy36 package main diff --git a/examples/ws2812/teensy36.go b/examples/ws2812/teensy36.go new file mode 100644 index 000000000..2d58e6cdf --- /dev/null +++ b/examples/ws2812/teensy36.go @@ -0,0 +1,10 @@ +//go:build teensy36 + +package main + +import "machine" + +func init() { + // The Teensy 3.6 has no onboard LEDs, so drive an external strip. + neo = machine.D02 +} diff --git a/smoketest.sh b/smoketest.sh index 866de14bf..a217fd700 100755 --- a/smoketest.sh +++ b/smoketest.sh @@ -109,6 +109,7 @@ tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples tinygo build -size short -o ./build/test.hex -target=arduino ./examples/ws2812 tinygo build -size short -o ./build/test.hex -target=digispark ./examples/ws2812 tinygo build -size short -o ./build/test.bin -target=xiao-esp32c3 ./examples/ws2812 +tinygo build -size short -o ./build/test.hex -target=teensy36 ./examples/ws2812 tinygo build -size short -o ./build/test.hex -target=arduino ./examples/ws2812/neopixel-shield tinygo build -size short -o ./build/test.hex -target=arduino-zero ./examples/ws2812/neopixel-shield tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bme280/main.go diff --git a/ws2812/doc.go b/ws2812/doc.go index e76c0323e..9542f4660 100644 --- a/ws2812/doc.go +++ b/ws2812/doc.go @@ -4,6 +4,6 @@ // On RP2040/RP2350 it uses PIO for hardware-timed control. package ws2812 // import "tinygo.org/x/drivers/ws2812" -//go:generate go run gen-ws2812.go -arch=cortexm 16 48 64 120 125 150 160 168 200 +//go:generate go run gen-ws2812.go -arch=cortexm 16 48 64 120 125 150 160 168 180 200 //go:generate go run gen-ws2812.go -arch=tinygoriscv 320 //go:generate go run gen-ws2812.go -arch=esp32c3 160 diff --git a/ws2812/gen-ws2812.go b/ws2812/gen-ws2812.go index c1f2634a0..73d344bc9 100644 --- a/ws2812/gen-ws2812.go +++ b/ws2812/gen-ws2812.go @@ -291,8 +291,12 @@ func writeGoWrapper(f *os.File, arch string, megahertz int) error { buf := &bytes.Buffer{} fmt.Fprintf(buf, "\n") fmt.Fprintf(buf, "func (d Device) writeByte%d(c byte) {\n", megahertz) - fmt.Fprintf(buf, " portSet, maskSet := d.Pin.PortMaskSet()\n") - fmt.Fprintf(buf, " portClear, maskClear := d.Pin.PortMaskClear()\n") + if arch == "cortexm" { + fmt.Fprintf(buf, " portSet, maskSet, portClear, maskClear := d.portMasks()\n") + } else { + fmt.Fprintf(buf, " portSet, maskSet := d.Pin.PortMaskSet()\n") + fmt.Fprintf(buf, " portClear, maskClear := d.Pin.PortMaskClear()\n") + } fmt.Fprintf(buf, "\n") fmt.Fprintf(buf, " mask := interrupt.Disable()\n") fmt.Fprintf(buf, " C.ws2812_writeByte%d(C.char(c), (*C.uint32_t)(unsafe.Pointer(portSet)), (*C.uint32_t)(unsafe.Pointer(portClear)), C.uint32_t(maskSet), C.uint32_t(maskClear))\n", megahertz) diff --git a/ws2812/ws2812-asm_cortexm.go b/ws2812/ws2812-asm_cortexm.go index a1722f6d3..fa8a97a79 100644 --- a/ws2812/ws2812-asm_cortexm.go +++ b/ws2812/ws2812-asm_cortexm.go @@ -2042,6 +2042,421 @@ void ws2812_writeByte168(char c, uint32_t *portSet, uint32_t *portClear, uint32_ [portClear]"m"(*portClear)); } +__attribute__((always_inline)) +void ws2812_writeByte180(char c, uint32_t *portSet, uint32_t *portClear, uint32_t maskSet, uint32_t maskClear) { + // Timings: + // T0H: 63 - 65 cycles or 350.0ns - 361.1ns + // T1H: 189 - 191 cycles or 1050.0ns - 1061.1ns + // TLD: 207 - cycles or 1150.0ns - + uint32_t value = (uint32_t)c << 24; + char i = 8; + __asm__ __volatile__( + "1: @ send_bit\n" + "\t str %[maskSet], %[portSet] @ [2] T0H and T0L 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 nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t lsls %[value], #1 @ [1]\n" + "\t bcs.n 2f @ [1/3] skip_store\n" + "\t str %[maskClear], %[portClear] @ [2] 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 nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t str %[maskClear], %[portClear] @ [2] 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 nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t nop\n" + "\t subs %[i], #1 @ [1]\n" + "\t beq.n 3f @ [1/3] end\n" + "\t b 1b @ [1/3] send_bit\n" + "\t3: @ end\n" + : [value]"+r"(value), + [i]"+r"(i) + : [maskSet]"r"(maskSet), + [portSet]"m"(*portSet), + [maskClear]"r"(maskClear), + [portClear]"m"(*portClear)); +} + __attribute__((always_inline)) void ws2812_writeByte200(char c, uint32_t *portSet, uint32_t *portClear, uint32_t maskSet, uint32_t maskClear) { // Timings: @@ -2504,8 +2919,7 @@ void ws2812_writeByte200(char c, uint32_t *portSet, uint32_t *portClear, uint32_ import "C" func (d Device) writeByte16(c byte) { - portSet, maskSet := d.Pin.PortMaskSet() - portClear, maskClear := d.Pin.PortMaskClear() + portSet, maskSet, portClear, maskClear := d.portMasks() mask := interrupt.Disable() C.ws2812_writeByte16(C.char(c), (*C.uint32_t)(unsafe.Pointer(portSet)), (*C.uint32_t)(unsafe.Pointer(portClear)), C.uint32_t(maskSet), C.uint32_t(maskClear)) @@ -2514,8 +2928,7 @@ func (d Device) writeByte16(c byte) { } func (d Device) writeByte48(c byte) { - portSet, maskSet := d.Pin.PortMaskSet() - portClear, maskClear := d.Pin.PortMaskClear() + portSet, maskSet, portClear, maskClear := d.portMasks() mask := interrupt.Disable() C.ws2812_writeByte48(C.char(c), (*C.uint32_t)(unsafe.Pointer(portSet)), (*C.uint32_t)(unsafe.Pointer(portClear)), C.uint32_t(maskSet), C.uint32_t(maskClear)) @@ -2524,8 +2937,7 @@ func (d Device) writeByte48(c byte) { } func (d Device) writeByte64(c byte) { - portSet, maskSet := d.Pin.PortMaskSet() - portClear, maskClear := d.Pin.PortMaskClear() + portSet, maskSet, portClear, maskClear := d.portMasks() mask := interrupt.Disable() C.ws2812_writeByte64(C.char(c), (*C.uint32_t)(unsafe.Pointer(portSet)), (*C.uint32_t)(unsafe.Pointer(portClear)), C.uint32_t(maskSet), C.uint32_t(maskClear)) @@ -2534,8 +2946,7 @@ func (d Device) writeByte64(c byte) { } func (d Device) writeByte120(c byte) { - portSet, maskSet := d.Pin.PortMaskSet() - portClear, maskClear := d.Pin.PortMaskClear() + portSet, maskSet, portClear, maskClear := d.portMasks() mask := interrupt.Disable() C.ws2812_writeByte120(C.char(c), (*C.uint32_t)(unsafe.Pointer(portSet)), (*C.uint32_t)(unsafe.Pointer(portClear)), C.uint32_t(maskSet), C.uint32_t(maskClear)) @@ -2544,8 +2955,7 @@ func (d Device) writeByte120(c byte) { } func (d Device) writeByte125(c byte) { - portSet, maskSet := d.Pin.PortMaskSet() - portClear, maskClear := d.Pin.PortMaskClear() + portSet, maskSet, portClear, maskClear := d.portMasks() mask := interrupt.Disable() C.ws2812_writeByte125(C.char(c), (*C.uint32_t)(unsafe.Pointer(portSet)), (*C.uint32_t)(unsafe.Pointer(portClear)), C.uint32_t(maskSet), C.uint32_t(maskClear)) @@ -2554,8 +2964,7 @@ func (d Device) writeByte125(c byte) { } func (d Device) writeByte150(c byte) { - portSet, maskSet := d.Pin.PortMaskSet() - portClear, maskClear := d.Pin.PortMaskClear() + portSet, maskSet, portClear, maskClear := d.portMasks() mask := interrupt.Disable() C.ws2812_writeByte150(C.char(c), (*C.uint32_t)(unsafe.Pointer(portSet)), (*C.uint32_t)(unsafe.Pointer(portClear)), C.uint32_t(maskSet), C.uint32_t(maskClear)) @@ -2564,8 +2973,7 @@ func (d Device) writeByte150(c byte) { } func (d Device) writeByte160(c byte) { - portSet, maskSet := d.Pin.PortMaskSet() - portClear, maskClear := d.Pin.PortMaskClear() + portSet, maskSet, portClear, maskClear := d.portMasks() mask := interrupt.Disable() C.ws2812_writeByte160(C.char(c), (*C.uint32_t)(unsafe.Pointer(portSet)), (*C.uint32_t)(unsafe.Pointer(portClear)), C.uint32_t(maskSet), C.uint32_t(maskClear)) @@ -2574,8 +2982,7 @@ func (d Device) writeByte160(c byte) { } func (d Device) writeByte168(c byte) { - portSet, maskSet := d.Pin.PortMaskSet() - portClear, maskClear := d.Pin.PortMaskClear() + portSet, maskSet, portClear, maskClear := d.portMasks() mask := interrupt.Disable() C.ws2812_writeByte168(C.char(c), (*C.uint32_t)(unsafe.Pointer(portSet)), (*C.uint32_t)(unsafe.Pointer(portClear)), C.uint32_t(maskSet), C.uint32_t(maskClear)) @@ -2583,9 +2990,17 @@ func (d Device) writeByte168(c byte) { interrupt.Restore(mask) } +func (d Device) writeByte180(c byte) { + portSet, maskSet, portClear, maskClear := d.portMasks() + + mask := interrupt.Disable() + C.ws2812_writeByte180(C.char(c), (*C.uint32_t)(unsafe.Pointer(portSet)), (*C.uint32_t)(unsafe.Pointer(portClear)), C.uint32_t(maskSet), C.uint32_t(maskClear)) + + interrupt.Restore(mask) +} + func (d Device) writeByte200(c byte) { - portSet, maskSet := d.Pin.PortMaskSet() - portClear, maskClear := d.Pin.PortMaskClear() + portSet, maskSet, portClear, maskClear := d.portMasks() mask := interrupt.Disable() C.ws2812_writeByte200(C.char(c), (*C.uint32_t)(unsafe.Pointer(portSet)), (*C.uint32_t)(unsafe.Pointer(portClear)), C.uint32_t(maskSet), C.uint32_t(maskClear)) diff --git a/ws2812/ws2812_cortexm.go b/ws2812/ws2812_cortexm.go index 44aa8de86..909eef5df 100644 --- a/ws2812/ws2812_cortexm.go +++ b/ws2812/ws2812_cortexm.go @@ -40,6 +40,9 @@ func (d Device) WriteByte(c byte) error { case 168_000_000: // 168MHz, e.g. stm32f405 d.writeByte168(c) return nil + case 180_000_000: // 180MHz, e.g. teensy36 + d.writeByte180(c) + return nil case 200_000_000: // 200MHz, e.g. rp2040 starting with TinyGo v0.37 d.writeByte200(c) return nil diff --git a/ws2812/ws2812_mk66f18.go b/ws2812/ws2812_mk66f18.go new file mode 100644 index 000000000..5668dfe51 --- /dev/null +++ b/ws2812/ws2812_mk66f18.go @@ -0,0 +1,10 @@ +//go:build mk66f18 + +package ws2812 + +// portMasks returns the bit-band aliases of the pin's PSOR and PCOR bits, +// so writing 1 sets or clears only this pin. +func (d Device) portMasks() (portSet *uint32, maskSet uint32, portClear *uint32, maskClear uint32) { + p := d.Pin.Fast() + return &p.PSOR.Reg, 1, &p.PCOR.Reg, 1 +} diff --git a/ws2812/ws2812_portmask_cortexm.go b/ws2812/ws2812_portmask_cortexm.go new file mode 100644 index 000000000..a13635f3b --- /dev/null +++ b/ws2812/ws2812_portmask_cortexm.go @@ -0,0 +1,10 @@ +//go:build cortexm && !mk66f18 + +package ws2812 + +// portMasks returns the registers and masks that set and clear the pin. +func (d Device) portMasks() (portSet *uint32, maskSet uint32, portClear *uint32, maskClear uint32) { + portSet, maskSet = d.Pin.PortMaskSet() + portClear, maskClear = d.Pin.PortMaskClear() + return +}