forked from Makuna/NeoPixelBus
Uart0 support (#239)
This commit is contained in:
@@ -25,9 +25,7 @@ NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
|
||||
// For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
|
||||
// There are other Esp8266 alternative methods that provide more pin options, but also have
|
||||
// other side effects.
|
||||
//NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount);
|
||||
//
|
||||
// NeoEsp8266Uart800KbpsMethod uses GPI02 instead
|
||||
// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods
|
||||
|
||||
// You can also use one of these for Esp8266,
|
||||
// each having their own restrictions
|
||||
@@ -38,9 +36,10 @@ NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
|
||||
//NeoPixelBus<NeoRgbFeature, NeoEsp8266Dma400KbpsMethod> strip(PixelCount, PixelPin);
|
||||
|
||||
// Uart method is good for the Esp-01 or other pin restricted modules
|
||||
// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods
|
||||
// NOTE: These will ignore the PIN and use GPI02 pin
|
||||
//NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart800KbpsMethod> strip(PixelCount, PixelPin);
|
||||
//NeoPixelBus<NeoRgbFeature, NeoEsp8266Uart400KbpsMethod> strip(PixelCount, PixelPin);
|
||||
//NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart1800KbpsMethod> strip(PixelCount, PixelPin);
|
||||
//NeoPixelBus<NeoRgbFeature, NeoEsp8266Uart1400KbpsMethod> strip(PixelCount, PixelPin);
|
||||
|
||||
// The bitbang method is really only good if you are not using WiFi features of the ESP
|
||||
// It works with all but pin 16
|
||||
|
@@ -29,10 +29,7 @@ NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
|
||||
// For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
|
||||
// There are other Esp8266 alternative methods that provide more pin options, but also have
|
||||
// other side effects.
|
||||
//NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount);
|
||||
//
|
||||
// NeoEsp8266Uart800KbpsMethod uses GPI02 instead
|
||||
|
||||
// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods
|
||||
|
||||
// NeoPixel animation time management object
|
||||
NeoPixelAnimator animations(PixelCount, NEO_CENTISECONDS);
|
||||
|
@@ -16,9 +16,7 @@ NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
|
||||
// For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
|
||||
// There are other Esp8266 alternative methods that provide more pin options, but also have
|
||||
// other side effects.
|
||||
//NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount);
|
||||
//
|
||||
// NeoEsp8266Uart800KbpsMethod uses GPI02 instead
|
||||
// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods
|
||||
|
||||
NeoPixelAnimator animations(AnimationChannels); // NeoPixel animation management object
|
||||
|
||||
|
@@ -29,9 +29,7 @@ NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
|
||||
// For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
|
||||
// There are other Esp8266 alternative methods that provide more pin options, but also have
|
||||
// other side effects.
|
||||
//NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount);
|
||||
//
|
||||
// NeoEsp8266Uart800KbpsMethod uses GPI02 instead
|
||||
// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods
|
||||
|
||||
// what is stored for state is specific to the need, in this case, the colors and
|
||||
// the pixel to animate;
|
||||
|
@@ -14,9 +14,7 @@ NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
|
||||
// For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
|
||||
// There are other Esp8266 alternative methods that provide more pin options, but also have
|
||||
// other side effects.
|
||||
//NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount);
|
||||
//
|
||||
// NeoEsp8266Uart800KbpsMethod uses GPI02 instead
|
||||
// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods
|
||||
|
||||
NeoPixelAnimator animations(PixelCount); // NeoPixel animation management object
|
||||
|
||||
|
42
keywords.txt
42
keywords.txt
@@ -31,20 +31,34 @@ NeoEsp8266DmaWs2812xMethod KEYWORD1
|
||||
NeoEsp8266DmaSk6812Method KEYWORD1
|
||||
NeoEsp8266Dma800KbpsMethod KEYWORD1
|
||||
NeoEsp8266Dma400KbpsMethod KEYWORD1
|
||||
NeoEsp8266UartWs2813Method KEYWORD1
|
||||
NeoEsp8266UartWs2812xMethod KEYWORD1
|
||||
NeoEsp8266UartWs2812Method KEYWORD1
|
||||
NeoEsp8266UartSk6812Method KEYWORD1
|
||||
NeoEsp8266UartLc8812Method KEYWORD1
|
||||
NeoEsp8266Uart800KbpsMethod KEYWORD1
|
||||
NeoEsp8266Uart400KbpsMethod KEYWORD1
|
||||
NeoEsp8266AsyncUartWs2813Method KEYWORD1
|
||||
NeoEsp8266AsyncUartWs2812xMethod KEYWORD1
|
||||
NeoEsp8266AsyncUartWs2812Method KEYWORD1
|
||||
NeoEsp8266AsyncUartSk6812Method KEYWORD1
|
||||
NeoEsp8266AsyncUartLc8812Method KEYWORD1
|
||||
NeoEsp8266AsyncUart800KbpsMethod KEYWORD1
|
||||
NeoEsp8266AsyncUart400KbpsMethod KEYWORD1
|
||||
NeoEsp8266Uart0Ws2813Method KEYWORD1
|
||||
NeoEsp8266Uart0Ws2812xMethod KEYWORD1
|
||||
NeoEsp8266Uart0Ws2812Method KEYWORD1
|
||||
NeoEsp8266Uart0Sk6812Method KEYWORD1
|
||||
NeoEsp8266Uart0Lc8812Method KEYWORD1
|
||||
NeoEsp8266Uart0800KbpsMethod KEYWORD1
|
||||
NeoEsp8266Uart0400KbpsMethod KEYWORD1
|
||||
NeoEsp8266AsyncUart0Ws2813Method KEYWORD1
|
||||
NeoEsp8266AsyncUart0Ws2812xMethod KEYWORD1
|
||||
NeoEsp8266AsyncUart0Ws2812Method KEYWORD1
|
||||
NeoEsp8266AsyncUart0Sk6812Method KEYWORD1
|
||||
NeoEsp8266AsyncUart0Lc8812Method KEYWORD1
|
||||
NeoEsp8266AsyncUart0800KbpsMethod KEYWORD1
|
||||
NeoEsp8266AsyncUart0400KbpsMethod KEYWORD1
|
||||
NeoEsp8266Uart1Ws2813Method KEYWORD1
|
||||
NeoEsp8266Uart1Ws2812xMethod KEYWORD1
|
||||
NeoEsp8266Uart1Ws2812Method KEYWORD1
|
||||
NeoEsp8266Uart1Sk6812Method KEYWORD1
|
||||
NeoEsp8266Uart1Lc8812Method KEYWORD1
|
||||
NeoEsp8266Uart1800KbpsMethod KEYWORD1
|
||||
NeoEsp8266Uart1400KbpsMethod KEYWORD1
|
||||
NeoEsp8266AsyncUart1Ws2813Method KEYWORD1
|
||||
NeoEsp8266AsyncUart1Ws2812xMethod KEYWORD1
|
||||
NeoEsp8266AsyncUart1Ws2812Method KEYWORD1
|
||||
NeoEsp8266AsyncUart1Sk6812Method KEYWORD1
|
||||
NeoEsp8266AsyncUart1Lc8812Method KEYWORD1
|
||||
NeoEsp8266AsyncUart1800KbpsMethod KEYWORD1
|
||||
NeoEsp8266AsyncUart1400KbpsMethod KEYWORD1
|
||||
NeoEsp8266BitBangWs2813Method KEYWORD1
|
||||
NeoEsp8266BitBangWs2812xMethod KEYWORD1
|
||||
NeoEsp8266BitBangWs2812Method KEYWORD1
|
||||
|
@@ -8,7 +8,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/Makuna/NeoPixelBus"
|
||||
},
|
||||
"version": "2.3.5",
|
||||
"version": "2.4.0",
|
||||
"frameworks": "arduino",
|
||||
"platforms": "*"
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
name=NeoPixelBus by Makuna
|
||||
version=2.3.5
|
||||
version=2.4.0
|
||||
author=Michael C. Miller (makuna@live.com)
|
||||
maintainer=Michael C. Miller (makuna@live.com)
|
||||
sentence=A library that makes controlling NeoPixels (WS2811, WS2812, WS2813 & SK6812) and DotStars (APA102) easy.
|
||||
|
@@ -29,186 +29,114 @@ License along with NeoPixel. If not, see
|
||||
#include <utility>
|
||||
extern "C"
|
||||
{
|
||||
#include <eagle_soc.h>
|
||||
// #include <eagle_soc.h>
|
||||
#include <ets_sys.h>
|
||||
#include <uart.h>
|
||||
#include <uart_register.h>
|
||||
// #include <uart.h>
|
||||
// #include <uart_register.h>
|
||||
}
|
||||
|
||||
#define UART1 1
|
||||
#define UART1_INV_MASK (0x3f << 19)
|
||||
volatile NeoEsp8266UartInterruptContext* NeoEsp8266UartInterruptContext::s_uartInteruptContext[] = { nullptr, nullptr };
|
||||
|
||||
// Gets the number of bytes waiting in the TX FIFO of UART1
|
||||
static inline uint8_t getUartTxFifoLength()
|
||||
void NeoEsp8266UartInterruptContext::StartSending(uint8_t uartNum, uint8_t* start, uint8_t* end)
|
||||
{
|
||||
return (U1S >> USTXC) & 0xff;
|
||||
// send the pixels asynchronously
|
||||
_asyncBuff = start;
|
||||
_asyncBuffEnd = end;
|
||||
|
||||
// enable the transmit interrupt
|
||||
USIE(uartNum) |= (1 << UIFE);
|
||||
}
|
||||
|
||||
// Append a byte to the TX FIFO of UART1
|
||||
// You must ensure the TX FIFO isn't full
|
||||
static inline void enqueue(uint8_t byte)
|
||||
void NeoEsp8266UartInterruptContext::Attach(uint8_t uartNum)
|
||||
{
|
||||
U1F = byte;
|
||||
}
|
||||
|
||||
static const uint8_t* esp8266_uart1_async_buf;
|
||||
static const uint8_t* esp8266_uart1_async_buf_end;
|
||||
|
||||
NeoEsp8266Uart::NeoEsp8266Uart(uint16_t pixelCount, size_t elementSize)
|
||||
{
|
||||
_sizePixels = pixelCount * elementSize;
|
||||
_pixels = (uint8_t*)malloc(_sizePixels);
|
||||
memset(_pixels, 0x00, _sizePixels);
|
||||
}
|
||||
|
||||
NeoEsp8266Uart::~NeoEsp8266Uart()
|
||||
{
|
||||
// Wait until the TX fifo is empty. This way we avoid broken frames
|
||||
// when destroying & creating a NeoPixelBus to change its length.
|
||||
while (getUartTxFifoLength() > 0)
|
||||
{
|
||||
yield();
|
||||
}
|
||||
|
||||
free(_pixels);
|
||||
}
|
||||
|
||||
void NeoEsp8266Uart::InitializeUart(uint32_t uartBaud)
|
||||
{
|
||||
// Configure the serial line with 1 start bit (0), 6 data bits and 1 stop bit (1)
|
||||
Serial1.begin(uartBaud, SERIAL_6N1, SERIAL_TX_ONLY);
|
||||
|
||||
// Invert the TX voltage associated with logic level so:
|
||||
// - A logic level 0 will generate a Vcc signal
|
||||
// - A logic level 1 will generate a Gnd signal
|
||||
CLEAR_PERI_REG_MASK(UART_CONF0(UART1), UART1_INV_MASK);
|
||||
SET_PERI_REG_MASK(UART_CONF0(UART1), (BIT(22)));
|
||||
}
|
||||
|
||||
void NeoEsp8266Uart::UpdateUart()
|
||||
{
|
||||
// Since the UART can finish sending queued bytes in the FIFO in
|
||||
// the background, instead of waiting for the FIFO to flush
|
||||
// we annotate the start time of the frame so we can calculate
|
||||
// when it will finish.
|
||||
_startTime = micros();
|
||||
|
||||
// Then keep filling the FIFO until done
|
||||
const uint8_t* ptr = _pixels;
|
||||
const uint8_t* end = ptr + _sizePixels;
|
||||
while (ptr != end)
|
||||
{
|
||||
ptr = FillUartFifo(ptr, end);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t* ICACHE_RAM_ATTR NeoEsp8266Uart::FillUartFifo(const uint8_t* pixels, const uint8_t* end)
|
||||
{
|
||||
// Remember: UARTs send less significant bit (LSB) first so
|
||||
// pushing ABCDEF byte will generate a 0FEDCBA1 signal,
|
||||
// including a LOW(0) start & a HIGH(1) stop bits.
|
||||
// Also, we have configured UART to invert logic levels, so:
|
||||
const uint8_t _uartData[4] = {
|
||||
0b110111, // On wire: 1 000 100 0 [Neopixel reads 00]
|
||||
0b000111, // On wire: 1 000 111 0 [Neopixel reads 01]
|
||||
0b110100, // On wire: 1 110 100 0 [Neopixel reads 10]
|
||||
0b000100, // On wire: 1 110 111 0 [NeoPixel reads 11]
|
||||
};
|
||||
uint8_t avail = (UART_TX_FIFO_SIZE - getUartTxFifoLength()) / 4;
|
||||
if (end - pixels > avail)
|
||||
{
|
||||
end = pixels + avail;
|
||||
}
|
||||
while (pixels < end)
|
||||
{
|
||||
uint8_t subpix = *pixels++;
|
||||
enqueue(_uartData[(subpix >> 6) & 0x3]);
|
||||
enqueue(_uartData[(subpix >> 4) & 0x3]);
|
||||
enqueue(_uartData[(subpix >> 2) & 0x3]);
|
||||
enqueue(_uartData[ subpix & 0x3]);
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
NeoEsp8266AsyncUart::NeoEsp8266AsyncUart(uint16_t pixelCount, size_t elementSize)
|
||||
: NeoEsp8266Uart(pixelCount, elementSize)
|
||||
{
|
||||
_asyncPixels = (uint8_t*)malloc(_sizePixels);
|
||||
}
|
||||
|
||||
NeoEsp8266AsyncUart::~NeoEsp8266AsyncUart()
|
||||
{
|
||||
// Remember: the UART interrupt can be sending data from _asyncPixels in the background
|
||||
while (esp8266_uart1_async_buf != esp8266_uart1_async_buf_end)
|
||||
{
|
||||
yield();
|
||||
}
|
||||
free(_asyncPixels);
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR NeoEsp8266AsyncUart::InitializeUart(uint32_t uartBaud)
|
||||
{
|
||||
NeoEsp8266Uart::InitializeUart(uartBaud);
|
||||
|
||||
// Disable all interrupts
|
||||
ETS_UART_INTR_DISABLE();
|
||||
|
||||
// Clear the RX & TX FIFOS
|
||||
SET_PERI_REG_MASK(UART_CONF0(UART1), UART_RXFIFO_RST | UART_TXFIFO_RST);
|
||||
CLEAR_PERI_REG_MASK(UART_CONF0(UART1), UART_RXFIFO_RST | UART_TXFIFO_RST);
|
||||
const uint32_t fifoResetFlags = (1 << UCTXRST) | (1 << UCRXRST);
|
||||
USC0(uartNum) |= fifoResetFlags;
|
||||
USC0(uartNum) &= ~(fifoResetFlags);
|
||||
|
||||
// Set the interrupt handler
|
||||
ETS_UART_INTR_ATTACH(IntrHandler, NULL);
|
||||
// attach the ISR if needed
|
||||
if (s_uartInteruptContext[0] == nullptr &&
|
||||
s_uartInteruptContext[1] == nullptr)
|
||||
{
|
||||
ETS_UART_INTR_ATTACH(Isr, s_uartInteruptContext);
|
||||
}
|
||||
|
||||
// attach the context
|
||||
s_uartInteruptContext[uartNum] = this;
|
||||
|
||||
// Set tx fifo trigger. 80 bytes gives us 200 microsecs to refill the FIFO
|
||||
WRITE_PERI_REG(UART_CONF1(UART1), 80 << UART_TXFIFO_EMPTY_THRHD_S);
|
||||
USC1(uartNum) = (80 << UCFET);
|
||||
|
||||
// Disable RX & TX interrupts. It is enabled by uart.c in the SDK
|
||||
CLEAR_PERI_REG_MASK(UART_INT_ENA(UART1), UART_RXFIFO_FULL_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA);
|
||||
// Disable RX & TX interrupts. It maybe still enabled by uart.c in the SDK
|
||||
USIE(uartNum) &= ~((1 << UIFF) | (1 << UIFE));
|
||||
|
||||
// Clear all pending interrupts in UART1
|
||||
WRITE_PERI_REG(UART_INT_CLR(UART1), 0xffff);
|
||||
USIC(uartNum) = 0xffff;
|
||||
|
||||
// Reenable interrupts
|
||||
ETS_UART_INTR_ENABLE();
|
||||
}
|
||||
|
||||
void NeoEsp8266AsyncUart::UpdateUart()
|
||||
void NeoEsp8266UartInterruptContext::Detach(uint8_t uartNum)
|
||||
{
|
||||
// Instruct ESP8266 hardware uart1 to send the pixels asynchronously
|
||||
esp8266_uart1_async_buf = _pixels;
|
||||
esp8266_uart1_async_buf_end = _pixels + _sizePixels;
|
||||
SET_PERI_REG_MASK(UART_INT_ENA(1), UART_TXFIFO_EMPTY_INT_ENA);
|
||||
// Disable interrupts
|
||||
ETS_UART_INTR_DISABLE();
|
||||
|
||||
// Annotate when we started to send bytes, so we can calculate when we are ready to send again
|
||||
_startTime = micros();
|
||||
if (s_uartInteruptContext[uartNum] != nullptr)
|
||||
{
|
||||
// turn off uart
|
||||
USC1(uartNum) = 0;
|
||||
USIC(uartNum) = 0xffff;
|
||||
USIE(uartNum) = 0;
|
||||
|
||||
// Copy the pixels to the idle buffer and swap them
|
||||
memcpy(_asyncPixels, _pixels, _sizePixels);
|
||||
std::swap(_asyncPixels, _pixels);
|
||||
s_uartInteruptContext[uartNum] = nullptr;
|
||||
|
||||
if (s_uartInteruptContext[0] == nullptr &&
|
||||
s_uartInteruptContext[1] == nullptr)
|
||||
{
|
||||
// detach our ISR
|
||||
ETS_UART_INTR_ATTACH(NULL, NULL);
|
||||
|
||||
// return so we don't enable interrupts since there is no ISR anymore
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR NeoEsp8266AsyncUart::IntrHandler(void* param)
|
||||
// Reenable interrupts
|
||||
ETS_UART_INTR_ENABLE();
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR NeoEsp8266UartInterruptContext::Isr(void* param)
|
||||
{
|
||||
// make sure this is for us
|
||||
if (param == s_uartInteruptContext)
|
||||
{
|
||||
// Interrupt handler is shared between UART0 & UART1
|
||||
if (READ_PERI_REG(UART_INT_ST(UART1))) //any UART1 stuff
|
||||
// so we need to test for both
|
||||
for (uint8_t uartNum = 0; uartNum < 2; uartNum++)
|
||||
{
|
||||
if (USIS(uartNum) && s_uartInteruptContext[uartNum] != nullptr)
|
||||
{
|
||||
// Fill the FIFO with new data
|
||||
esp8266_uart1_async_buf = FillUartFifo(esp8266_uart1_async_buf, esp8266_uart1_async_buf_end);
|
||||
s_uartInteruptContext[uartNum]->_asyncBuff = FillUartFifo(
|
||||
uartNum,
|
||||
s_uartInteruptContext[uartNum]->_asyncBuff,
|
||||
s_uartInteruptContext[uartNum]->_asyncBuffEnd);
|
||||
|
||||
// Disable TX interrupt when done
|
||||
if (esp8266_uart1_async_buf == esp8266_uart1_async_buf_end)
|
||||
if (s_uartInteruptContext[uartNum]->_asyncBuff == s_uartInteruptContext[uartNum]->_asyncBuffEnd)
|
||||
{
|
||||
CLEAR_PERI_REG_MASK(UART_INT_ENA(UART1), UART_TXFIFO_EMPTY_INT_ENA);
|
||||
}
|
||||
// Clear all interrupts flags (just in case)
|
||||
WRITE_PERI_REG(UART_INT_CLR(UART1), 0xffff);
|
||||
// clear the TX FIFO Empty
|
||||
USIE(uartNum) &= ~(1 << UIFE);
|
||||
}
|
||||
|
||||
if (READ_PERI_REG(UART_INT_ST(UART0)))
|
||||
{
|
||||
// TODO: gdbstub uses the interrupt of UART0, but there is no way to call its
|
||||
// interrupt handler gdbstub_uart_hdlr since it's static.
|
||||
WRITE_PERI_REG(UART_INT_CLR(UART0), 0xffff);
|
||||
// Clear all interrupts flags (just in case)
|
||||
USIC(uartNum) = 0xffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -29,27 +29,207 @@ License along with NeoPixel. If not, see
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#include <Arduino.h>
|
||||
|
||||
// NeoEsp8266Uart contains all the low level details that doesn't
|
||||
// depend on the transmission speed, and therefore, it isn't a template
|
||||
class NeoEsp8266Uart
|
||||
// this template method class is used to track the data being sent on the uart
|
||||
// when using the default serial ISR installed by the core
|
||||
// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes
|
||||
//
|
||||
class NeoEsp8266UartContext
|
||||
{
|
||||
public:
|
||||
// Gets the number of bytes waiting in the TX FIFO
|
||||
static inline uint8_t ICACHE_RAM_ATTR GetTxFifoLength(uint8_t uartNum)
|
||||
{
|
||||
return (USS(uartNum) >> USTXC) & 0xff;
|
||||
}
|
||||
// Append a byte to the TX FIFO
|
||||
static inline void ICACHE_RAM_ATTR Enqueue(uint8_t uartNum, uint8_t value)
|
||||
{
|
||||
USF(uartNum) = value;
|
||||
}
|
||||
|
||||
static const volatile uint8_t* ICACHE_RAM_ATTR FillUartFifo(uint8_t uartNum,
|
||||
const volatile uint8_t* pixels,
|
||||
const volatile uint8_t* end)
|
||||
{
|
||||
// Remember: UARTs send less significant bit (LSB) first so
|
||||
// pushing ABCDEF byte will generate a 0FEDCBA1 signal,
|
||||
// including a LOW(0) start & a HIGH(1) stop bits.
|
||||
// Also, we have configured UART to invert logic levels, so:
|
||||
const uint8_t _uartData[4] = {
|
||||
0b110111, // On wire: 1 000 100 0 [Neopixel reads 00]
|
||||
0b000111, // On wire: 1 000 111 0 [Neopixel reads 01]
|
||||
0b110100, // On wire: 1 110 100 0 [Neopixel reads 10]
|
||||
0b000100, // On wire: 1 110 111 0 [NeoPixel reads 11]
|
||||
};
|
||||
uint8_t avail = (UART_TX_FIFO_SIZE - GetTxFifoLength(uartNum)) / 4;
|
||||
if (end - pixels > avail)
|
||||
{
|
||||
end = pixels + avail;
|
||||
}
|
||||
while (pixels < end)
|
||||
{
|
||||
uint8_t subpix = *pixels++;
|
||||
Enqueue(uartNum, _uartData[(subpix >> 6) & 0x3]);
|
||||
Enqueue(uartNum, _uartData[(subpix >> 4) & 0x3]);
|
||||
Enqueue(uartNum, _uartData[(subpix >> 2) & 0x3]);
|
||||
Enqueue(uartNum, _uartData[subpix & 0x3]);
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
};
|
||||
|
||||
// this template method class is used to track the data being sent on the uart
|
||||
// when using our own UART ISR
|
||||
// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes
|
||||
//
|
||||
class NeoEsp8266UartInterruptContext : NeoEsp8266UartContext
|
||||
{
|
||||
public:
|
||||
NeoEsp8266UartInterruptContext() :
|
||||
_asyncBuff(nullptr),
|
||||
_asyncBuffEnd(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
bool IsSending()
|
||||
{
|
||||
return (_asyncBuff != _asyncBuffEnd);
|
||||
}
|
||||
|
||||
void StartSending(uint8_t uartNum, uint8_t* start, uint8_t* end);
|
||||
void Attach(uint8_t uartNum);
|
||||
void Detach(uint8_t uartNum);
|
||||
|
||||
private:
|
||||
volatile const uint8_t* _asyncBuff;
|
||||
volatile const uint8_t* _asyncBuffEnd;
|
||||
volatile static NeoEsp8266UartInterruptContext* s_uartInteruptContext[2];
|
||||
|
||||
static void ICACHE_RAM_ATTR Isr(void* param);
|
||||
};
|
||||
|
||||
// this template feature class is used a base for all others and contains
|
||||
// common methods
|
||||
//
|
||||
class UartFeatureBase
|
||||
{
|
||||
protected:
|
||||
NeoEsp8266Uart(uint16_t pixelCount, size_t elementSize);
|
||||
static void ConfigUart(uint8_t uartNum)
|
||||
{
|
||||
// clear all invert bits
|
||||
USC0(uartNum) &= ~((1 << UCDTRI) | (1 << UCRTSI) | (1 << UCTXI) | (1 << UCDSRI) | (1 << UCCTSI) | (1 << UCRXI));
|
||||
// Invert the TX voltage associated with logic level so:
|
||||
// - A logic level 0 will generate a Vcc signal
|
||||
// - A logic level 1 will generate a Gnd signal
|
||||
USC0(uartNum) |= (1 << UCTXI);
|
||||
}
|
||||
};
|
||||
|
||||
~NeoEsp8266Uart();
|
||||
// this template feature class is used to define the specifics for uart0
|
||||
// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes
|
||||
//
|
||||
class UartFeature0 : UartFeatureBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t Index = 0;
|
||||
static void Init(uint32_t baud)
|
||||
{
|
||||
// Configure the serial line with 1 start bit (0), 6 data bits and 1 stop bit (1)
|
||||
Serial.begin(baud, SERIAL_6N1, SERIAL_TX_ONLY);
|
||||
ConfigUart(Index);
|
||||
}
|
||||
};
|
||||
|
||||
void InitializeUart(uint32_t uartBaud);
|
||||
|
||||
void UpdateUart();
|
||||
|
||||
static const uint8_t* ICACHE_RAM_ATTR FillUartFifo(const uint8_t* pixels, const uint8_t* end);
|
||||
// this template feature class is used to define the specifics for uart1
|
||||
// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes
|
||||
//
|
||||
class UartFeature1 : UartFeatureBase
|
||||
{
|
||||
public:
|
||||
static const uint32_t Index = 1;
|
||||
static void Init(uint32_t baud)
|
||||
{
|
||||
// Configure the serial line with 1 start bit (0), 6 data bits and 1 stop bit (1)
|
||||
Serial1.begin(baud, SERIAL_6N1, SERIAL_TX_ONLY);
|
||||
ConfigUart(Index);
|
||||
}
|
||||
};
|
||||
|
||||
// this template method class is used a base for all others and contains
|
||||
// common properties and methods
|
||||
//
|
||||
// used by NeoEsp8266Uart and NeoEsp8266AsyncUart
|
||||
//
|
||||
class NeoEsp8266UartBase
|
||||
{
|
||||
protected:
|
||||
size_t _sizePixels; // Size of '_pixels' buffer below
|
||||
uint8_t* _pixels; // Holds LED color values
|
||||
uint32_t _startTime; // Microsecond count when last update started
|
||||
|
||||
NeoEsp8266UartBase(uint16_t pixelCount, size_t elementSize)
|
||||
{
|
||||
_sizePixels = pixelCount * elementSize;
|
||||
_pixels = (uint8_t*)malloc(_sizePixels);
|
||||
memset(_pixels, 0x00, _sizePixels);
|
||||
}
|
||||
|
||||
~NeoEsp8266UartBase()
|
||||
{
|
||||
free(_pixels);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// NeoEsp8266AsyncUart handles all transmission asynchronously using interrupts
|
||||
// this template method class is used to glue uart feature and context for
|
||||
// synchronous uart method
|
||||
//
|
||||
// used by NeoEsp8266UartMethodBase
|
||||
//
|
||||
template<typename T_UARTFEATURE, typename T_UARTCONTEXT> class NeoEsp8266Uart : public NeoEsp8266UartBase
|
||||
{
|
||||
protected:
|
||||
|
||||
NeoEsp8266Uart(uint16_t pixelCount, size_t elementSize) :
|
||||
NeoEsp8266UartBase(pixelCount, elementSize)
|
||||
{
|
||||
}
|
||||
|
||||
~NeoEsp8266Uart()
|
||||
{
|
||||
// Wait until the TX fifo is empty. This way we avoid broken frames
|
||||
// when destroying & creating a NeoPixelBus to change its length.
|
||||
while (T_UARTCONTEXT::GetTxFifoLength(T_UARTFEATURE::Index) > 0)
|
||||
{
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeUart(uint32_t uartBaud)
|
||||
{
|
||||
T_UARTFEATURE::Init(uartBaud);
|
||||
}
|
||||
|
||||
void UpdateUart()
|
||||
{
|
||||
// Since the UART can finish sending queued bytes in the FIFO in
|
||||
// the background, instead of waiting for the FIFO to flush
|
||||
// we annotate the start time of the frame so we can calculate
|
||||
// when it will finish.
|
||||
_startTime = micros();
|
||||
|
||||
// Then keep filling the FIFO until done
|
||||
const uint8_t* ptr = _pixels;
|
||||
const uint8_t* end = ptr + _sizePixels;
|
||||
while (ptr != end)
|
||||
{
|
||||
ptr = const_cast<uint8_t*>(T_UARTCONTEXT::FillUartFifo(T_UARTFEATURE::Index, ptr, end));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// this template method class is used to glue uart feature and context for
|
||||
// asynchronously uart method
|
||||
//
|
||||
// This UART controller uses two buffers that are swapped in every call to
|
||||
// NeoPixelBus.Show(). One buffer contains the data that is being sent
|
||||
@@ -58,19 +238,56 @@ protected:
|
||||
//
|
||||
// Therefore, the result of NeoPixelBus.Pixels() is invalidated after
|
||||
// every call to NeoPixelBus.Show() and must not be cached.
|
||||
class NeoEsp8266AsyncUart: public NeoEsp8266Uart
|
||||
//
|
||||
// used by NeoEsp8266UartMethodBase
|
||||
//
|
||||
template<typename T_UARTFEATURE, typename T_UARTCONTEXT> class NeoEsp8266AsyncUart : public NeoEsp8266UartBase
|
||||
{
|
||||
protected:
|
||||
NeoEsp8266AsyncUart(uint16_t pixelCount, size_t elementSize);
|
||||
NeoEsp8266AsyncUart(uint16_t pixelCount, size_t elementSize) :
|
||||
NeoEsp8266UartBase(pixelCount, elementSize)
|
||||
{
|
||||
_asyncPixels = (uint8_t*)malloc(_sizePixels);
|
||||
}
|
||||
|
||||
~NeoEsp8266AsyncUart();
|
||||
~NeoEsp8266AsyncUart()
|
||||
{
|
||||
// Remember: the UART interrupt can be sending data from _asyncPixels in the background
|
||||
while (_context.IsSending())
|
||||
{
|
||||
yield();
|
||||
}
|
||||
// detach context, which will disable intr, may disable ISR
|
||||
_context.Detach(T_UARTFEATURE::Index);
|
||||
|
||||
void InitializeUart(uint32_t uartBaud);
|
||||
free(_asyncPixels);
|
||||
}
|
||||
|
||||
void UpdateUart();
|
||||
void ICACHE_RAM_ATTR InitializeUart(uint32_t uartBaud)
|
||||
{
|
||||
T_UARTFEATURE::Init(uartBaud);
|
||||
|
||||
// attach the context, which will enable the ISR
|
||||
_context.Attach(T_UARTFEATURE::Index);
|
||||
}
|
||||
|
||||
void UpdateUart()
|
||||
{
|
||||
// Instruct ESP8266 hardware uart to send the pixels asynchronously
|
||||
_context.StartSending(T_UARTFEATURE::Index,
|
||||
_pixels,
|
||||
_pixels + _sizePixels);
|
||||
|
||||
// Annotate when we started to send bytes, so we can calculate when we are ready to send again
|
||||
_startTime = micros();
|
||||
|
||||
// Copy the pixels to the idle buffer and swap them
|
||||
memcpy(_asyncPixels, _pixels, _sizePixels);
|
||||
std::swap(_asyncPixels, _pixels);
|
||||
}
|
||||
|
||||
private:
|
||||
static void ICACHE_RAM_ATTR IntrHandler(void* param);
|
||||
T_UARTCONTEXT _context;
|
||||
|
||||
uint8_t* _asyncPixels; // Holds a copy of LED color values taken when UpdateUart began
|
||||
};
|
||||
@@ -175,22 +392,45 @@ private:
|
||||
};
|
||||
};
|
||||
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266Uart> NeoEsp8266UartWs2812xMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266Uart> NeoEsp8266UartSk6812Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266Uart> NeoEsp8266Uart800KbpsMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266Uart> NeoEsp8266Uart400KbpsMethod;
|
||||
// uart 0
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>> NeoEsp8266Uart0Ws2812xMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>> NeoEsp8266Uart0Sk6812Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>> NeoEsp8266Uart0800KbpsMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>> NeoEsp8266Uart0400KbpsMethod;
|
||||
|
||||
typedef NeoEsp8266UartWs2812xMethod NeoEsp8266UartWs2813Method;
|
||||
typedef NeoEsp8266Uart800KbpsMethod NeoEsp8266UartWs2812Method;
|
||||
typedef NeoEsp8266UartSk6812Method NeoEsp8266UartLc8812Method;
|
||||
typedef NeoEsp8266Uart0Ws2812xMethod NeoEsp8266Uart0Ws2813Method;
|
||||
typedef NeoEsp8266Uart0800KbpsMethod NeoEsp8266Uart0Ws2812Method;
|
||||
typedef NeoEsp8266Uart0Sk6812Method NeoEsp8266Uart0Lc8812Method;
|
||||
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266AsyncUart> NeoEsp8266AsyncUartWs2812xMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266AsyncUart> NeoEsp8266AsyncUartSk6812Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266AsyncUart> NeoEsp8266AsyncUart800KbpsMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266AsyncUart> NeoEsp8266AsyncUart400KbpsMethod;
|
||||
// uart 1
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>> NeoEsp8266Uart1Ws2812xMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>> NeoEsp8266Uart1Sk6812Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>> NeoEsp8266Uart1800KbpsMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>> NeoEsp8266Uart1400KbpsMethod;
|
||||
|
||||
typedef NeoEsp8266Uart1Ws2812xMethod NeoEsp8266Uart1Ws2813Method;
|
||||
typedef NeoEsp8266Uart1800KbpsMethod NeoEsp8266Uart1Ws2812Method;
|
||||
typedef NeoEsp8266Uart1Sk6812Method NeoEsp8266Uart1Lc8812Method;
|
||||
|
||||
// uart 0 async
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>> NeoEsp8266AsyncUart0Ws2812xMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>> NeoEsp8266AsyncUart0Sk6812Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>> NeoEsp8266AsyncUart0800KbpsMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266AsyncUart<UartFeature0, NeoEsp8266UartInterruptContext>> NeoEsp8266AsyncUart0400KbpsMethod;
|
||||
|
||||
typedef NeoEsp8266AsyncUart0Ws2812xMethod NeoEsp8266AsyncUart0Ws2813Method;
|
||||
typedef NeoEsp8266AsyncUart0800KbpsMethod NeoEsp8266AsyncUart0Ws2812Method;
|
||||
typedef NeoEsp8266AsyncUart0Sk6812Method NeoEsp8266AsyncUart0Lc8812Method;
|
||||
|
||||
// uart 1 async
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>> NeoEsp8266AsyncUart1Ws2812xMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>> NeoEsp8266AsyncUart1Sk6812Method;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>> NeoEsp8266AsyncUart1800KbpsMethod;
|
||||
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266AsyncUart<UartFeature1, NeoEsp8266UartInterruptContext>> NeoEsp8266AsyncUart1400KbpsMethod;
|
||||
|
||||
typedef NeoEsp8266AsyncUart1Ws2812xMethod NeoEsp8266AsyncUart1Ws2813Method;
|
||||
typedef NeoEsp8266AsyncUart1800KbpsMethod NeoEsp8266AsyncUart1Ws2812Method;
|
||||
typedef NeoEsp8266AsyncUart1Sk6812Method NeoEsp8266AsyncUart1Lc8812Method;
|
||||
|
||||
typedef NeoEsp8266AsyncUartWs2812xMethod NeoEsp8266AsyncUartWs2813Method;
|
||||
typedef NeoEsp8266AsyncUart800KbpsMethod NeoEsp8266AsyncUartWs2812Method;
|
||||
typedef NeoEsp8266AsyncUartSk6812Method NeoEsp8266AsyncUartLc8812Method;
|
||||
#endif
|
||||
|
||||
|
Reference in New Issue
Block a user