diff --git a/ws2812/gen-ws2812-arm.go b/ws2812/gen-ws2812-arm.go index a25738c..8f9b14e 100644 --- a/ws2812/gen-ws2812-arm.go +++ b/ws2812/gen-ws2812-arm.go @@ -26,6 +26,13 @@ import ( // (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: +// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/ +// For the assembly (more or less): +// https://cpldcpu.wordpress.com/2014/01/19/light_ws2812-library-v2-0/ +// The timings deviate a little bit from the code here, but so far the timings +// from wp.josh.com seem to be fine for the ws2812. // Clock frequencies to support, in MHz. var clockFrequencies = []int{16, 48, 64, 120} @@ -41,15 +48,29 @@ func writeImplementation(f *os.File, megahertz int) error { // T1H 1 code, high voltage time 550 700 5500 ns // TLD data, low voltage time 450 600 5000 ns // TLL latch, low voltage time 6000 ns + // The equivalent table for WS2811 LEDs would be the following: + // Symbol Parameter Min Typical Max Units + // T0H 0 code, high voltage time 350 500 650 ns + // T1H 1 code, high voltage time 1050 1200 5500 ns + // TLD data, low voltage time 1150 1300 5000 ns + // TLL latch, low voltage time 6000 ns + // Combining the two (min and max) leads to the following table: + // Symbol Parameter Min Typical Max Units + // T0H 0 code, high voltage time 350 - 500 ns + // T1H 1 code, high voltage time 1050 - 5500 ns + // TLD data, low voltage time 1150 - 5000 ns + // TLL latch, low voltage time 6000 ns + // These comined timings are used so that the ws2812 package is compatible + // with both WS2812 and with WS2811 chips. // T0H is the time the pin should be high to send a "0" bit. // T1H is the time the pin should be high to send a "1" bit. // TLD is the time the pin should be low between bits. // TLL is the time the pin should be low to apply (latch) the new colors. - minCyclesT0H := int(math.Ceil(0.200 / cycleTimeNS)) + minCyclesT0H := int(math.Ceil(0.350 / cycleTimeNS)) maxCyclesT0H := int(math.Floor(0.500 / cycleTimeNS)) - minCyclesT1H := int(math.Ceil(0.550 / cycleTimeNS)) + minCyclesT1H := int(math.Ceil(1.050 / cycleTimeNS)) maxCyclesT1H := int(math.Floor(5.500 / cycleTimeNS)) - minCyclesTLD := int(math.Ceil(0.450 / cycleTimeNS)) + minCyclesTLD := int(math.Ceil(1.150 / cycleTimeNS)) // Assembly template: // 1: @ send_bit diff --git a/ws2812/ws2812-asm_cortexm.go b/ws2812/ws2812-asm_cortexm.go index 30f5941..de32ee9 100644 --- a/ws2812/ws2812-asm_cortexm.go +++ b/ws2812/ws2812-asm_cortexm.go @@ -16,18 +16,26 @@ func (d Device) writeByte16(c byte) { 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 - + // T0H: 6 - 8 cycles or 375.0ns - 500.0ns + // T1H: 17 - 19 cycles or 1062.5ns - 1187.5ns + // TLD: 19 - cycles or 1187.5ns - mask := interrupt.Disable() value := uint32(c) << 24 device.AsmFull(` 1: @ send_bit str {maskSet}, {portSet} @ [2] T0H and T0L start here + 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 @@ -38,6 +46,17 @@ func (d Device) writeByte16(c byte) { 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{}{ @@ -56,9 +75,9 @@ func (d Device) writeByte48(c byte) { 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 - + // T0H: 17 - 19 cycles or 354.2ns - 395.8ns + // T1H: 51 - 53 cycles or 1062.5ns - 1104.2ns + // TLD: 56 - cycles or 1166.7ns - mask := interrupt.Disable() value := uint32(c) << 24 device.AsmFull(` @@ -70,10 +89,34 @@ func (d Device) writeByte48(c byte) { 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 @@ -110,6 +153,40 @@ func (d Device) writeByte48(c byte) { 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{}{ @@ -128,9 +205,9 @@ func (d Device) writeByte64(c byte) { 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 - + // T0H: 23 - 25 cycles or 359.4ns - 390.6ns + // T1H: 68 - 70 cycles or 1062.5ns - 1093.8ns + // TLD: 74 - cycles or 1156.2ns - mask := interrupt.Disable() value := uint32(c) << 24 device.AsmFull(` @@ -145,10 +222,42 @@ func (d Device) writeByte64(c byte) { 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 @@ -198,6 +307,51 @@ func (d Device) writeByte64(c byte) { 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{}{ @@ -216,9 +370,9 @@ func (d Device) writeByte120(c byte) { 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 - + // T0H: 42 - 44 cycles or 350.0ns - 366.7ns + // T1H: 126 - 128 cycles or 1050.0ns - 1066.7ns + // TLD: 138 - cycles or 1150.0ns - mask := interrupt.Disable() value := uint32(c) << 24 device.AsmFull(` @@ -244,10 +398,70 @@ func (d Device) writeByte120(c byte) { nop 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 nop nop nop @@ -341,6 +555,90 @@ func (d Device) writeByte120(c byte) { 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 + 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{}{