Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/FastLED/FastLED.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Guyer <sam.guyer@gmail.com>2019-08-19 02:23:04 +0300
committerDaniel Garcia <danielgarcia@gmail.com>2019-08-19 02:23:04 +0300
commit5efea077bd3fe3e12c0900880317ab6800d743a8 (patch)
tree18ab94f42a59d8fafcc9373ad2838add0142553c
parentdae69768c643f69a8856dfc1b769d62ae051b624 (diff)
Improvements to RMT driver (#867)
* Support for ESP32 Credit to Rina Shkrabova for the first cut. * Clean up interrupt handling I think there was actually an error in the interrupt enabling/disabling, but I also cleaned it up so that it is more clear how interrupts are handled. * Better interrupt handling * Added RMT version Not fully portable yet, though. The timing numbers are hard-wired for WS2812, and the RMT channel is also hard-wired. * Fixed the timing Timing is now computed from T1, T2, amd T3 instead of being hard-wired. * Better buffer management The RMT signal is sent in 10-pixel chunks, using double-buffering to hide the latency when possible. Also: assign RMT channels sequentially. * Total rewrite using Martin's code * Better comments * Fixed the timing calculation We were not doing the conversion from ESP32 cycles to RMT cycles correctly. Now it all works! * Added Martin's changes * Removed confusing comments * Added my name! * Fixed ESP32 compile problem On ESP platforms the dev kit provides the function __cxa_pure_virtual, so there is no need to define it. * honor WAIT_TIME for chipsets that need it (for example TM1829) * Better interrupt handling Suggested by @h3ndrik : allocated the interrupt once at the initialization and then just turn it on and off. This is the strategy that the ESP32 core uses also. * Major refactoring Two major changes to the RMT driver. First, I realized that we can have only one interrupt handler attached to the RMT peripheral, so it needs to be able to handle all of the attached strips. To accomplish this, I store each ClocklessController in an array indexed by its RMT channel. The interrupt handler can then take the channel that triggered it and index into the array to get the right controller. The second major change is that I replaced all of the explicit bit twiddling of the RMT configurartion with calls to the proper functions in ESP32 core. That should make the code more stable if the core changes. * Fixed the interrupt dispatch Since the interrupt handler is global for all channels, we need to store not just the controller, but also the buffer refill function for each strip. * Added a demo This version of DemoReel100 spins off a separate task on core 0 that just performs the FastLED.show() operations. Regular code running on core 1 (the default for Arduino) signals this task to request a show(). * Avoid unnecessary timeouts Replaced a 500ms delay in the show task with MAX_DELAY. There's really no point in timing out (and crashing the program) just because the application hasn't called show. * Parallel output Reworked the code again in order to support parallel output, which is now the default mode. You can also now ask it to use the built-in RMT driver if you have other parts of your code that need the RMT peripheral. Two #defines control choices -- put either or both of these before including FastLED.h: #define FASTLED_RMT_CORE_DRIVER Uses the ESP core RMT driver. To do this, though, it allocates a big buffer to hold all of the pixel bits, so there is a memory and compute cost. #define FASTLED_RMT_SERIAL_OUTPUT Force serial output of each strip. * Documentation Describing the implementation and the compile-time switches * Removing files that should not be there * Fixed synchronization The previous checkin had bugs in the syncronization that caused problems in parallel mode when strips are different lengths. * Fixed a stupid bug Made the code bullet-proof in a few ways, but most importantly fixed a terrible integer underflow bug in the code that fills the RMT buffer. * Another major overhaul The big change in this version is the ability to support more than 8 controllers. Instead of assigning RMT channels to controllers in a fixed mapping, channels are assigned on the fly, allowing the driver to reuse channels as they become available. * Oops Didn't mean to check these in. * Fixed built-in driver mode Fixed the code so that it works with the built-in RMT driver. There's nothing special to do to enable it -- just #define FASTLED_RMT_BUILTIN_DRIVER true * Cleanup Fixing some documentation and configuration stuff * Rewrite of fastpin I've been needing to rewrite fastpin_esp32.h for the ESP32 ports and masks. This file also makes sure we don't use pins that won't work, even with clockless chips like the WS2812. * Got rid of tabs Which were making the code ugly. * Minor tweaks Added proper definitions for port() and toggle() to use the GPIO.out register. Changed the pin number test to avoid unnecessary conditions. * Allow TX and RX pins * Fixed pin access methods This should be the right set of definitions -- consistent with the other platforms. * Experimental Do not merge this code * Change pixel buffering The previous version of this code saved a copy of the PixelController every time show() is called. It appears that this causes massive memory fragmentation, eventually locking up the processor. This new version saves the pixel data is a separate buffer that is allocated only one time. * Some rearranging of the code Nothing major here. Added comments and put the functions is a better order. Added some defensive programming. * New I2S driver for ESP32 * Two updates: (1) avoid copying all the pixel data up front, and (2) use T1, T2, and T3 to encode thepulse patterns * Trying to get the timing better. * This version seems pretty solid * Yves' very cool changes to improve performance and accuracy * First attempt at merging the two drivers * Complete I2S implementation, with switch to choose it over the RMT * Removed the old header * This was added by accident * Changed the RMT driver so that it no longer needs to copy all the pixel data up front, which was slowing it down and using a lot of extra memory * Fixed a typo: make sure to load a different channel each time * Commented out all the Serial.print output * fillHalfRMTBuffer needs to be virtual in order to preserve the color channel order from the template parameters * Two mods: (1) convert CPU cycles directly to RMT cycles without going through nanoseconds; (2) improve performance of fill buffer by using a pointer into RMT memory rather than a bunch of indexes, and by inlining the getNextByte routine. * Minor cleanup * Cleaned up the conversion of CPU cycles to RMT cycles
-rw-r--r--platforms/esp/32/clockless_rmt_esp32.h43
1 files changed, 22 insertions, 21 deletions
diff --git a/platforms/esp/32/clockless_rmt_esp32.h b/platforms/esp/32/clockless_rmt_esp32.h
index accd6008..6368bc93 100644
--- a/platforms/esp/32/clockless_rmt_esp32.h
+++ b/platforms/esp/32/clockless_rmt_esp32.h
@@ -118,21 +118,16 @@ __attribute__ ((always_inline)) inline static uint32_t __clock_cycles() {
#define DIVIDER 2 /* 4, 8 still seem to work, but timings become marginal */
#define MAX_PULSES 32 /* A channel has a 64 "pulse" buffer - we use half per pass */
-// -- Convert ESP32 cycles back into nanoseconds
-#define ESPCLKS_TO_NS(_CLKS) (((long)(_CLKS) * 1000L) / F_CPU_MHZ)
-
-// -- Convert nanoseconds into RMT cycles
-#define F_CPU_RMT ( 80000000L)
-#define NS_PER_SEC (1000000000L)
-#define CYCLES_PER_SEC (F_CPU_RMT/DIVIDER)
-#define NS_PER_CYCLE ( NS_PER_SEC / CYCLES_PER_SEC )
-#define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE )
-
-// -- Convert ESP32 cycles to RMT cycles
-#define TO_RMT_CYCLES(_CLKS) NS_TO_CYCLES(ESPCLKS_TO_NS(_CLKS))
+// -- Convert ESP32 CPU cycles to RMT device cycles, taking into account the divider
+#define F_CPU_RMT ( 80000000L)
+#define RMT_CYCLES_PER_SEC (F_CPU_RMT/DIVIDER)
+#define RMT_CYCLES_PER_ESP_CYCLE (F_CPU / RMT_CYCLES_PER_SEC)
+#define ESP_TO_RMT_CYCLES(n) ((n) / (RMT_CYCLES_PER_ESP_CYCLE))
// -- Number of cycles to signal the strip to latch
-#define RMT_RESET_DURATION NS_TO_CYCLES(50000)
+#define NS_PER_CYCLE ( 1000000000L / RMT_CYCLES_PER_SEC )
+#define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE )
+#define RMT_RESET_DURATION NS_TO_CYCLES(50000)
// -- Core or custom driver
#ifndef FASTLED_RMT_BUILTIN_DRIVER
@@ -190,6 +185,7 @@ class ClocklessController : public CPixelLEDController<RGB_ORDER>
PixelController<RGB_ORDER> * mPixels;
int mCurColor;
uint16_t mCurPulse;
+ volatile uint32_t * mRMT_mem_ptr;
// -- Buffer to hold all of the pulses. For the version that uses
// the RMT driver built into the ESP core.
@@ -208,17 +204,17 @@ public:
// according to the timing values given in the template instantiation
// T1H
mOne.level0 = 1;
- mOne.duration0 = TO_RMT_CYCLES(T1+T2);
+ mOne.duration0 = ESP_TO_RMT_CYCLES(T1+T2); // TO_RMT_CYCLES(T1+T2);
// T1L
mOne.level1 = 0;
- mOne.duration1 = TO_RMT_CYCLES(T3);
+ mOne.duration1 = ESP_TO_RMT_CYCLES(T3); // TO_RMT_CYCLES(T3);
// T0H
mZero.level0 = 1;
- mZero.duration0 = TO_RMT_CYCLES(T1);
+ mZero.duration0 = ESP_TO_RMT_CYCLES(T1); // TO_RMT_CYCLES(T1);
// T0L
mZero.level1 = 0;
- mZero.duration1 = TO_RMT_CYCLES(T2 + T3);
+ mZero.duration1 = ESP_TO_RMT_CYCLES(T2+T3); // TO_RMT_CYCLES(T2 + T3);
gControllers[gNumControllers] = this;
gNumControllers++;
@@ -414,6 +410,7 @@ protected:
// -- Initialize the counters that keep track of where we are in
// the pixel data.
+ mRMT_mem_ptr = & (RMTMEM.chan[mRMT_channel].data32[0].val);
mCurPulse = 0;
mCurColor = 0;
@@ -494,7 +491,7 @@ protected:
}
}
- uint8_t IRAM_ATTR getNextByte()
+ uint8_t IRAM_ATTR getNextByte() __attribute__ ((always_inline))
{
uint8_t byte;
@@ -543,7 +540,8 @@ protected:
// rmt_item32_t value corresponding to the buffered bit value
for (register uint32_t j = 0; j < 8; j++) {
uint32_t val = (byteval & 0x80000000L) ? one_val : zero_val;
- RMTMEM.chan[mRMT_channel].data32[mCurPulse].val = val;
+ * mRMT_mem_ptr++ = val;
+ // Replaces: RMTMEM.chan[mRMT_channel].data32[mCurPulse].val = val;
byteval <<= 1;
mCurPulse++;
}
@@ -554,15 +552,18 @@ protected:
// RMT buffer with 0's, which signals to the device that we're done.
if ( ! mPixels->has(1) ) {
while (pulses < 32) {
- RMTMEM.chan[mRMT_channel].data32[mCurPulse].val = 0;
+ * mRMT_mem_ptr++ = 0;
+ // Replaces: RMTMEM.chan[mRMT_channel].data32[mCurPulse].val = 0;
mCurPulse++;
pulses++;
}
}
// -- When we have filled the back half the buffer, reset the position to the first half
- if (mCurPulse >= MAX_PULSES*2)
+ if (mCurPulse >= MAX_PULSES*2) {
+ mRMT_mem_ptr = & (RMTMEM.chan[mRMT_channel].data32[0].val);
mCurPulse = 0;
+ }
}
};