mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-08 00:43:39 +00:00
3b16972ed8
Signed-off-by: deadprogram <ron@hybridgroup.com>
64 lines
1.9 KiB
Go
64 lines
1.9 KiB
Go
//go:build avr
|
|
// +build avr
|
|
|
|
package ws2812
|
|
|
|
// This file implements the WS2812 protocol for AVR microcontrollers.
|
|
|
|
import (
|
|
"device/avr"
|
|
"machine"
|
|
"runtime/interrupt"
|
|
)
|
|
|
|
// Send a single byte using the WS2812 protocol.
|
|
func (d Device) WriteByte(c byte) error {
|
|
// On AVR, the port is always the same for setting and clearing a register
|
|
// so use only one. This avoids the following error:
|
|
// error: inline assembly requires more registers than available
|
|
// Probably this is about pointer registers, which are very limited on AVR.
|
|
port, maskSet := d.Pin.PortMaskSet()
|
|
_, maskClear := d.Pin.PortMaskClear()
|
|
mask := interrupt.Disable()
|
|
|
|
switch machine.CPUFrequency() {
|
|
case 16e6: // 16MHz
|
|
// See:
|
|
// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
|
|
// T0H: 4 cycles or 250ns
|
|
// T0L: 14 cycles or 875ns -> together 18 cycles or 1125ns
|
|
// T1H: 9 cycles or 562ns
|
|
// T1L: 8 cycles or 500ns -> together 17 cycles or 1062ns
|
|
avr.AsmFull(`
|
|
send_bit:
|
|
st {portSet}, {maskSet} ; [2] set output high
|
|
lsl {value} ; [1] shift off the next bit, store it in C
|
|
brcs skip_store ; [1/2] branch if this bit is high (long pulse)
|
|
st {portClear}, {maskClear} ; [2] set output low (short pulse)
|
|
skip_store:
|
|
nop ; [4] wait before changing the output again
|
|
nop
|
|
nop
|
|
nop
|
|
st {portClear}, {maskClear} ; [2] set output low (end of pulse)
|
|
nop ; [3]
|
|
nop
|
|
nop
|
|
subi {i}, 1 ; [1] subtract one (for the loop)
|
|
brne send_bit ; [1/2] send the next bit, if not at the end of the loop
|
|
`, map[string]interface{}{
|
|
"value": c,
|
|
"i": byte(8),
|
|
"maskSet": maskSet,
|
|
"portSet": port,
|
|
"maskClear": maskClear,
|
|
"portClear": port,
|
|
})
|
|
interrupt.Restore(mask)
|
|
return nil
|
|
default:
|
|
interrupt.Restore(mask)
|
|
return errUnknownClockSpeed
|
|
}
|
|
}
|