Uart0 support (#239)

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

No files matched your search

+77 -149
View File
@@ -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();
// 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
if (s_uartInteruptContext[uartNum] != nullptr)
{
// Fill the FIFO with new data
esp8266_uart1_async_buf = FillUartFifo(esp8266_uart1_async_buf, esp8266_uart1_async_buf_end);
// Disable TX interrupt when done
if (esp8266_uart1_async_buf == esp8266_uart1_async_buf_end)
// turn off uart
USC1(uartNum) = 0;
USIC(uartNum) = 0xffff;
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 gdbstub_uart_hdlr since it's static.
WRITE_PERI_REG(UART_INT_CLR(UART0), 0xffff);
// Interrupt handler is shared between UART0 & UART1
// 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
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;
}
}
}
}