Files
Ayke van Laethem 715c33d4b0 all: prepare for CGo changes in TinyGo
For details, see: https://github.com/tinygo-org/tinygo/pull/3927
This change just means we need to be more careful to use the right type,
now that types like C.uint32_t don't match to the Go equivalent (like
uint32).
2023-10-05 10:01:25 +02:00

71 lines
2.2 KiB
Go

//go:build avr
package ws2812
// This file implements the WS2812 protocol for AVR microcontrollers.
import (
"machine"
"runtime/interrupt"
"unsafe"
)
/*
#include <stdint.h>
__attribute__((always_inline))
void ws2812_writeByte16(char c, uint8_t *port, uint8_t maskSet, uint8_t maskClear) {
// 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
char i = 8;
__asm__ __volatile__(
"1:\n"
"\t st %[port], %[maskSet] ; [2] set output high\n"
"\t lsl %[value] ; [1] shift off the next bit, store it in C\n"
"\t brcs 2f ; [1/2] branch if this bit is high (long pulse)\n"
"\t st %[port], %[maskClear] ; [2] set output low (short pulse)\n"
"\t2:\n"
"\t nop ; [4] wait before changing the output again\n"
"\t nop\n"
"\t nop\n"
"\t nop\n"
"\t st %[port], %[maskClear] ; [2] set output low (end of pulse)\n"
"\t nop ; [3]\n"
"\t nop\n"
"\t nop\n"
"\t subi %[i], 1 ; [1] subtract one (for the loop)\n"
"\t brne 1b ; [1/2] send the next bit, if not at the end of the loop\n"
: [value]"+r"(c),
[i]"+r"(i)
: [maskSet]"r"(maskSet),
[maskClear]"r"(maskClear),
[port]"m"(*port));
}
*/
import "C"
// 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
C.ws2812_writeByte16(C.char(c), (*C.uint8_t)(unsafe.Pointer(port)), C.uint8_t(maskSet), C.uint8_t(maskClear))
interrupt.Restore(mask)
return nil
default:
interrupt.Restore(mask)
return errUnknownClockSpeed
}
}