Updated to Sam Guyer's latest

This commit is contained in:
Brian Bulkowski
2020-08-20 18:03:25 -07:00
parent df69632301
commit cd08f93593
2 changed files with 128 additions and 54 deletions

View File

@ -3,6 +3,10 @@
#define FASTLED_INTERNAL
#include "FastLED.h"
static const char *TAG = "FastLED";
#define FASTLED_ESP32_SHOWTIMING 1
// -- Forward reference
class ESP32RMTController;
@ -22,13 +26,22 @@ static int gNext = 0;
static intr_handle_t gRMT_intr_handle = NULL;
// -- Global semaphore for the whole show process
// Semaphore is not given until all data has been sent
static xSemaphoreHandle gTX_sem = NULL;
// -- Make sure we can't call show() too quickly
CMinWait<50> gWait;
static bool gInitialized = false;
// -- SZG: For debugging purposes
#if FASTLED_ESP32_SHOWTIMING == 1
static uint32_t gLastFill[8];
static int gTooSlow[8];
static uint32_t gTotalTime[8];
#endif
ESP32RMTController::ESP32RMTController(int DATA_PIN, int T1, int T2, int T3)
: mPixelData(0),
mSize(0),
@ -60,12 +73,14 @@ ESP32RMTController::ESP32RMTController(int DATA_PIN, int T1, int T2, int T3)
mPin = gpio_num_t(DATA_PIN);
}
// -- Getters and setters for use in ClocklessController
uint8_t * ESP32RMTController::getPixelData(int size_in_bytes)
// -- Get or create the buffer for the pixel data
// We can't allocate it ahead of time because we don't have
// the PixelController object until show is called.
uint32_t * ESP32RMTController::getPixelBuffer(int size_in_bytes)
{
if (mPixelData == 0) {
mSize = size_in_bytes;
mPixelData = (uint8_t *) calloc( mSize, sizeof(uint8_t));
mSize = ((size_in_bytes-1) / sizeof(uint32_t)) + 1;
mPixelData = (uint32_t *) calloc( mSize, sizeof(uint32_t));
}
return mPixelData;
}
@ -76,13 +91,13 @@ void ESP32RMTController::init()
{
if (gInitialized) return;
ESP_LOGW(TAG, "controller init");
for (int i = 0; i < FASTLED_RMT_MAX_CHANNELS; i++) {
gOnChannel[i] = NULL;
// -- RMT configuration for transmission
rmt_config_t rmt_tx;
rmt_tx.channel = rmt_channel_t(i);
rmt_tx.rmt_mode = RMT_MODE_TX;
rmt_config_t rmt_tx = RMT_DEFAULT_CONFIG_TX((gpio_num_t)0, rmt_channel_t(i));
rmt_tx.gpio_num = gpio_num_t(0); // The particular pin will be assigned later
rmt_tx.mem_block_num = 1;
rmt_tx.clk_div = DIVIDER;
@ -98,9 +113,9 @@ void ESP32RMTController::init()
if (FASTLED_RMT_BUILTIN_DRIVER) {
rmt_driver_install(rmt_channel_t(i), 0, 0);
} else {
// -- Set up the RMT to send 1 pixel of the pulse buffer and then
// -- Set up the RMT to send 32 bits of the pulse buffer and then
// generate an interrupt. When we get this interrupt we
// fill the other part in preparation (kind of like double-buffering)
// fill the other part in preparation (like double-buffering)
rmt_set_tx_thr_intr_en(rmt_channel_t(i), true, PULSES_PER_FILL);
}
}
@ -130,7 +145,6 @@ void ESP32RMTController::showPixels()
if (gNumStarted == 0) {
// -- First controller: make sure everything is set up
ESP32RMTController::init();
xSemaphoreTake(gTX_sem, portMAX_DELAY);
#if FASTLED_ESP32_FLASH_LOCK == 1
// -- Make sure no flash operations happen right now
@ -146,6 +160,9 @@ void ESP32RMTController::showPixels()
if (gNumStarted == gNumControllers) {
gNext = 0;
// -- This Take always succeeds immediately
xSemaphoreTake(gTX_sem, portMAX_DELAY);
// -- First, fill all the available channels
int channel = 0;
while (channel < FASTLED_RMT_MAX_CHANNELS && gNext < gNumControllers) {
@ -154,21 +171,25 @@ void ESP32RMTController::showPixels()
}
// -- Make sure it's been at least 50us since last show
mWait.wait();
gWait.wait();
// -- Start them all
/* This turns out to be a bad idea. We don't want all of the interrupts
coming in at the same time.
for (int i = 0; i < channel; i++) {
ESP32RMTController * pController = gControllers[i];
pController->tx_start();
}
*/
// -- Wait here while the rest of the data is sent. The interrupt handler
// will keep refilling the RMT buffers until it is all sent; then it
// gives the semaphore back.
// -- Wait here while the data is sent. The interrupt handler
// will keep refilling the RMT buffers until it is all
// done; then it gives the semaphore back.
xSemaphoreTake(gTX_sem, portMAX_DELAY);
xSemaphoreGive(gTX_sem);
mWait.mark();
// -- Make sure we don't call showPixels too quickly
gWait.mark();
// -- Reset the counters
gNumStarted = 0;
@ -179,6 +200,16 @@ void ESP32RMTController::showPixels()
// -- Release the lock on flash operations
spi_flash_op_unlock();
#endif
#if FASTLED_ESP32_SHOWTIMING == 1
// uint32_t expected = (2080000L / (1000000000L/F_CPU));
for (int i = 0; i < gNumControllers; i++) {
if (gTooSlow[i] > 0) {
printf("Channel %d total time %d too slow %d\n",i,gTotalTime[i],gTooSlow[i]);
}
}
#endif
}
}
@ -217,17 +248,21 @@ void ESP32RMTController::startOnChannel(int channel)
// -- Use our custom driver to send the data incrementally
// -- Initialize the counters that keep track of where we are in
// the pixel data.
mRMT_mem_ptr = & (RMTMEM.chan[mRMT_channel].data32[0].val);
// the pixel data and the RMT buffer
mRMT_mem_start = & (RMTMEM.chan[mRMT_channel].data32[0].val);
mRMT_mem_ptr = mRMT_mem_start;
mCur = 0;
mWhichHalf = 0;
// -- Store 2 pixels worth of data (two "buffers" full)
// -- Fill both halves of the RMT buffer (a totaly of 64 bits of pixel data)
fillNext();
fillNext();
// -- Turn on the interrupts
rmt_set_tx_intr_en(mRMT_channel, true);
// -- Kick off the transmission
tx_start();
}
}
@ -235,8 +270,13 @@ void ESP32RMTController::startOnChannel(int channel)
// Setting this RMT flag is what actually kicks off the peripheral
void ESP32RMTController::tx_start()
{
// dev->conf_ch[channel].conf1.tx_start = 1;
rmt_tx_start(mRMT_channel, true);
#if FASTLED_ESP32_SHOWTIMING == 1
gLastFill[mRMT_channel] = __clock_cycles();
gTooSlow[mRMT_channel] = 0;
gTotalTime[mRMT_channel] = 0;
#endif
}
// -- A controller is done
@ -247,11 +287,11 @@ void ESP32RMTController::tx_start()
// controller is done until we look it up.
void ESP32RMTController::doneOnChannel(rmt_channel_t channel, void * arg)
{
ESP32RMTController * pController = gOnChannel[channel];
portBASE_TYPE HPTaskAwoken = 0;
// -- Turn off output on the pin
// SZG: Do I really need to do this?
// ESP32RMTController * pController = gOnChannel[channel];
// gpio_matrix_out(pController->mPin, 0x100, 0, 0);
gOnChannel[channel] = NULL;
@ -262,6 +302,7 @@ void ESP32RMTController::doneOnChannel(rmt_channel_t channel, void * arg)
if (FASTLED_RMT_BUILTIN_DRIVER) {
xSemaphoreGive(gTX_sem);
} else {
portBASE_TYPE HPTaskAwoken = 0;
xSemaphoreGiveFromISR(gTX_sem, &HPTaskAwoken);
if (HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR();
}
@ -270,7 +311,6 @@ void ESP32RMTController::doneOnChannel(rmt_channel_t channel, void * arg)
// start the next one on this channel
if (gNext < gNumControllers) {
startNext(channel);
pController->tx_start();
}
}
}
@ -281,6 +321,10 @@ void ESP32RMTController::doneOnChannel(rmt_channel_t channel, void * arg)
// next half of the RMT buffer with data.
void IRAM_ATTR ESP32RMTController::interruptHandler(void *arg)
{
#if FASTLED_ESP32_SHOWTIMING == 1
int64_t now = __clock_cycles();
#endif
// -- The basic structure of this code is borrowed from the
// interrupt handler in esp-idf/components/driver/rmt.c
uint32_t intr_st = RMT.int_st.val;
@ -292,18 +336,27 @@ void IRAM_ATTR ESP32RMTController::interruptHandler(void *arg)
ESP32RMTController * pController = gOnChannel[channel];
if (pController != NULL) {
// -- More to send on this channel
if (intr_st & BIT(tx_next_bit)) {
// -- More to send on this channel
RMT.int_clr.val |= BIT(tx_next_bit);
// -- Refill the half of the buffer that we just finished,
// allowing the other half to proceed.
pController->fillNext();
#if FASTLED_ESP32_SHOWTIMING == 1
uint32_t delta = (now - gLastFill[channel]);
if (delta > C_NS(50500)) {
gTooSlow[channel]++;
}
gTotalTime[channel] += delta;
gLastFill[channel] = now;
#endif
} else {
// -- Transmission is complete on this channel
if (intr_st & BIT(tx_done_bit)) {
RMT.int_clr.val |= BIT(tx_done_bit);
#if FASTLED_ESP32_SHOWTIMING == 1
uint32_t delta = (now - gLastFill[channel]);
gTotalTime[channel] += delta;
#endif
doneOnChannel(rmt_channel_t(channel), 0);
}
}
@ -319,18 +372,15 @@ void IRAM_ATTR ESP32RMTController::fillNext()
{
if (mCur < mSize) {
// -- Get the zero and one values into local variables
uint32_t one_val = mOne.val;
uint32_t zero_val = mZero.val;
// -- Fill 32 slots in the RMT memory
uint8_t a = mPixelData[mCur++];
uint8_t b = mPixelData[mCur++];
uint8_t c = mPixelData[mCur++];
uint8_t d = mPixelData[mCur++];
register uint32_t pixeldata = a << 24 | b << 16 | c << 8 | d;
register uint32_t one_val = mOne.val;
register uint32_t zero_val = mZero.val;
// -- Use locals for speed
volatile register uint32_t * pItem = mRMT_mem_ptr;
// -- Get the next four bytes of pixel data
register uint32_t pixeldata = mPixelData[mCur];
mCur++;
// Shift bits out, MSB first, setting RMTMEM.chan[n].data32[x] to the
// rmt_item32_t value corresponding to the buffered bit value
@ -344,7 +394,7 @@ void IRAM_ATTR ESP32RMTController::fillNext()
// -- Flip to the other half, resetting the pointer if necessary
mWhichHalf++;
if (mWhichHalf == 2) {
pItem = & (RMTMEM.chan[mRMT_channel].data32[0].val);
pItem = mRMT_mem_start;
mWhichHalf = 0;
}
@ -369,6 +419,7 @@ void ESP32RMTController::initPulseBuffer(int size_in_bytes)
mBufferSize = size_in_bytes * 8 * 4;
mBuffer = (rmt_item32_t *) calloc( mBufferSize, sizeof(rmt_item32_t));
}
mCurPulse = 0;
}
@ -385,3 +436,4 @@ void ESP32RMTController::convertByte(uint32_t byteval)
mCurPulse++;
}
}

