From e35a9583d8c4cdbc7e6c4747aa17154be2f108bf Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 9 Sep 2020 18:40:24 +0200 Subject: [PATCH] ws2812: add support for ESP8266 This patch adds support for the ESP8266 chip by adding support for 80MHz operation. This should also work when the ESP32 is changed to 80MHz, but I didn't test it (as there is not currently a way to do that). Verified on a NodeMCU dev board with an ESP8266. --- ws2812/ws2812_xtensa.go | 118 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/ws2812/ws2812_xtensa.go b/ws2812/ws2812_xtensa.go index 3b2669936..ab95c1cf2 100644 --- a/ws2812/ws2812_xtensa.go +++ b/ws2812/ws2812_xtensa.go @@ -16,7 +16,7 @@ func (d Device) WriteByte(c byte) error { portClear, maskClear := d.Pin.PortMaskClear() switch machine.CPUFrequency() { - case 160e6: + case 160e6: // 160MHz // See: // https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/ // Because I do not know the exact instruction timings, I'm going to @@ -224,6 +224,122 @@ func (d Device) WriteByte(c byte) error { "portClear": uintptr(unsafe.Pointer(portClear)), }) return nil + case 80e6: // 80MHz + // See docs for 160MHz. + // T0H: 21 cycles or 262.5ns + // T0L: 67 cycles or 837.5ns + // +: 88 cycles or 1100.0ns + // T1H: 47 cycles or 587.5ns + // T1L: 39 cycles or 487.5ns + // +: 86 cycles or 1075.0ns + device.AsmFull(` + 1: // send_bit + s32i {maskSet}, {portSet}, 0 // [1] T0H and T1H start here + nop // [18] + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + slli {value}, {value}, 1 // [1] shift {value} to the left by 1 + bbsi {value}, 8, 2f // [1] branch to skip_store if bit 8 is set + s32i {maskClear}, {portClear}, 0 // [1] T0H -> T0L transition + 2: // skip_store + nop // [27] + 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 + s32i {maskClear}, {portClear}, 0 // [1] T1H -> T1L transition + nop // [36] + 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 + addi {i}, {i}, -1 // [1] + bnez {i}, 1b // [1] send_bit, T1H and T1L end here + + // Restore original values after modifying them in the inline + // assembly. Not doing that would result in undefined behavior as + // the compiler doesn't know we're modifying these values. + movi.n {i}, 8 + slli {value}, {value}, 8 + `, map[string]interface{}{ + // Note: casting pointers to uintptr here because of what might be + // an Xtensa backend bug with inline assembly. + "value": uint32(c), + "i": 8, + "maskSet": maskSet, + "portSet": uintptr(unsafe.Pointer(portSet)), + "maskClear": maskClear, + "portClear": uintptr(unsafe.Pointer(portClear)), + }) + return nil default: return errUnknownClockSpeed }