diff options
author | Sam Guyer <sam.guyer@gmail.com> | 2017-08-16 23:24:18 +0300 |
---|---|---|
committer | Daniel Garcia <danielgarcia@gmail.com> | 2017-08-16 23:24:18 +0300 |
commit | 8e691e1ccea314ad745b36396a208252086166ec (patch) | |
tree | 4acec41bb4be35ba65a884efe56cf7b8f2ebac74 | |
parent | 507ed8c6a5a7e43b0ce5e00e5bdb60204464e997 (diff) |
Support for ESP32 (#474)
Credit to Rina Shkrabova for the first cut.
-rw-r--r-- | fastled_delay.h | 7 | ||||
-rw-r--r-- | led_sysdefs.h | 2 | ||||
-rw-r--r-- | platforms.h | 2 | ||||
-rw-r--r-- | platforms/esp/32/clockless_block_esp32.h | 168 | ||||
-rw-r--r-- | platforms/esp/32/clockless_esp32.h | 125 | ||||
-rw-r--r-- | platforms/esp/32/fastled_esp32.h | 7 | ||||
-rw-r--r-- | platforms/esp/32/fastpin_esp32.h | 92 | ||||
-rw-r--r-- | platforms/esp/32/led_sysdefs_esp32.h | 33 |
8 files changed, 436 insertions, 0 deletions
diff --git a/fastled_delay.h b/fastled_delay.h index f16d322e..cfc7882f 100644 --- a/fastled_delay.h +++ b/fastled_delay.h @@ -33,6 +33,13 @@ public: //////////////////////////////////////////////////////////////////////////////////////////// // Default is now just 'nop', with special case for AVR + +// ESP32 core has it's own definition of NOP, so undef it first +#ifdef ESP32 +#undef NOP +#undef NOP2 +#endif + #if defined(__AVR__) # define NOP __asm__ __volatile__ ("cp r0,r0\n"); # define NOP2 __asm__ __volatile__ ("rjmp .+0"); diff --git a/led_sysdefs.h b/led_sysdefs.h index 57faad2f..93d878ac 100644 --- a/led_sysdefs.h +++ b/led_sysdefs.h @@ -25,6 +25,8 @@ #include "platforms/arm/d21/led_sysdefs_arm_d21.h" #elif defined(ESP8266) #include "platforms/esp/8266/led_sysdefs_esp8266.h" +#elif defined(ESP32) +#include "platforms/esp/32/led_sysdefs_esp32.h" #else // AVR platforms #include "platforms/avr/led_sysdefs_avr.h" diff --git a/platforms.h b/platforms.h index 7216de7c..9fb4fcb9 100644 --- a/platforms.h +++ b/platforms.h @@ -25,6 +25,8 @@ #include "platforms/arm/d21/fastled_arm_d21.h" #elif defined(ESP8266) #include "platforms/esp/8266/fastled_esp8266.h" +#elif defined(ESP32) +#include "platforms/esp/32/fastled_esp32.h" #else // AVR platforms #include "platforms/avr/fastled_avr.h" diff --git a/platforms/esp/32/clockless_block_esp32.h b/platforms/esp/32/clockless_block_esp32.h new file mode 100644 index 00000000..8ab5807a --- /dev/null +++ b/platforms/esp/32/clockless_block_esp32.h @@ -0,0 +1,168 @@ +#ifndef __INC_CLOCKLESS_BLOCK_ESP8266_H +#define __INC_CLOCKLESS_BLOCK_ESP8266_H + +#define FASTLED_HAS_BLOCKLESS 1 + +#define PORT_MASK (((1<<LANES)-1) & 0x0000FFFFL) +#define MIN(X,Y) (((X)<(Y)) ? (X):(Y)) +#define USED_LANES (MIN(LANES,4)) +#define REAL_FIRST_PIN 12 +#define LAST_PIN (12 + USED_LANES - 1) + +FASTLED_NAMESPACE_BEGIN + +#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES +extern uint32_t _frame_cnt; +extern uint32_t _retry_cnt; +#endif + +template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 5> +class InlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PORT_MASK> { + typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t; + typedef typename FastPin<FIRST_PIN>::port_t data_t; + + data_t mPinMask; + data_ptr_t mPort; + CMinWait<WAIT_TIME> mWait; +public: + virtual int size() { return CLEDController::size() * LANES; } + + virtual void showPixels(PixelController<RGB_ORDER, LANES, PORT_MASK> & pixels) { + // mWait.wait(); + /*uint32_t clocks = */ + int cnt=FASTLED_INTERRUPT_RETRY_COUNT; + while(!showRGBInternal(pixels) && cnt--) { + ets_intr_unlock(); +#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES + _retry_cnt++; +#endif + delayMicroseconds(WAIT_TIME * 10); + ets_intr_lock(); + } + // #if FASTLED_ALLOW_INTTERUPTS == 0 + // Adjust the timer + // long microsTaken = CLKS_TO_MICROS(clocks); + // MS_COUNTER += (1 + (microsTaken / 1000)); + // #endif + + // mWait.mark(); + } + + template<int PIN> static void initPin() { + if(PIN >= REAL_FIRST_PIN && PIN <= LAST_PIN) { + _ESPPIN<PIN, 1<<(PIN & 0xFF)>::setOutput(); + // FastPin<PIN>::setOutput(); + } + } + + virtual void init() { + // Only supportd on pins 12-15 + // SZG: This probably won't work (check pins definitions in fastpin_esp32) + initPin<12>(); + initPin<13>(); + initPin<14>(); + initPin<15>(); + mPinMask = FastPin<FIRST_PIN>::mask(); + mPort = FastPin<FIRST_PIN>::port(); + + // Serial.print("Mask is "); Serial.println(PORT_MASK); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + + typedef union { + uint8_t bytes[8]; + uint16_t shorts[4]; + uint32_t raw[2]; + } Lines; + +#define ESP_ADJUST 0 // (2*(F_CPU/24000000)) +#define ESP_ADJUST2 0 + template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & last_mark, register Lines & b, PixelController<RGB_ORDER, LANES, PORT_MASK> &pixels) { // , register uint32_t & b2) { + Lines b2 = b; + transpose8x1_noinline(b.bytes,b2.bytes); + + register uint8_t d = pixels.template getd<PX>(pixels); + register uint8_t scale = pixels.template getscale<PX>(pixels); + + for(register uint32_t i = 0; i < USED_LANES; i++) { + while((__clock_cycles() - last_mark) < (T1+T2+T3)); + last_mark = __clock_cycles(); + *FastPin<FIRST_PIN>::sport() = PORT_MASK << REAL_FIRST_PIN; + + uint32_t nword = ((uint32_t)(~b2.bytes[7-i]) & PORT_MASK) << REAL_FIRST_PIN; + while((__clock_cycles() - last_mark) < (T1-6)); + *FastPin<FIRST_PIN>::cport() = nword; + + while((__clock_cycles() - last_mark) < (T1+T2)); + *FastPin<FIRST_PIN>::cport() = PORT_MASK << REAL_FIRST_PIN; + + b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale); + } + + for(register uint32_t i = USED_LANES; i < 8; i++) { + while((__clock_cycles() - last_mark) < (T1+T2+T3)); + last_mark = __clock_cycles(); + *FastPin<FIRST_PIN>::sport() = PORT_MASK << REAL_FIRST_PIN; + + uint32_t nword = ((uint32_t)(~b2.bytes[7-i]) & PORT_MASK) << REAL_FIRST_PIN; + while((__clock_cycles() - last_mark) < (T1-6)); + *FastPin<FIRST_PIN>::cport() = nword; + + while((__clock_cycles() - last_mark) < (T1+T2)); + *FastPin<FIRST_PIN>::cport() = PORT_MASK << REAL_FIRST_PIN; + } + } + + // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then + // gcc will use register Y for the this pointer. + static uint32_t showRGBInternal(PixelController<RGB_ORDER, LANES, PORT_MASK> &allpixels) { + + // Setup the pixel controller and load/scale the first byte + Lines b0; + + for(int i = 0; i < USED_LANES; i++) { + b0.bytes[i] = allpixels.loadAndScale0(i); + } + allpixels.preStepFirstByteDithering(); + + ets_intr_lock(); + uint32_t _start = __clock_cycles(); + uint32_t last_mark = _start; + + while(allpixels.has(1)) { + // Write first byte, read next byte + writeBits<8+XTRA0,1>(last_mark, b0, allpixels); + + // Write second byte, read 3rd byte + writeBits<8+XTRA0,2>(last_mark, b0, allpixels); + allpixels.advanceData(); + + // Write third byte + writeBits<8+XTRA0,0>(last_mark, b0, allpixels); + +#if (FASTLED_ALLOW_INTERRUPTS == 1) + ets_intr_unlock(); +#endif + + allpixels.stepDithering(); + +#if (FASTLED_ALLOW_INTERRUPTS == 1) + ets_intr_lock(); + // if interrupts took longer than 45µs, punt on the current frame + if((int32_t)(__clock_cycles()-last_mark) > 0) { + if((int32_t)(__clock_cycles()-last_mark) > (T1+T2+T3+((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US))) { ets_intr_unlock(); return 0; } + } +#endif + }; + + ets_intr_unlock(); +#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES + _frame_cnt++; +#endif + return __clock_cycles() - _start; + } +}; + +FASTLED_NAMESPACE_END +#endif diff --git a/platforms/esp/32/clockless_esp32.h b/platforms/esp/32/clockless_esp32.h new file mode 100644 index 00000000..0ed9224b --- /dev/null +++ b/platforms/esp/32/clockless_esp32.h @@ -0,0 +1,125 @@ +#pragma once + +FASTLED_NAMESPACE_BEGIN + +#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES +extern uint32_t _frame_cnt; +extern uint32_t _retry_cnt; +#endif + +// Info on reading cycle counter from https://github.com/kbeckmann/nodemcu-firmware/blob/ws2812-dual/app/modules/ws2812.c +__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 + +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> { + + typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t; + typedef typename FastPin<DATA_PIN>::port_t data_t; + + data_t mPinMask; + data_ptr_t mPort; + CMinWait<WAIT_TIME> mWait; +public: + virtual void init() { + FastPin<DATA_PIN>::setOutput(); + mPinMask = FastPin<DATA_PIN>::mask(); + mPort = FastPin<DATA_PIN>::port(); + } + + virtual uint16_t getMaxRefreshRate() const { return 400; } + +protected: + + virtual void showPixels(PixelController<RGB_ORDER> & pixels) { + mWait.wait(); + int cnt = FASTLED_INTERRUPT_RETRY_COUNT; + while((showRGBInternal(pixels)==0) && cnt--) { +#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES + _retry_cnt++; +#endif + ets_intr_unlock(); + // interrupts(); + delayMicroseconds(WAIT_TIME); + ets_intr_lock(); + // noInterrupts(); + } + // ets_intr_unlock(); + mWait.mark(); + } + +#define _ESP_ADJ (0) +#define _ESP_ADJ2 (0) + + template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & last_mark, register uint32_t b) { + b = ~b; b <<= 24; + for(register uint32_t i = BITS; i > 0; i--) { + while((__clock_cycles() - last_mark) < (T1+T2+T3)); + last_mark = __clock_cycles(); + FastPin<DATA_PIN>::hi(); + + while((__clock_cycles() - last_mark) < T1); + if(b & 0x80000000L) { FastPin<DATA_PIN>::lo(); } + b <<= 1; + + while((__clock_cycles() - last_mark) < (T1+T2)); + FastPin<DATA_PIN>::lo(); + } + } + + // This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then + // gcc will use register Y for the this pointer. + static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) { + // Setup the pixel controller and load/scale the first byte + pixels.preStepFirstByteDithering(); + register uint32_t b = pixels.loadAndScale0(); + pixels.preStepFirstByteDithering(); + ets_intr_lock(); + // noInterrupts(); + uint32_t start = __clock_cycles(); + uint32_t last_mark = start; + while(pixels.has(1)) { + // Write first byte, read next byte + writeBits<8+XTRA0>(last_mark, b); + b = pixels.loadAndScale1(); + + // Write second byte, read 3rd byte + writeBits<8+XTRA0>(last_mark, b); + b = pixels.loadAndScale2(); + + // Write third byte, read 1st byte of next pixel + writeBits<8+XTRA0>(last_mark, b); + b = pixels.advanceAndLoadAndScale0(); + +#if (FASTLED_ALLOW_INTERRUPTS == 1) + ets_intr_unlock(); + // interrupts(); +#endif + + pixels.stepDithering(); + +#if (FASTLED_ALLOW_INTERRUPTS == 1) + ets_intr_lock(); + // noInterrupts(); + // if interrupts took longer than 45µs, punt on the current frame + if((int32_t)(__clock_cycles()-last_mark) > 0) { + if((int32_t)(__clock_cycles()-last_mark) > (T1+T2+T3+((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US))) { sei(); return 0; } + } +#endif + }; + + ets_intr_unlock(); + // interrupts(); +#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES + _frame_cnt++; +#endif + return __clock_cycles() - start; + } +}; + +FASTLED_NAMESPACE_END diff --git a/platforms/esp/32/fastled_esp32.h b/platforms/esp/32/fastled_esp32.h new file mode 100644 index 00000000..2dcbe2df --- /dev/null +++ b/platforms/esp/32/fastled_esp32.h @@ -0,0 +1,7 @@ +#pragma once + +#include "bitswap.h" +#include "fastled_delay.h" +#include "fastpin_esp32.h" +#include "clockless_esp32.h" +// #include "clockless_block_esp32.h" diff --git a/platforms/esp/32/fastpin_esp32.h b/platforms/esp/32/fastpin_esp32.h new file mode 100644 index 00000000..9aa4ebe7 --- /dev/null +++ b/platforms/esp/32/fastpin_esp32.h @@ -0,0 +1,92 @@ +#pragma once + +FASTLED_NAMESPACE_BEGIN + +struct FASTLED_ESP_IO { + volatile uint32_t _GPO; + volatile uint32_t _GPOS; + volatile uint32_t _GPOC; +}; + +#define _GPB0 (*(FASTLED_ESP_IO*)(GPIO_OUT_REG)) +// #define _GPB0 (*(FASTLED_ESP_IO*)(DR_REG_GPIO_BASE)) +// #define _GPB1 (*(FASTLED_ESP_IO*)(0x3ff44010)) +//THERE'S a second register for pins 32-39 (33 for outputs) but let's get one working first +#define OUTPUT_PIN_LIMIT 31 + + +template<uint8_t PIN, uint32_t MASK> class _ESPPIN { + +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + inline static void setOutput() { pinMode(PIN, OUTPUT); } + inline static void setInput() { pinMode(PIN, INPUT); } + + inline static void hi() __attribute__ ((always_inline)) { if(PIN < OUTPUT_PIN_LIMIT) { _GPB0._GPOS = MASK; } } + // inline static void hi() __attribute__ ((always_inline)) { gpio_set_level((gpio_num_t)PIN, HIGH); } + + inline static void lo() __attribute__ ((always_inline)) { if (PIN < OUTPUT_PIN_LIMIT){ _GPB0._GPOC = MASK; } } + // inline static void lo() __attribute__ ((always_inline)) { gpio_set_level((gpio_num_t)PIN, LOW); } + inline static void set(register port_t val) __attribute__ ((always_inline)) { if (PIN < OUTPUT_PIN_LIMIT){ _GPB0._GPO = val; }} + // inline static void set(register port_t val) __attribute__ ((always_inline)) { gpio_set_level((gpio_num_t)PIN, val); } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { if (PIN < OUTPUT_PIN_LIMIT){ _GPB0._GPO = MASK; } } + + inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { if (PIN<OUTPUT_PIN_LIMIT) { return GPIO_OUT_REG | MASK; }} + inline static port_t loval() __attribute__ ((always_inline)) { if (PIN<OUTPUT_PIN_LIMIT) { return GPIO_OUT_REG & ~MASK; }} + inline static port_ptr_t port() __attribute__ ((always_inline)) { if(PIN<OUTPUT_PIN_LIMIT) { return &_GPB0._GPO; }} + inline static port_ptr_t sport() __attribute__ ((always_inline)) { if (PIN<OUTPUT_PIN_LIMIT) {return &_GPB0._GPOS; }} + inline static port_ptr_t cport() __attribute__ ((always_inline)) { if (PIN<OUTPUT_PIN_LIMIT) {return &_GPB0._GPOC; }} + inline static port_t mask() __attribute__ ((always_inline)) { return MASK; } + + inline static bool isset() __attribute__ ((always_inline)) { return (0x004 & MASK); } +}; + +#define _DEFPIN_ESP32(PIN, REAL_PIN) template<> class FastPin<PIN> : public _ESPPIN<REAL_PIN, (1<<(REAL_PIN & 0xFF))> {}; + + +#ifdef FASTLED_ESP32_RAW_PIN_ORDER + +_DEFPIN_ESP32(0,0); _DEFPIN_ESP32(1,1); _DEFPIN_ESP32(2,2); +_DEFPIN_ESP32(3,3); _DEFPIN_ESP32(4,4); _DEFPIN_ESP32(5,5); + +// -- These are not safe to use: +// _DEFPIN_ESP32(6,6); _DEFPIN_ESP32(7,7); _DEFPIN_ESP32(8,8); +// _DEFPIN_ESP32(9,9); _DEFPIN_ESP32(10,10); _DEFPIN_ESP32(11,11); + +_DEFPIN_ESP32(12,12); _DEFPIN_ESP32(13,13); +_DEFPIN_ESP32(14,14); _DEFPIN_ESP32(15,15); _DEFPIN_ESP32(16,16); +_DEFPIN_ESP32(17,17); _DEFPIN_ESP32(18,18); _DEFPIN_ESP32(19,19); + +// No pin 20 : _DEFPIN_ESP32(20,20); + +_DEFPIN_ESP32(21,21); _DEFPIN_ESP32(22,22); _DEFPIN_ESP32(23,23); + +// No pin 24 : _DEFPIN_ESP32(24,24); + +_DEFPIN_ESP32(25,25); _DEFPIN_ESP32(26,26); _DEFPIN_ESP32(27,27); + +// No pin 28-31: _DEFPIN_ESP32(28,28); _DEFPIN_ESP32(29,29); _DEFPIN_ESP32(30,30); _DEFPIN_ESP32(31,31); + +// Need special handling for pins > 31 +// _DEFPIN_ESP32(32,32); _DEFPIN_ESP32(33,33); + +#define PORTA_FIRST_PIN 32 +// The rest of the pins - these are generally not available +// _DEFPIN_ESP32(11,6); +// _DEFPIN_ESP32(12,7); _DEFPIN_ESP32(13,8); _DEFPIN_ESP32(14,9); _DEFPIN_ESP32(15,10); +// _DEFPIN_ESP32(16,11); + +#endif + +#define HAS_HARDWARE_PIN_SUPPORT + +#define FASTLED_NAMESPACE_END diff --git a/platforms/esp/32/led_sysdefs_esp32.h b/platforms/esp/32/led_sysdefs_esp32.h new file mode 100644 index 00000000..68e78239 --- /dev/null +++ b/platforms/esp/32/led_sysdefs_esp32.h @@ -0,0 +1,33 @@ +#pragma once + +#ifndef ESP32 +#define ESP32 +#endif + +#define FASTLED_ESP32 + +// Use system millis timer +#define FASTLED_HAS_MILLIS + +typedef volatile uint32_t RoReg; +typedef volatile uint32_t RwReg; +typedef unsigned long prog_uint32_t; +typedef bool boolean; + +// Default to NOT using PROGMEM here +#ifndef FASTLED_USE_PROGMEM +# define FASTLED_USE_PROGMEM 0 +#endif + +#ifndef FASTLED_ALLOW_INTERRUPTS +# define FASTLED_ALLOW_INTERRUPTS 1 +# define INTERRUPT_THRESHOLD 0 +#endif + +#define NEED_CXX_BITS + +// These can be overridden +# define FASTLED_ESP32_RAW_PIN_ORDER + +// #define cli() os_intr_lock(); +// #define sei() os_intr_lock(); |