View File

@ -193,12 +193,13 @@ private:
rmt_item32_t mOne;
// -- Pixel data
uint8_t * mPixelData;
uint32_t * mPixelData;
int mSize;
int mCur;
// -- RMT memory
volatile uint32_t * mRMT_mem_ptr;
volatile uint32_t * mRMT_mem_start;
int mWhichHalf;
// -- Buffer to hold all of the pulses. For the version that uses
@ -207,9 +208,6 @@ private:
uint16_t mBufferSize;
int mCurPulse;
// -- Make sure we can't call show() too quickly
CMinWait<50> mWait;
public:
// -- Constructor
@ -217,8 +215,8 @@ public:
// member variables.
ESP32RMTController(int DATA_PIN, int T1, int T2, int T3);
// -- Getters and setters for use in ClocklessController
uint8_t * getPixelData(int size_in_bytes);
// -- Get or create the pixel data buffer
uint32_t * getPixelBuffer(int size_in_bytes);
// -- Initialize RMT subsystem
// This only needs to be done once
@ -303,25 +301,49 @@ protected:
// This method loads all of the pixel data into a separate buffer for use by
// by the RMT driver. Copying does two important jobs: it fixes the color
// order for the pixels, and it performs the scaling/adjusting ahead of time.
// It also packs the bytes into 32 bit chunks with the right bit order.
void loadPixelData(PixelController<RGB_ORDER> & pixels)
{
// -- Make sure the buffer is allocated
int size = pixels.size() * 3;
uint8_t * pData = mRMTController.getPixelData(size);
int size_in_bytes = pixels.size() * 3;
uint32_t * pData = mRMTController.getPixelBuffer(size_in_bytes);
// -- Read out the pixel data using the pixel controller methods that
// perform the scaling and adjustments
int count = 0;
int which = 0;
while (pixels.has(1)) {
*pData++ = pixels.loadAndScale0();
*pData++ = pixels.loadAndScale1();
*pData++ = pixels.loadAndScale2();
pixels.advanceData();
pixels.stepDithering();
count += 3;
}
// -- Get the next four bytes of data
uint8_t four[4] = {0,0,0,0};
for (int i = 0; i < 4; i++) {
switch (which) {
case 0:
four[i] = pixels.loadAndScale0();
break;
case 1:
four[i] = pixels.loadAndScale1();
break;
case 2:
four[i] = pixels.loadAndScale2();
pixels.advanceData();
pixels.stepDithering();
break;
}
// -- Move to the next color
which++;
if (which > 2) which = 0;
assert(count == size);
// -- Stop if there's no more data
if ( ! pixels.has(1)) break;
}
// -- Pack the four bytes into a 32-bit value with the right bit order
uint8_t a = four[0];
uint8_t b = four[1];
uint8_t c = four[2];
uint8_t d = four[3];
pData[count++] = a << 24 | b << 16 | c << 8 | d;
}
}
// -- Show pixels