Uart0 support (#239)

This commit is contained in:
Michael Miller
2018-12-23 14:30:01 -08:00
committed by GitHub
parent de143ce482
commit 8933c307c5
10 changed files with 386 additions and 214 deletions

View File

@@ -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. // 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 // There are other Esp8266 alternative methods that provide more pin options, but also have
// other side effects. // other side effects.
//NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount); // for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods
//
// NeoEsp8266Uart800KbpsMethod uses GPI02 instead
// You can also use one of these for Esp8266, // You can also use one of these for Esp8266,
// each having their own restrictions // each having their own restrictions
@@ -38,9 +36,10 @@ NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
//NeoPixelBus<NeoRgbFeature, NeoEsp8266Dma400KbpsMethod> strip(PixelCount, PixelPin); //NeoPixelBus<NeoRgbFeature, NeoEsp8266Dma400KbpsMethod> strip(PixelCount, PixelPin);
// Uart method is good for the Esp-01 or other pin restricted modules // 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 // NOTE: These will ignore the PIN and use GPI02 pin
//NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart800KbpsMethod> strip(PixelCount, PixelPin); //NeoPixelBus<NeoGrbFeature, NeoEsp8266Uart1800KbpsMethod> strip(PixelCount, PixelPin);
//NeoPixelBus<NeoRgbFeature, NeoEsp8266Uart400KbpsMethod> 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 // The bitbang method is really only good if you are not using WiFi features of the ESP
// It works with all but pin 16 // It works with all but pin 16

View File

@@ -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. // 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 // There are other Esp8266 alternative methods that provide more pin options, but also have
// other side effects. // other side effects.
//NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount); // for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods
//
// NeoEsp8266Uart800KbpsMethod uses GPI02 instead
// NeoPixel animation time management object // NeoPixel animation time management object
NeoPixelAnimator animations(PixelCount, NEO_CENTISECONDS); NeoPixelAnimator animations(PixelCount, NEO_CENTISECONDS);

View File

@@ -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. // 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 // There are other Esp8266 alternative methods that provide more pin options, but also have
// other side effects. // other side effects.
//NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount); // for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods
//
// NeoEsp8266Uart800KbpsMethod uses GPI02 instead
NeoPixelAnimator animations(AnimationChannels); // NeoPixel animation management object NeoPixelAnimator animations(AnimationChannels); // NeoPixel animation management object

View File

@@ -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. // 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 // There are other Esp8266 alternative methods that provide more pin options, but also have
// other side effects. // other side effects.
//NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount); // for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods
//
// NeoEsp8266Uart800KbpsMethod uses GPI02 instead
// what is stored for state is specific to the need, in this case, the colors and // what is stored for state is specific to the need, in this case, the colors and
// the pixel to animate; // the pixel to animate;

View File

@@ -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. // 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 // There are other Esp8266 alternative methods that provide more pin options, but also have
// other side effects. // other side effects.
//NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount); // for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods
//
// NeoEsp8266Uart800KbpsMethod uses GPI02 instead
NeoPixelAnimator animations(PixelCount); // NeoPixel animation management object NeoPixelAnimator animations(PixelCount); // NeoPixel animation management object

View File

@@ -31,20 +31,34 @@ NeoEsp8266DmaWs2812xMethod KEYWORD1
NeoEsp8266DmaSk6812Method KEYWORD1 NeoEsp8266DmaSk6812Method KEYWORD1
NeoEsp8266Dma800KbpsMethod KEYWORD1 NeoEsp8266Dma800KbpsMethod KEYWORD1
NeoEsp8266Dma400KbpsMethod KEYWORD1 NeoEsp8266Dma400KbpsMethod KEYWORD1
NeoEsp8266UartWs2813Method KEYWORD1 NeoEsp8266Uart0Ws2813Method KEYWORD1
NeoEsp8266UartWs2812xMethod KEYWORD1 NeoEsp8266Uart0Ws2812xMethod KEYWORD1
NeoEsp8266UartWs2812Method KEYWORD1 NeoEsp8266Uart0Ws2812Method KEYWORD1
NeoEsp8266UartSk6812Method KEYWORD1 NeoEsp8266Uart0Sk6812Method KEYWORD1
NeoEsp8266UartLc8812Method KEYWORD1 NeoEsp8266Uart0Lc8812Method KEYWORD1
NeoEsp8266Uart800KbpsMethod KEYWORD1 NeoEsp8266Uart0800KbpsMethod KEYWORD1
NeoEsp8266Uart400KbpsMethod KEYWORD1 NeoEsp8266Uart0400KbpsMethod KEYWORD1
NeoEsp8266AsyncUartWs2813Method KEYWORD1 NeoEsp8266AsyncUart0Ws2813Method KEYWORD1
NeoEsp8266AsyncUartWs2812xMethod KEYWORD1 NeoEsp8266AsyncUart0Ws2812xMethod KEYWORD1
NeoEsp8266AsyncUartWs2812Method KEYWORD1 NeoEsp8266AsyncUart0Ws2812Method KEYWORD1
NeoEsp8266AsyncUartSk6812Method KEYWORD1 NeoEsp8266AsyncUart0Sk6812Method KEYWORD1
NeoEsp8266AsyncUartLc8812Method KEYWORD1 NeoEsp8266AsyncUart0Lc8812Method KEYWORD1
NeoEsp8266AsyncUart800KbpsMethod KEYWORD1 NeoEsp8266AsyncUart0800KbpsMethod KEYWORD1
NeoEsp8266AsyncUart400KbpsMethod 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 NeoEsp8266BitBangWs2813Method KEYWORD1
NeoEsp8266BitBangWs2812xMethod KEYWORD1 NeoEsp8266BitBangWs2812xMethod KEYWORD1
NeoEsp8266BitBangWs2812Method KEYWORD1 NeoEsp8266BitBangWs2812Method KEYWORD1

View File

@@ -8,7 +8,7 @@
"type": "git", "type": "git",
"url": "https://github.com/Makuna/NeoPixelBus" "url": "https://github.com/Makuna/NeoPixelBus"
}, },
"version": "2.3.5", "version": "2.4.0",
"frameworks": "arduino", "frameworks": "arduino",
"platforms": "*" "platforms": "*"
} }

