mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-08 00:43:39 +00:00
ws2812: improve timings to be compatible with the WS2811
The WS2811 has a slightly slower data rate, but it is close enough that a single library can support both LEDs at the same time. In my testing, this package was already compatible with WS2811 LEDs before this change when running at 4-5V, but when the voltage dropped to 3V or so the LEDs started misbehaving. With these improved timings, the LEDs remained of the correct color even at very low voltages (although blue was starting to fade due to lack of voltage - that's not a software/protocol issue).
This commit is contained in:
committed by
Ron Evans
parent
cad03a7d4e
commit
7a2b1b8c90
@@ -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
|
||||
|
||||
+310
-12
@@ -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{}{
|
||||
|
||||
Reference in New Issue
Block a user