diff --git a/ws2812/ws2812-asm_cortexm.go b/ws2812/ws2812-asm_cortexm.go index 1f06c3335..a1722f6d3 100644 --- a/ws2812/ws2812-asm_cortexm.go +++ b/ws2812/ws2812-asm_cortexm.go @@ -1281,6 +1281,377 @@ void ws2812_writeByte150(char c, uint32_t *portSet, uint32_t *portClear, uint32_ [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 - 58 cycles or 350.0ns - 362.5ns + // T1H: 168 - 170 cycles or 1050.0ns - 1062.5ns + // TLD: 184 - 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 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 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 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: @@ -2192,6 +2563,16 @@ func (d Device) writeByte150(c byte) { 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), (*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) writeByte168(c byte) { portSet, maskSet := d.Pin.PortMaskSet() portClear, maskClear := d.Pin.PortMaskClear() diff --git a/ws2812/ws2812.go b/ws2812/ws2812.go index 2066e0361..9ca96954a 100644 --- a/ws2812/ws2812.go +++ b/ws2812/ws2812.go @@ -4,7 +4,7 @@ // 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 168 200 +//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=tinygoriscv 160 320 import ( diff --git a/ws2812/ws2812_cortexm.go b/ws2812/ws2812_cortexm.go index b1f6ed273..44aa8de86 100644 --- a/ws2812/ws2812_cortexm.go +++ b/ws2812/ws2812_cortexm.go @@ -34,6 +34,9 @@ func (d Device) WriteByte(c byte) error { case 150_000_000: // 150MHz, e.g. rp2350 d.writeByte150(c) return nil + case 160_000_000: // 160MHz, e.g. stm32u585 + d.writeByte160(c) + return nil case 168_000_000: // 168MHz, e.g. stm32f405 d.writeByte168(c) return nil