Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 117 additions & 1 deletion ws2812/ws2812_xtensa.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down