Fix issue with 4.1 ESP-IDF

Update multiple channels, so the RMT channel is correct for MEM_BLOCK
This commit is contained in:
Brian Bulkowski committed 2020-09-14 18:28:58 -07:00
1 parent fa1fa56ef3
commit 220446e033
5 files changed
+662 -108

No files matched your search

@@ -4,6 +4,7 @@
#include "FastLED.h"
//static const char *TAG = "FastLED";
#include "esp_idf_version.h"
// -- Forward reference
@@ -128,6 +129,62 @@ void memorybuf_get(char *b, int *len) {
#endif /* FASTLED_ESP32_SHOWTIMING == 1 */
/*
** Internal functions that need to be exposed
**
** In 4.0, there's one code structure -- the "ll" interfaces have not been exposed, but
** one can use the rmt_ functions without setting up the driver.
*
** In ESP-IDF 4.1, the functions route through a different structure, which is only set up
** when the internal driver is called.... but one can bypass with the ll functions.
**
** In 4.2, the structures changed again, to go more directly.
**
** Really, epressif. You have so much error checking in your code, trying to protect embedded
** programmers for this and that, and keep changing code that probably doesn't need to change.
*/
#if ( ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0)) && ( ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 2, 0))
#define USE_FASTLED_RMT_FNS 1
#include <hal/rmt_ll.h>
esp_err_t fastled_set_tx_thr_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh)
{
/* regs is rmt_dev_t, which is the static "RMT" */
rmt_ll_set_tx_limit(&RMT, channel, evt_thresh);
rmt_ll_enable_tx_thres_interrupt(&RMT, channel, true);
return(ESP_OK);
}
esp_err_t fastled_set_tx_intr_en(rmt_channel_t channel, bool en)
{
/* regs is rmt_dev_t, which is the static "RMT" */
rmt_ll_enable_tx_end_interrupt(&RMT, channel, en);
return(ESP_OK);
}
esp_err_t fastled_tx_start(rmt_channel_t channel, bool tx_idx_rst)
{
if (tx_idx_rst) {
rmt_ll_reset_tx_pointer(&RMT, channel);
}
rmt_ll_clear_tx_end_interrupt(&RMT, channel);
rmt_ll_enable_tx_end_interrupt(&RMT, channel, true);
rmt_ll_start_tx(&RMT, channel);
return ESP_OK;
}
#else
#define USE_FASTLED_RMT_FNS 0
#endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 2, 0)
/*
** in later versions of the driver, they very carefully set the "mem_owner"
** flag before copying over. Let's do the same.
@@ -139,7 +196,7 @@ void memorybuf_get(char *b, int *len) {
#define RMT_MEM_OWNER_HW 1
#endif
static inline void rmt_set_mem_owner(rmt_channel_t channel, uint8_t owner)
static inline void fastled_set_mem_owner(rmt_channel_t channel, uint8_t owner)
{
RMT.conf_ch[(uint16_t)channel].conf1.mem_owner = owner;
}
@@ -202,17 +259,32 @@ void ESP32RMTController::init()
{
if (gInitialized) return;
// -- Create a semaphore to block execution until all the controllers are done
if (gTX_sem == NULL) {
gTX_sem = xSemaphoreCreateBinary();
xSemaphoreGive(gTX_sem);
}
for (int i = 0; i < FASTLED_RMT_MAX_CHANNELS; i++) {
gOnChannel[i] = NULL;
// if you are using MEM_BLOCK_NUM, the RMT channel won't be the same as the "channel number"
rmt_channel_t rmt_channel = rmt_channel_t(i * MEM_BLOCK_NUM);
// -- RMT configuration for transmission
// NOTE: In ESP-IDF 4.1++, there is a #define to init, but that doesn't exist
// in
// in earlier versions
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0)
rmt_config_t rmt_tx = RMT_DEFAULT_CONFIG_TX(gpio_num_t(0), rmt_channel);
#else
rmt_config_t rmt_tx;
memset((void*) &rmt_tx, 0, sizeof(rmt_tx));
rmt_tx.channel = rmt_channel_t(i);
rmt_tx.channel = rmt_channel;
rmt_tx.rmt_mode = RMT_MODE_TX;
rmt_tx.gpio_num = gpio_num_t(0); // The particular pin will be assigned later
#endif
rmt_tx.mem_block_num = MEM_BLOCK_NUM;
rmt_tx.clk_div = DIVIDER;
rmt_tx.tx_config.loop_en = false;
@@ -222,33 +294,32 @@ void ESP32RMTController::init()
rmt_tx.tx_config.idle_output_en = true;
// -- Apply the configuration
// warning: using more than MEM_BLOCK_NUM 1 means sometimes this might fail because
// we don't have enough MEM_BLOCKs. Todo: add code to track and only allocate as many channels
// as we have memblocks.
ESP_ERROR_CHECK( rmt_config(&rmt_tx) );
if (FASTLED_RMT_BUILTIN_DRIVER) {
ESP_ERROR_CHECK( rmt_driver_install(rmt_channel_t(i), 0, 0) );
} else {
ESP_ERROR_CHECK( rmt_driver_install(rmt_channel, 0, 0) );
}
else {
// -- 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 (like double-buffering)
ESP_ERROR_CHECK( rmt_set_tx_thr_intr_en(rmt_channel_t(i), true, PULSES_PER_FILL) );
#if USE_FASTLED_RMT_FNS
ESP_ERROR_CHECK( fastled_set_tx_thr_intr_en(rmt_channel, true, PULSES_PER_FILL) );
#else
ESP_ERROR_CHECK( rmt_set_tx_thr_intr_en(rmt_channel, true, PULSES_PER_FILL) );
#endif
}
}
// -- Create a semaphore to block execution until all the controllers are done
if (gTX_sem == NULL) {
gTX_sem = xSemaphoreCreateBinary();
xSemaphoreGive(gTX_sem);
}
if ( ! FASTLED_RMT_BUILTIN_DRIVER) {
if ( ! FASTLED_RMT_BUILTIN_DRIVER ) {
// -- Allocate the interrupt if we have not done so yet. This
// interrupt handler must work for all different kinds of
// strips, so it delegates to the refill function for each
// specific instantiation of ClocklessController.
if (gRMT_intr_handle == NULL) {
ESP_ERROR_CHECK(
esp_intr_alloc(ETS_RMT_INTR_SOURCE, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL3, interruptHandler, 0, &gRMT_intr_handle)
);
@@ -286,25 +357,16 @@ void ESP32RMTController::showPixels()
// -- First, fill all the available channels
int channel = 0;
while (channel < FASTLED_RMT_MAX_CHANNELS && gNext < gNumControllers) {
while ( (channel < FASTLED_RMT_MAX_CHANNELS) && (gNext < gNumControllers) ) {
ESP32RMTController::startNext(channel);
// -- Important: when we use more than one memory block, we need to
// skip the channels that would otherwise overlap in memory. SZG
channel += MEM_BLOCK_NUM;
channel++;
}
// -- Make sure it's been at least 50us since last show
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 data is sent. The interrupt handler
// will keep refilling the RMT buffers until it is all
// done; then it gives the semaphore back.
@@ -358,13 +420,13 @@ void ESP32RMTController::startNext(int channel)
void ESP32RMTController::startOnChannel(int channel)
{
// -- Assign this channel and configure the RMT
mRMT_channel = rmt_channel_t(channel);
// -- Store a reference to this controller, so we can get it
// inside the interrupt handler
gOnChannel[channel] = this;
// the RMT channel depends on the MEM_BLOCK
mRMT_channel = rmt_channel_t(channel * MEM_BLOCK_NUM);
// -- Assign the pin to this channel
rmt_set_pin(mRMT_channel, RMT_MODE_TX, mPin);
@@ -387,7 +449,11 @@ void ESP32RMTController::startOnChannel(int channel)
fillNext();
// -- Turn on the interrupts
#if USE_FASTLED_RMT_FNS
fastled_set_tx_intr_en(mRMT_channel, true);
#else
rmt_set_tx_intr_en(mRMT_channel, true);
#endif
// -- Kick off the transmission
tx_start();
@@ -399,7 +465,12 @@ void ESP32RMTController::startOnChannel(int channel)
// Setting this RMT flag is what actually kicks off the peripheral
void ESP32RMTController::tx_start()
{
#if USE_FASTLED_RMT_FNS
fastled_tx_start(mRMT_channel, true);
#else
rmt_tx_start(mRMT_channel, true);
#endif
mLastFill = __clock_cycles();
}
@@ -514,11 +585,11 @@ bool IRAM_ATTR ESP32RMTController::timingOk() {
mCur = mSize;
// other code also set some zeros to make sure there wasn't anything bad.
rmt_set_mem_owner(mRMT_channel, RMT_MEM_OWNER_SW);
fastled_set_mem_owner(mRMT_channel, RMT_MEM_OWNER_SW);
for (uint32_t j = 0; j < PULSES_PER_FILL; j++) {
* mRMT_mem_ptr++ = 0;
}
rmt_set_mem_owner(mRMT_channel, RMT_MEM_OWNER_HW);
fastled_set_mem_owner(mRMT_channel, RMT_MEM_OWNER_HW);
return false;
}
@@ -550,7 +621,7 @@ void IRAM_ATTR ESP32RMTController::fillNext()
volatile register uint32_t * pItem = mRMT_mem_ptr;
// set the owner to SW --- current driver does this but its not clear it matters
rmt_set_mem_owner(mRMT_channel, RMT_MEM_OWNER_SW);
fastled_set_mem_owner(mRMT_channel, RMT_MEM_OWNER_SW);
// Shift bits out, MSB first, setting RMTMEM.chan[n].data32[x] to the
// rmt_item32_t value corresponding to the buffered bit value
@@ -583,18 +654,18 @@ void IRAM_ATTR ESP32RMTController::fillNext()
mRMT_mem_ptr = pItem;
// set the owner back to HW
rmt_set_mem_owner(mRMT_channel, RMT_MEM_OWNER_HW);
fastled_set_mem_owner(mRMT_channel, RMT_MEM_OWNER_HW);
// update the time I last filled
mLastFill = __clock_cycles();
} else {
// -- No more data; signal to the RMT we are done
rmt_set_mem_owner(mRMT_channel, RMT_MEM_OWNER_SW);
fastled_set_mem_owner(mRMT_channel, RMT_MEM_OWNER_SW);
for (uint32_t j = 0; j < PULSES_PER_FILL; j++) {
* mRMT_mem_ptr++ = 0;
}
rmt_set_mem_owner(mRMT_channel, RMT_MEM_OWNER_HW);
fastled_set_mem_owner(mRMT_channel, RMT_MEM_OWNER_HW);
}
}
@@ -199,6 +199,9 @@ class ESP32RMTController
private:
// -- RMT has 8 channels, numbered 0 to 7
// NOTE: if you are using MAX_BLOCK not 1, you'll have fewer,
// and you need to "skip around" -- the RMT channels for MAX_BLOCK == 2
// are 0, 2, 4, 6.... etc
rmt_channel_t mRMT_channel;
// -- Store the GPIO pin