View File

@@ -1,5 +1,5 @@
name=NeoPixelBus by Makuna name=NeoPixelBus by Makuna
version=2.3.5 version=2.4.0
author=Michael C. Miller (makuna@live.com) author=Michael C. Miller (makuna@live.com)
maintainer=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. sentence=A library that makes controlling NeoPixels (WS2811, WS2812, WS2813 & SK6812) and DotStars (APA102) easy.

View File

@@ -29,186 +29,114 @@ License along with NeoPixel. If not, see
#include <utility> #include <utility>
extern "C" extern "C"
{ {
#include <eagle_soc.h> // #include <eagle_soc.h>
#include <ets_sys.h> #include <ets_sys.h>
#include <uart.h> // #include <uart.h>
#include <uart_register.h> // #include <uart_register.h>
} }
#define UART1 1 volatile NeoEsp8266UartInterruptContext* NeoEsp8266UartInterruptContext::s_uartInteruptContext[] = { nullptr, nullptr };
#define UART1_INV_MASK (0x3f << 19)
// Gets the number of bytes waiting in the TX FIFO of UART1 void NeoEsp8266UartInterruptContext::StartSending(uint8_t uartNum, uint8_t* start, uint8_t* end)
static inline uint8_t getUartTxFifoLength()
{ {
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 void NeoEsp8266UartInterruptContext::Attach(uint8_t uartNum)
// You must ensure the TX FIFO isn't full
static inline void enqueue(uint8_t byte)
{ {
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 // Disable all interrupts
ETS_UART_INTR_DISABLE(); ETS_UART_INTR_DISABLE();
// Clear the RX & TX FIFOS // Clear the RX & TX FIFOS
SET_PERI_REG_MASK(UART_CONF0(UART1), UART_RXFIFO_RST | UART_TXFIFO_RST); const uint32_t fifoResetFlags = (1 << UCTXRST) | (1 << UCRXRST);
CLEAR_PERI_REG_MASK(UART_CONF0(UART1), UART_RXFIFO_RST | UART_TXFIFO_RST); USC0(uartNum) |= fifoResetFlags;
USC0(uartNum) &= ~(fifoResetFlags);
// Set the interrupt handler // attach the ISR if needed
ETS_UART_INTR_ATTACH(IntrHandler, NULL); 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 // 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 // Disable RX & TX interrupts. It maybe still 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); USIE(uartNum) &= ~((1 << UIFF) | (1 << UIFE));
// Clear all pending interrupts in UART1 // Clear all pending interrupts in UART1
WRITE_PERI_REG(UART_INT_CLR(UART1), 0xffff); USIC(uartNum) = 0xffff;
// Reenable interrupts // Reenable interrupts
ETS_UART_INTR_ENABLE(); ETS_UART_INTR_ENABLE();
} }
void NeoEsp8266AsyncUart::UpdateUart() void NeoEsp8266UartInterruptContext::Detach(uint8_t uartNum)
{ {
// Instruct ESP8266 hardware uart1 to send the pixels asynchronously // Disable interrupts
esp8266_uart1_async_buf = _pixels; ETS_UART_INTR_DISABLE();
esp8266_uart1_async_buf_end = _pixels + _sizePixels;
SET_PERI_REG_MASK(UART_INT_ENA(1), UART_TXFIFO_EMPTY_INT_ENA);
// Annotate when we started to send bytes, so we can calculate when we are ready to send again if (s_uartInteruptContext[uartNum] != nullptr)
_startTime = micros();
// Copy the pixels to the idle buffer and swap them
memcpy(_asyncPixels, _pixels, _sizePixels);
std::swap(_asyncPixels, _pixels);
}
void ICACHE_RAM_ATTR NeoEsp8266AsyncUart::IntrHandler(void* param)
{
// Interrupt handler is shared between UART0 & UART1
if (READ_PERI_REG(UART_INT_ST(UART1))) //any UART1 stuff
{ {
// Fill the FIFO with new data // turn off uart
esp8266_uart1_async_buf = FillUartFifo(esp8266_uart1_async_buf, esp8266_uart1_async_buf_end); USC1(uartNum) = 0;
// Disable TX interrupt when done USIC(uartNum) = 0xffff;
if (esp8266_uart1_async_buf == esp8266_uart1_async_buf_end) USIE(uartNum) = 0;
s_uartInteruptContext[uartNum] = nullptr;
if (s_uartInteruptContext[0] == nullptr &&
s_uartInteruptContext[1] == nullptr)
{ {
CLEAR_PERI_REG_MASK(UART_INT_ENA(UART1), UART_TXFIFO_EMPTY_INT_ENA); // detach our ISR
ETS_UART_INTR_ATTACH(NULL, NULL);
// return so we don't enable interrupts since there is no ISR anymore
return;
} }
// Clear all interrupts flags (just in case)
WRITE_PERI_REG(UART_INT_CLR(UART1), 0xffff);
} }
if (READ_PERI_REG(UART_INT_ST(UART0))) // Reenable interrupts
ETS_UART_INTR_ENABLE();
}
void ICACHE_RAM_ATTR NeoEsp8266UartInterruptContext::Isr(void* param)
{
// make sure this is for us
if (param == s_uartInteruptContext)
{ {
// TODO: gdbstub uses the interrupt of UART0, but there is no way to call its // Interrupt handler is shared between UART0 & UART1
// interrupt handler gdbstub_uart_hdlr since it's static. // so we need to test for both
WRITE_PERI_REG(UART_INT_CLR(UART0), 0xffff); for (uint8_t uartNum = 0; uartNum < 2; uartNum++)
{
if (USIS(uartNum) && s_uartInteruptContext[uartNum] != nullptr)
{
// Fill the FIFO with new data
s_uartInteruptContext[uartNum]->_asyncBuff = FillUartFifo(
uartNum,
s_uartInteruptContext[uartNum]->_asyncBuff,
s_uartInteruptContext[uartNum]->_asyncBuffEnd);
// Disable TX interrupt when done
if (s_uartInteruptContext[uartNum]->_asyncBuff == s_uartInteruptContext[uartNum]->_asyncBuffEnd)
{
// clear the TX FIFO Empty
USIE(uartNum) &= ~(1 << UIFE);
}
// Clear all interrupts flags (just in case)
USIC(uartNum) = 0xffff;
}
}
} }
} }

View File

@@ -29,27 +29,207 @@ License along with NeoPixel. If not, see
#ifdef ARDUINO_ARCH_ESP8266 #ifdef ARDUINO_ARCH_ESP8266
#include <Arduino.h> #include <Arduino.h>
// NeoEsp8266Uart contains all the low level details that doesn't // this template method class is used to track the data being sent on the uart
// depend on the transmission speed, and therefore, it isn't a template // when using the default serial ISR installed by the core
class NeoEsp8266Uart // 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: 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); // this template feature class is used to define the specifics for uart1
// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes
void UpdateUart(); //
class UartFeature1 : UartFeatureBase
static const uint8_t* ICACHE_RAM_ATTR FillUartFifo(const uint8_t* pixels, const uint8_t* end); {
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 size_t _sizePixels; // Size of '_pixels' buffer below
uint8_t* _pixels; // Holds LED color values uint8_t* _pixels; // Holds LED color values
uint32_t _startTime; // Microsecond count when last update started 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 // This UART controller uses two buffers that are swapped in every call to
// NeoPixelBus.Show(). One buffer contains the data that is being sent // 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 // Therefore, the result of NeoPixelBus.Pixels() is invalidated after
// every call to NeoPixelBus.Show() and must not be cached. // 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: 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);
free(_asyncPixels);
}
void InitializeUart(uint32_t uartBaud); 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(); 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: 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 uint8_t* _asyncPixels; // Holds a copy of LED color values taken when UpdateUart began
}; };
@@ -175,22 +392,45 @@ private:
}; };
}; };
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266Uart> NeoEsp8266UartWs2812xMethod; // uart 0
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266Uart> NeoEsp8266UartSk6812Method; typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>> NeoEsp8266Uart0Ws2812xMethod;
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266Uart> NeoEsp8266Uart800KbpsMethod; typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>> NeoEsp8266Uart0Sk6812Method;
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266Uart> NeoEsp8266Uart400KbpsMethod; typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>> NeoEsp8266Uart0800KbpsMethod;
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266Uart<UartFeature0, NeoEsp8266UartContext>> NeoEsp8266Uart0400KbpsMethod;
typedef NeoEsp8266UartWs2812xMethod NeoEsp8266UartWs2813Method; typedef NeoEsp8266Uart0Ws2812xMethod NeoEsp8266Uart0Ws2813Method;
typedef NeoEsp8266Uart800KbpsMethod NeoEsp8266UartWs2812Method; typedef NeoEsp8266Uart0800KbpsMethod NeoEsp8266Uart0Ws2812Method;
typedef NeoEsp8266UartSk6812Method NeoEsp8266UartLc8812Method; typedef NeoEsp8266Uart0Sk6812Method NeoEsp8266Uart0Lc8812Method;
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266AsyncUart> NeoEsp8266AsyncUartWs2812xMethod; // uart 1
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266AsyncUart> NeoEsp8266AsyncUartSk6812Method; typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedWs2812x, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>> NeoEsp8266Uart1Ws2812xMethod;
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed800Kbps, NeoEsp8266AsyncUart> NeoEsp8266AsyncUart800KbpsMethod; typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeedSk6812, NeoEsp8266Uart<UartFeature1, NeoEsp8266UartContext>> NeoEsp8266Uart1Sk6812Method;
typedef NeoEsp8266UartMethodBase<NeoEsp8266UartSpeed400Kbps, NeoEsp8266AsyncUart> NeoEsp8266AsyncUart400KbpsMethod; 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 #endif