From df0d0d29a454a0e17aaa459c1be447534ca83463 Mon Sep 17 00:00:00 2001 From: Olaf Flebbe Date: Sat, 30 Apr 2022 23:45:28 +0200 Subject: [PATCH] ws2812: support thingplus-rp2040 board Added 125 MHz rp2040 timing Added unsafe.Pointer for pointer conversion --- examples/ws2812/others.go | 4 +- examples/ws2812/thingplus-rp2040.go | 13 ++ ws2812/gen-ws2812.go | 3 +- ws2812/ws2812-asm_cortexm.go | 316 +++++++++++++++++++++++++++- ws2812/ws2812-asm_tinygoriscv.go | 5 +- ws2812/ws2812.go | 2 +- ws2812/ws2812_cortexm.go | 3 + 7 files changed, 335 insertions(+), 11 deletions(-) create mode 100644 examples/ws2812/thingplus-rp2040.go diff --git a/examples/ws2812/others.go b/examples/ws2812/others.go index 10cb248..493f54e 100644 --- a/examples/ws2812/others.go +++ b/examples/ws2812/others.go @@ -1,5 +1,5 @@ -//go:build !digispark && !arduino && !qtpy && !m5stamp_c3 -// +build !digispark,!arduino,!qtpy,!m5stamp_c3 +//go:build !digispark && !arduino && !qtpy && !m5stamp_c3 && !thingplus_rp2040 +// +build !digispark,!arduino,!qtpy,!m5stamp_c3,!thingplus_rp2040 package main diff --git a/examples/ws2812/thingplus-rp2040.go b/examples/ws2812/thingplus-rp2040.go new file mode 100644 index 0000000..21dfac3 --- /dev/null +++ b/examples/ws2812/thingplus-rp2040.go @@ -0,0 +1,13 @@ +//go:build thingplus_rp2040 +// +build thingplus_rp2040 + +package main + +import "machine" + +// This is the pin assignment for the internal neopixel of the +// Sparkfun thingplus rp2040. +// Replace neo and led in the code below to match the pin +// that you are using if different. +var neo machine.Pin = machine.GPIO8 +var led = machine.LED diff --git a/ws2812/gen-ws2812.go b/ws2812/gen-ws2812.go index 2948ebe..b87c074 100644 --- a/ws2812/gen-ws2812.go +++ b/ws2812/gen-ws2812.go @@ -252,7 +252,7 @@ func writeGoWrapper(f *os.File, arch string, megahertz int) error { 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), portSet, portClear, maskSet, maskClear)\n", megahertz) + fmt.Fprintf(buf, " C.ws2812_writeByte%d(C.char(c), (*uint32)(unsafe.Pointer(portSet)), (*uint32)(unsafe.Pointer(portClear)), maskSet, maskClear)\n", megahertz) buf.WriteString(` interrupt.Restore(mask) } @@ -293,6 +293,7 @@ package ws2812 // gen-ws2812.go and run "go generate". import "runtime/interrupt" +import "unsafe" /* #include diff --git a/ws2812/ws2812-asm_cortexm.go b/ws2812/ws2812-asm_cortexm.go index 0cf3739..781db6d 100644 --- a/ws2812/ws2812-asm_cortexm.go +++ b/ws2812/ws2812-asm_cortexm.go @@ -7,6 +7,7 @@ package ws2812 // gen-ws2812.go and run "go generate". import "runtime/interrupt" +import "unsafe" /* #include @@ -636,6 +637,301 @@ void ws2812_writeByte120(char c, uint32_t *portSet, uint32_t *portClear, uint32_ [portClear]"m"(*portClear)); } +__attribute__((always_inline)) +void ws2812_writeByte125(char c, uint32_t *portSet, uint32_t *portClear, uint32_t maskSet, uint32_t maskClear) { + // Timings: + // T0H: 44 - 46 cycles or 352.0ns - 368.0ns + // T1H: 132 - 134 cycles or 1056.0ns - 1072.0ns + // TLD: 144 - cycles or 1152.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 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 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 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_writeByte168(char c, uint32_t *portSet, uint32_t *portClear, uint32_t maskSet, uint32_t maskClear) { // Timings: @@ -1033,7 +1329,7 @@ func (d Device) writeByte16(c byte) { portClear, maskClear := d.Pin.PortMaskClear() mask := interrupt.Disable() - C.ws2812_writeByte16(C.char(c), portSet, portClear, maskSet, maskClear) + C.ws2812_writeByte16(C.char(c), (*uint32)(unsafe.Pointer(portSet)), (*uint32)(unsafe.Pointer(portClear)), maskSet, maskClear) interrupt.Restore(mask) } @@ -1043,7 +1339,7 @@ func (d Device) writeByte48(c byte) { portClear, maskClear := d.Pin.PortMaskClear() mask := interrupt.Disable() - C.ws2812_writeByte48(C.char(c), portSet, portClear, maskSet, maskClear) + C.ws2812_writeByte48(C.char(c), (*uint32)(unsafe.Pointer(portSet)), (*uint32)(unsafe.Pointer(portClear)), maskSet, maskClear) interrupt.Restore(mask) } @@ -1053,7 +1349,7 @@ func (d Device) writeByte64(c byte) { portClear, maskClear := d.Pin.PortMaskClear() mask := interrupt.Disable() - C.ws2812_writeByte64(C.char(c), portSet, portClear, maskSet, maskClear) + C.ws2812_writeByte64(C.char(c), (*uint32)(unsafe.Pointer(portSet)), (*uint32)(unsafe.Pointer(portClear)), maskSet, maskClear) interrupt.Restore(mask) } @@ -1063,7 +1359,17 @@ func (d Device) writeByte120(c byte) { portClear, maskClear := d.Pin.PortMaskClear() mask := interrupt.Disable() - C.ws2812_writeByte120(C.char(c), portSet, portClear, maskSet, maskClear) + C.ws2812_writeByte120(C.char(c), (*uint32)(unsafe.Pointer(portSet)), (*uint32)(unsafe.Pointer(portClear)), maskSet, maskClear) + + interrupt.Restore(mask) +} + +func (d Device) writeByte125(c byte) { + portSet, maskSet := d.Pin.PortMaskSet() + portClear, maskClear := d.Pin.PortMaskClear() + + mask := interrupt.Disable() + C.ws2812_writeByte125(C.char(c), (*uint32)(unsafe.Pointer(portSet)), (*uint32)(unsafe.Pointer(portClear)), maskSet, maskClear) interrupt.Restore(mask) } @@ -1073,7 +1379,7 @@ func (d Device) writeByte168(c byte) { portClear, maskClear := d.Pin.PortMaskClear() mask := interrupt.Disable() - C.ws2812_writeByte168(C.char(c), portSet, portClear, maskSet, maskClear) + C.ws2812_writeByte168(C.char(c), (*uint32)(unsafe.Pointer(portSet)), (*uint32)(unsafe.Pointer(portClear)), maskSet, maskClear) interrupt.Restore(mask) } diff --git a/ws2812/ws2812-asm_tinygoriscv.go b/ws2812/ws2812-asm_tinygoriscv.go index 75a1a99..3c5decb 100644 --- a/ws2812/ws2812-asm_tinygoriscv.go +++ b/ws2812/ws2812-asm_tinygoriscv.go @@ -7,6 +7,7 @@ package ws2812 // gen-ws2812.go and run "go generate". import "runtime/interrupt" +import "unsafe" /* #include @@ -1114,7 +1115,7 @@ func (d Device) writeByte160(c byte) { portClear, maskClear := d.Pin.PortMaskClear() mask := interrupt.Disable() - C.ws2812_writeByte160(C.char(c), portSet, portClear, maskSet, maskClear) + C.ws2812_writeByte160(C.char(c), (*uint32)(unsafe.Pointer(portSet)), (*uint32)(unsafe.Pointer(portClear)), maskSet, maskClear) interrupt.Restore(mask) } @@ -1124,7 +1125,7 @@ func (d Device) writeByte320(c byte) { portClear, maskClear := d.Pin.PortMaskClear() mask := interrupt.Disable() - C.ws2812_writeByte320(C.char(c), portSet, portClear, maskSet, maskClear) + C.ws2812_writeByte320(C.char(c), (*uint32)(unsafe.Pointer(portSet)), (*uint32)(unsafe.Pointer(portClear)), maskSet, maskClear) interrupt.Restore(mask) } diff --git a/ws2812/ws2812.go b/ws2812/ws2812.go index a93c9f0..a371c61 100644 --- a/ws2812/ws2812.go +++ b/ws2812/ws2812.go @@ -1,7 +1,7 @@ // Package ws2812 implements a driver for WS2812 and SK6812 RGB LED strips. 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 125 168 //go:generate go run gen-ws2812.go -arch=tinygoriscv 160 320 import ( diff --git a/ws2812/ws2812_cortexm.go b/ws2812/ws2812_cortexm.go index fb153b9..160c92c 100644 --- a/ws2812/ws2812_cortexm.go +++ b/ws2812/ws2812_cortexm.go @@ -29,6 +29,9 @@ func (d Device) WriteByte(c byte) error { case 120_000_000: // 120MHz d.writeByte120(c) return nil + case 125_000_000: // 125 MHz e.g. rp2040 + d.writeByte125(c) + return nil case 168_000_000: // 168MHz, e.g. stm32f405 d.writeByte168(c) return nil