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:
Diffstat (limited to 'src/platforms/esp/32/clockless_rmt_esp32.h')
-rw-r--r--src/platforms/esp/32/clockless_rmt_esp32.h397
1 files changed, 397 insertions, 0 deletions
diff --git a/src/platforms/esp/32/clockless_rmt_esp32.h b/src/platforms/esp/32/clockless_rmt_esp32.h
new file mode 100644
index 00000000..8f5690bb
--- /dev/null
+++ b/src/platforms/esp/32/clockless_rmt_esp32.h
@@ -0,0 +1,397 @@
+/*
+ * Integration into FastLED ClocklessController
+ * Copyright (c) 2018,2019,2020 Samuel Z. Guyer
+ * Copyright (c) 2017 Thomas Basler
+ * Copyright (c) 2017 Martin F. Falatic
+ *
+ * ESP32 support is provided using the RMT peripheral device -- a unit
+ * on the chip designed specifically for generating (and receiving)
+ * precisely-timed digital signals. Nominally for use in infrared
+ * remote controls, we use it to generate the signals for clockless
+ * LED strips. The main advantage of using the RMT device is that,
+ * once programmed, it generates the signal asynchronously, allowing
+ * the CPU to continue executing other code. It is also not vulnerable
+ * to interrupts or other timing problems that could disrupt the signal.
+ *
+ * The implementation strategy is borrowed from previous work and from
+ * the RMT support built into the ESP32 IDF. The RMT device has 8
+ * channels, which can be programmed independently to send sequences
+ * of high/low bits. Memory for each channel is limited, however, so
+ * in order to send a long sequence of bits, we need to continuously
+ * refill the buffer until all the data is sent. To do this, we fill
+ * half the buffer and then set an interrupt to go off when that half
+ * is sent. Then we refill that half while the second half is being
+ * sent. This strategy effectively overlaps computation (by the CPU)
+ * and communication (by the RMT).
+ *
+ * Since the RMT device only has 8 channels, we need a strategy to
+ * allow more than 8 LED controllers. Our driver assigns controllers
+ * to channels on the fly, queuing up controllers as necessary until a
+ * channel is free. The main showPixels routine just fires off the
+ * first 8 controllers; the interrupt handler starts new controllers
+ * asynchronously as previous ones finish. So, for example, it can
+ * send the data for 8 controllers simultaneously, but 16 controllers
+ * would take approximately twice as much time.
+ *
+ * There is a #define that allows a program to control the total
+ * number of channels that the driver is allowed to use. It defaults
+ * to 8 -- use all the channels. Setting it to 1, for example, results
+ * in fully serial output:
+ *
+ * #define FASTLED_RMT_MAX_CHANNELS 1
+ *
+ * OTHER RMT APPLICATIONS
+ *
+ * The default FastLED driver takes over control of the RMT interrupt
+ * handler, making it hard to use the RMT device for other
+ * (non-FastLED) purposes. You can change it's behavior to use the ESP
+ * core driver instead, allowing other RMT applications to
+ * co-exist. To switch to this mode, add the following directive
+ * before you include FastLED.h:
+ *
+ * #define FASTLED_RMT_BUILTIN_DRIVER 1
+ *
+ * There may be a performance penalty for using this mode. We need to
+ * compute the RMT signal for the entire LED strip ahead of time,
+ * rather than overlapping it with communication. We also need a large
+ * buffer to hold the signal specification. Each bit of pixel data is
+ * represented by a 32-bit pulse specification, so it is a 32X blow-up
+ * in memory use.
+ *
+ * NEW: Use of Flash memory on the ESP32 can interfere with the timing
+ * of pixel output. The ESP-IDF system code disables all other
+ * code running on *either* core during these operation. To prevent
+ * this from happening, define this flag. It will force flash
+ * operations to wait until the show() is done.
+ *
+ * #define FASTLED_ESP32_FLASH_LOCK 1
+ *
+ * NEW (June 2020): The RMT controller has been split into two
+ * classes: ClocklessController, which is an instantiation of the
+ * FastLED CPixelLEDController template, and ESP32RMTController,
+ * which just handles driving the RMT peripheral. One benefit of
+ * this design is that ESP32RMTContoller is not a template, so
+ * its methods can be marked with the IRAM_ATTR and end up in
+ * IRAM memory. Another benefit is that all of the color channel
+ * processing is done up-front, in the templated class, so we
+ * can fill the RMT buffers more quickly.
+ *
+ * IN THEORY, this design would also allow FastLED.show() to
+ * send the data while the program continues to prepare the next
+ * frame of data.
+ *
+ * Based on public domain code created 19 Nov 2016 by Chris Osborn <fozztexx@fozztexx.com>
+ * http://insentricity.com *
+ *
+ */
+/*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#pragma once
+
+FASTLED_NAMESPACE_BEGIN
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "esp32-hal.h"
+// ESP_IDF_VERSION_MAJOR is defined in ESP-IDF v3.3 or later
+#if defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR > 3
+#include "esp_intr_alloc.h"
+#else
+#include "esp_intr.h"
+#endif
+#include "driver/gpio.h"
+#include "driver/rmt.h"
+#include "driver/periph_ctrl.h"
+#include "freertos/semphr.h"
+#include "soc/rmt_struct.h"
+
+#include "esp_log.h"
+
+extern void spi_flash_op_lock(void);
+extern void spi_flash_op_unlock(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+__attribute__ ((always_inline)) inline static uint32_t __clock_cycles() {
+ uint32_t cyc;
+ __asm__ __volatile__ ("rsr %0,ccount":"=a" (cyc));
+ return cyc;
+}
+
+#define FASTLED_HAS_CLOCKLESS 1
+#define NUM_COLOR_CHANNELS 3
+
+// NOT CURRENTLY IMPLEMENTED:
+// -- Set to true to print debugging information about timing
+// Useful for finding out if timing is being messed up by other things
+// on the processor (WiFi, for example)
+//#ifndef FASTLED_RMT_SHOW_TIMER
+//#define FASTLED_RMT_SHOW_TIMER false
+//#endif
+
+// -- Configuration constants
+#define DIVIDER 2 /* 4, 8 still seem to work, but timings become marginal */
+
+// -- RMT memory configuration
+// By default we use two memory blocks for each RMT channel instead of 1. The
+// reason is that one memory block is only 64 bits, which causes the refill
+// interrupt to fire too often. When combined with WiFi, this leads to conflicts
+// between interrupts and weird flashy effects on the LEDs. Special thanks to
+// Brian Bulkowski for finding this problem and developing a fix.
+#ifndef FASTLED_RMT_MEM_BLOCKS
+#define FASTLED_RMT_MEM_BLOCKS 2
+#endif
+
+#define MAX_PULSES (64 * FASTLED_RMT_MEM_BLOCKS) /* One block has a 64 "pulse" buffer */
+#define PULSES_PER_FILL (MAX_PULSES / 2) /* Half of the channel buffer */
+
+// -- 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 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
+#define FASTLED_RMT_BUILTIN_DRIVER false
+#endif
+
+// -- Max number of controllers we can support
+#ifndef FASTLED_RMT_MAX_CONTROLLERS
+#define FASTLED_RMT_MAX_CONTROLLERS 32
+#endif
+
+// -- Max RMT channel (default to 8 on ESP32 and 4 on ESP32-S2)
+#ifndef FASTLED_RMT_MAX_CHANNELS
+#ifdef CONFIG_IDF_TARGET_ESP32S2
+#define FASTLED_RMT_MAX_CHANNELS 4
+#else
+#define FASTLED_RMT_MAX_CHANNELS 8
+#endif
+#endif
+
+class ESP32RMTController
+{
+private:
+
+ // -- RMT has 8 channels, numbered 0 to 7
+ rmt_channel_t mRMT_channel;
+
+ // -- Store the GPIO pin
+ gpio_num_t mPin;
+
+ // -- Timing values for zero and one bits, derived from T1, T2, and T3
+ rmt_item32_t mZero;
+ rmt_item32_t mOne;
+
+ // -- Total expected time to send 32 bits
+ // Each strip should get an interrupt roughly at this interval
+ uint32_t mCyclesPerFill;
+ uint32_t mMaxCyclesPerFill;
+ uint32_t mLastFill;
+
+ // -- Pixel data
+ uint8_t * mPixelData;
+ int mSize;
+ int mCur;
+ int mBufSize;
+
+ // -- 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
+ // the RMT driver built into the ESP core.
+ rmt_item32_t * mBuffer;
+ uint16_t mBufferSize; // bytes
+ int mCurPulse;
+
+ // -- These values need to be real variables, so we can access them
+ // in the cpp file
+ static int gMaxChannel;
+ static int gMemBlocks;
+
+public:
+
+ // -- Constructor
+ // Mainly just stores the template parameters from the LEDController as
+ // member variables.
+ ESP32RMTController(int DATA_PIN, int T1, int T2, int T3, int maxChannel, int memBlocks);
+
+ // -- Get max cycles per fill
+ uint32_t IRAM_ATTR getMaxCyclesPerFill() const { return mMaxCyclesPerFill; }
+
+ // -- Get or create the pixel data buffer
+ uint8_t * getPixelBuffer(int size_in_bytes);
+
+ // -- Initialize RMT subsystem
+ // This only needs to be done once. The particular pin is not important,
+ // because we need to configure the RMT channels on the fly.
+ static void init(gpio_num_t pin);
+
+ // -- Show this string of pixels
+ // This is the main entry point for the pixel controller
+ void IRAM_ATTR showPixels();
+
+ // -- Start up the next controller
+ // This method is static so that it can dispatch to the
+ // appropriate startOnChannel method of the given controller.
+ static void IRAM_ATTR startNext(int channel);
+
+ // -- Start this controller on the given channel
+ // This function just initiates the RMT write; it does not wait
+ // for it to finish.
+ void IRAM_ATTR startOnChannel(int channel);
+
+ // -- Start RMT transmission
+ // Setting this RMT flag is what actually kicks off the peripheral
+ void IRAM_ATTR tx_start();
+
+ // -- A controller is done
+ // This function is called when a controller finishes writing
+ // its data. It is called either by the custom interrupt
+ // handler (below), or as a callback from the built-in
+ // interrupt handler. It is static because we don't know which
+ // controller is done until we look it up.
+ static void IRAM_ATTR doneOnChannel(rmt_channel_t channel, void * arg);
+
+ // -- Custom interrupt handler
+ // This interrupt handler handles two cases: a controller is
+ // done writing its data, or a controller needs to fill the
+ // next half of the RMT buffer with data.
+ static void IRAM_ATTR interruptHandler(void *arg);
+
+ // -- Fill RMT buffer
+ // Puts 32 bits of pixel data into the next 32 slots in the RMT memory
+ // Each data bit is represented by a 32-bit RMT item that specifies how
+ // long to hold the signal high, followed by how long to hold it low.
+ // NOTE: Now the default is to use 128-bit buffers, so half a buffer is
+ // is 64 bits. See FASTLED_RMT_MEM_BLOCKS
+ void IRAM_ATTR fillNext(bool check_time);
+
+ // -- Init pulse buffer
+ // Set up the buffer that will hold all of the pulse items for this
+ // controller.
+ // This function is only used when the built-in RMT driver is chosen
+ void initPulseBuffer(int size_in_bytes);
+
+ // -- Convert a byte into RMT pulses
+ // This function is only used when the built-in RMT driver is chosen
+ void convertByte(uint32_t byteval);
+};
+
+template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 5>
+class ClocklessController : public CPixelLEDController<RGB_ORDER>
+{
+private:
+
+ // -- The actual controller object for ESP32
+ ESP32RMTController mRMTController;
+
+ // -- This instantiation forces a check on the pin choice
+ FastPin<DATA_PIN> mFastPin;
+
+public:
+
+ ClocklessController()
+ : mRMTController(DATA_PIN, T1, T2, T3, FASTLED_RMT_MAX_CHANNELS, FASTLED_RMT_MEM_BLOCKS)
+ {}
+
+ void init()
+ {
+ }
+
+ virtual uint16_t getMaxRefreshRate() const { return 400; }
+
+protected:
+
+ // -- Load pixel data
+ // 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_in_bytes = pixels.size() * 3;
+ uint8_t * pData = mRMTController.getPixelBuffer(size_in_bytes);
+
+ // -- This might be faster
+ while (pixels.has(1)) {
+ *pData++ = pixels.loadAndScale0();
+ *pData++ = pixels.loadAndScale1();
+ *pData++ = pixels.loadAndScale2();
+ pixels.advanceData();
+ pixels.stepDithering();
+ }
+ }
+
+ // -- Show pixels
+ // This is the main entry point for the controller.
+ virtual void showPixels(PixelController<RGB_ORDER> & pixels)
+ {
+ if (FASTLED_RMT_BUILTIN_DRIVER) {
+ convertAllPixelData(pixels);
+ } else {
+ loadPixelData(pixels);
+ }
+
+ mRMTController.showPixels();
+ }
+
+ // -- Convert all pixels to RMT pulses
+ // This function is only used when the user chooses to use the
+ // built-in RMT driver, which needs all of the RMT pulses
+ // up-front.
+ void convertAllPixelData(PixelController<RGB_ORDER> & pixels)
+ {
+ // -- Make sure the data buffer is allocated
+ mRMTController.initPulseBuffer(pixels.size() * 3);
+
+ // -- Cycle through the R,G, and B values in the right order,
+ // storing the pulses in the big buffer
+
+ uint32_t byteval;
+ while (pixels.has(1)) {
+ byteval = pixels.loadAndScale0();
+ mRMTController.convertByte(byteval);
+ byteval = pixels.loadAndScale1();
+ mRMTController.convertByte(byteval);
+ byteval = pixels.loadAndScale2();
+ mRMTController.convertByte(byteval);
+ pixels.advanceData();
+ pixels.stepDithering();
+ }
+ }
+};
+
+
+FASTLED_NAMESPACE_END