#ifndef __INC_BLOCK_CLOCKLESS_ARM_K20_H #define __INC_BLOCK_CLOCKLESS_ARM_K20_H // Definition for a single channel clockless controller for the k20 family of chips, like that used in the teensy 3.0/3.1 // See clockless.h for detailed info on how the template parameters are used. #if defined(FASTLED_TEENSY3) #define FASTLED_HAS_BLOCKLESS 1 #define PORTC_FIRST_PIN 15 #define PORTD_FIRST_PIN 2 #define HAS_PORTDC 1 #define PORT_MASK (((1< FASTLED_NAMESPACE_BEGIN template class InlineBlockClocklessController : 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 int size() { return CLEDController::size() * LANES; } virtual void showPixels(PixelController & pixels) { mWait.wait(); uint32_t clocks = showRGBInternal(pixels); #if FASTLED_ALLOW_INTERRUPTS == 0 // Adjust the timer long microsTaken = CLKS_TO_MICROS(clocks); MS_COUNTER += (1 + (microsTaken / 1000)); #endif mWait.mark(); } virtual void init() { if(FIRST_PIN == PORTC_FIRST_PIN) { // PORTC switch(USED_LANES) { case 12: FastPin<30>::setOutput(); case 11: FastPin<29>::setOutput(); case 10: FastPin<27>::setOutput(); case 9: FastPin<28>::setOutput(); case 8: FastPin<12>::setOutput(); case 7: FastPin<11>::setOutput(); case 6: FastPin<13>::setOutput(); case 5: FastPin<10>::setOutput(); case 4: FastPin<9>::setOutput(); case 3: FastPin<23>::setOutput(); case 2: FastPin<22>::setOutput(); case 1: FastPin<15>::setOutput(); } } else if(FIRST_PIN == PORTD_FIRST_PIN) { // PORTD switch(USED_LANES) { case 8: FastPin<5>::setOutput(); case 7: FastPin<21>::setOutput(); case 6: FastPin<20>::setOutput(); case 5: FastPin<6>::setOutput(); case 4: FastPin<8>::setOutput(); case 3: FastPin<7>::setOutput(); case 2: FastPin<14>::setOutput(); case 1: FastPin<2>::setOutput(); } } mPinMask = FastPin::mask(); mPort = FastPin::port(); } virtual uint16_t getMaxRefreshRate() const { return 400; } typedef union { uint8_t bytes[12]; uint16_t shorts[6]; uint32_t raw[3]; } Lines; template __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, PixelController &pixels) { // , register uint32_t & b2) { register Lines b2; if(USED_LANES>8) { transpose8<1,2>(b.bytes,b2.bytes); transpose8<1,2>(b.bytes+8,b2.bytes+1); } else { transpose8x1(b.bytes,b2.bytes); } register uint8_t d = pixels.template getd(pixels); register uint8_t scale = pixels.template getscale(pixels); for(register uint32_t i = 0; i < (USED_LANES/2); i++) { while(ARM_DWT_CYCCNT < next_mark); next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3; *FastPin::sport() = PORT_MASK; while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000)))); if(USED_LANES>8) { *FastPin::cport() = ((~b2.shorts[i]) & PORT_MASK); } else { *FastPin::cport() = ((~b2.bytes[7-i]) & PORT_MASK); } while((next_mark - ARM_DWT_CYCCNT) > (T3)); *FastPin::cport() = PORT_MASK; b.bytes[i] = pixels.template loadAndScale(pixels,i,d,scale); b.bytes[i+(USED_LANES/2)] = pixels.template loadAndScale(pixels,i+(USED_LANES/2),d,scale); } // if folks use an odd numnber of lanes, get the last byte's value here if(USED_LANES & 0x01) { b.bytes[USED_LANES-1] = pixels.template loadAndScale(pixels,USED_LANES-1,d,scale); } for(register uint32_t i = USED_LANES/2; i < 8; i++) { while(ARM_DWT_CYCCNT < next_mark); next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3; *FastPin::sport() = PORT_MASK; while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000)))); if(USED_LANES>8) { *FastPin::cport() = ((~b2.shorts[i]) & PORT_MASK); } else { // b2.bytes[0] = 0; *FastPin::cport() = ((~b2.bytes[7-i]) & PORT_MASK); } while((next_mark - ARM_DWT_CYCCNT) > (T3)); *FastPin::cport() = PORT_MASK; } } // 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 &allpixels) { // Get access to the clock ARM_DEMCR |= ARM_DEMCR_TRCENA; ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; ARM_DWT_CYCCNT = 0; // Setup the pixel controller and load/scale the first byte allpixels.preStepFirstByteDithering(); register Lines b0; allpixels.preStepFirstByteDithering(); for(int i = 0; i < USED_LANES; i++) { b0.bytes[i] = allpixels.loadAndScale0(i); } cli(); uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3); while(allpixels.has(1)) { #if (FASTLED_ALLOW_INTERRUPTS == 1) cli(); // if interrupts took longer than 45µs, punt on the current frame if(ARM_DWT_CYCCNT > next_mark) { if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-5)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; } } #endif allpixels.stepDithering(); // Write first byte, read next byte writeBits<8+XTRA0,1>(next_mark, b0, allpixels); // Write second byte, read 3rd byte writeBits<8+XTRA0,2>(next_mark, b0, allpixels); allpixels.advanceData(); // Write third byte writeBits<8+XTRA0,0>(next_mark, b0, allpixels); #if (FASTLED_ALLOW_INTERRUPTS == 1) sei(); #endif }; return ARM_DWT_CYCCNT; } }; #define PMASK ((1<<(LANES))-1) #define PMASK_HI (PMASK>>8 & 0xFF) #define PMASK_LO (PMASK & 0xFF) template class SixteenWayInlineBlockClocklessController : 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() { static_assert(LANES <= 16, "Maximum of 16 lanes for Teensy parallel controllers!"); // FastPin<30>::setOutput(); // FastPin<29>::setOutput(); // FastPin<27>::setOutput(); // FastPin<28>::setOutput(); switch(LANES) { case 16: FastPin<12>::setOutput(); case 15: FastPin<11>::setOutput(); case 14: FastPin<13>::setOutput(); case 13: FastPin<10>::setOutput(); case 12: FastPin<9>::setOutput(); case 11: FastPin<23>::setOutput(); case 10: FastPin<22>::setOutput(); case 9: FastPin<15>::setOutput(); case 8: FastPin<5>::setOutput(); case 7: FastPin<21>::setOutput(); case 6: FastPin<20>::setOutput(); case 5: FastPin<6>::setOutput(); case 4: FastPin<8>::setOutput(); case 3: FastPin<7>::setOutput(); case 2: FastPin<14>::setOutput(); case 1: FastPin<2>::setOutput(); } } virtual void showPixels(PixelController & pixels) { mWait.wait(); uint32_t clocks = showRGBInternal(pixels); #if FASTLED_ALLOW_INTERRUPTS == 0 // Adjust the timer long microsTaken = CLKS_TO_MICROS(clocks); MS_COUNTER += (1 + (microsTaken / 1000)); #endif mWait.mark(); } typedef union { uint8_t bytes[16]; uint16_t shorts[8]; uint32_t raw[4]; } Lines; template __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, PixelController &pixels) { // , register uint32_t & b2) { register Lines b2; transpose8x1(b.bytes,b2.bytes); transpose8x1(b.bytes+8,b2.bytes+8); register uint8_t d = pixels.template getd(pixels); register uint8_t scale = pixels.template getscale(pixels); for(register uint32_t i = 0; (i < LANES) && (i < 8); i++) { while(ARM_DWT_CYCCNT < next_mark); next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3; *FastPin::sport() = PMASK_LO; *FastPin::sport() = PMASK_HI; while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+6)); *FastPin::cport() = ((~b2.bytes[7-i]) & PMASK_LO); *FastPin::cport() = ((~b2.bytes[15-i]) & PMASK_HI); while((next_mark - ARM_DWT_CYCCNT) > (T3)); *FastPin::cport() = PMASK_LO; *FastPin::cport() = PMASK_HI; b.bytes[i] = pixels.template loadAndScale(pixels,i,d,scale); if(LANES==16 || (LANES>8 && ((i+8) < LANES))) { b.bytes[i+8] = pixels.template loadAndScale(pixels,i+8,d,scale); } } } // 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 &allpixels) { // Get access to the clock ARM_DEMCR |= ARM_DEMCR_TRCENA; ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; ARM_DWT_CYCCNT = 0; // Setup the pixel controller and load/scale the first byte allpixels.preStepFirstByteDithering(); register Lines b0; allpixels.preStepFirstByteDithering(); for(int i = 0; i < LANES; i++) { b0.bytes[i] = allpixels.loadAndScale0(i); } cli(); uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3); while(allpixels.has(1)) { allpixels.stepDithering(); #if 0 && (FASTLED_ALLOW_INTERRUPTS == 1) cli(); // if interrupts took longer than 45µs, punt on the current frame if(ARM_DWT_CYCCNT > next_mark) { if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; } } #endif // Write first byte, read next byte writeBits<8+XTRA0,1>(next_mark, b0, allpixels); // Write second byte, read 3rd byte writeBits<8+XTRA0,2>(next_mark, b0, allpixels); allpixels.advanceData(); // Write third byte writeBits<8+XTRA0,0>(next_mark, b0, allpixels); #if 0 && (FASTLED_ALLOW_INTERRUPTS == 1) sei(); #endif }; sei(); return ARM_DWT_CYCCNT; } }; FASTLED_NAMESPACE_END #endif #endif