#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 class ClocklessController : public CPixelLEDController { typedef typename FastPin::port_ptr_t data_ptr_t; typedef typename FastPin::port_t data_t; data_t mPinMask; data_ptr_t mPort; CMinWait mWait; public: virtual void init() { FastPin::setOutput(); mPinMask = FastPin::mask(); mPort = FastPin::port(); } virtual uint16_t getMaxRefreshRate() const { return 400; } protected: virtual void showPixels(PixelController & pixels) { mWait.wait(); int cnt = FASTLED_INTERRUPT_RETRY_COUNT; while((showRGBInternal(pixels)==0) && cnt--) { #ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES ++_retry_cnt; #endif os_intr_unlock(); delayMicroseconds(WAIT_TIME); os_intr_lock(); } mWait.mark(); } #define _ESP_ADJ (0) #define _ESP_ADJ2 (0) template __attribute__ ((always_inline)) inline static bool writeBits(register uint32_t & last_mark, register uint32_t b) { b <<= 24; b = ~b; for(register uint32_t i = BITS; i > 0; --i) { while((__clock_cycles() - last_mark) < (T1+T2+T3)); last_mark = __clock_cycles(); FastPin::hi(); while((__clock_cycles() - last_mark) < T1); if(b & 0x80000000L) { FastPin::lo(); } b <<= 1; while((__clock_cycles() - last_mark) < (T1+T2)); FastPin::lo(); // even with interrupts disabled, the NMI interupt seems to cause // timing issues here. abort the frame if one bit took to long. if the // last of the 24 bits has been sent already, it is too late // this fixes the flickering first pixel that started to occur with // framework version 3.0.0 if ((__clock_cycles() - last_mark) >= (T1 + T2 + T3 - 5)) { return true; } } return false; } // 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 ICACHE_RAM_ATTR showRGBInternal(PixelController pixels) { // Setup the pixel controller and load/scale the first byte pixels.preStepFirstByteDithering(); register uint32_t b = pixels.loadAndScale0(); pixels.preStepFirstByteDithering(); uint32_t start; { struct Lock { Lock() { os_intr_lock(); } ~Lock() { os_intr_unlock(); } }; start = __clock_cycles(); uint32_t last_mark = start; while(pixels.has(1)) { // Write first byte, read next byte if (writeBits<8+XTRA0>(last_mark, b)) { return 0; } b = pixels.loadAndScale1(); // Write second byte, read 3rd byte if (writeBits<8+XTRA0>(last_mark, b)) { return 0; } b = pixels.loadAndScale2(); // Write third byte, read 1st byte of next pixel if (writeBits<8+XTRA0>(last_mark, b)) { return 0; } #if (FASTLED_ALLOW_INTERRUPTS == 1) os_intr_unlock(); #endif b = pixels.advanceAndLoadAndScale0(); pixels.stepDithering(); #if (FASTLED_ALLOW_INTERRUPTS == 1) os_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))) { return 0; } } #endif }; } #ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES ++_frame_cnt; #endif return __clock_cycles() - start; } }; FASTLED_NAMESPACE_END