diff options
author | Daniel Garcia <danielgarcia@gmail.com> | 2019-08-13 04:42:12 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-13 04:42:12 +0300 |
commit | 032ae7c606b918d7e135bffe62f49fbb8b4828ea (patch) | |
tree | baa9542fd25df5e507a104ac18b18b0049cb50e0 | |
parent | e68b1856ffe1d2196c75f11eda72059340903744 (diff) |
Teensy 4 support! (#864)
* Pre-teensy4 work - with a 600Mhz clock, a 1Mhz clock was giving us a clock divider that overflowed a uint8_t - whoops...
* Some tweaks to chipset definitions to help out the Teensy 4 implementation
* Updating the pintest program w/Teensy 4 defs
* Preliminary Teensy 4 support, including hardware SPI and clockless chipsets - no support for parallel output or DMA'd output yet - also not fully tested for all chipsets on all pins, but smoke tested with some chipsets and pin combinations and logic analyzer in the meantime
* Checking in initial block clockless output - it compiles, but no testing yet, so it shouldn't be hooked up anywhere yet.
* Tweak and fix parallel output - still need to hook it up to the default addLeds setup
* more kicking
-rw-r--r-- | chipsets.h | 31 | ||||
-rw-r--r-- | examples/Pintest/Pintest.ino | 49 | ||||
-rw-r--r-- | fastspi.h | 45 | ||||
-rw-r--r-- | fastspi_bitbang.h | 18 | ||||
-rw-r--r-- | fastspi_nop.h | 2 | ||||
-rw-r--r-- | fastspi_ref.h | 2 | ||||
-rw-r--r-- | led_sysdefs.h | 3 | ||||
-rw-r--r-- | platforms.h | 3 | ||||
-rw-r--r-- | platforms/arm/k20/fastspi_arm_k20.h | 2 | ||||
-rw-r--r-- | platforms/arm/k66/fastspi_arm_k66.h | 2 | ||||
-rw-r--r-- | platforms/arm/kl26/fastspi_arm_kl26.h | 2 | ||||
-rw-r--r-- | platforms/arm/mxrt1062/block_clockless_arm_mxrt1062.h | 209 | ||||
-rw-r--r-- | platforms/arm/mxrt1062/clockless_arm_mxrt1062.h | 128 | ||||
-rw-r--r-- | platforms/arm/mxrt1062/fastled_arm_mxrt1062.h | 9 | ||||
-rw-r--r-- | platforms/arm/mxrt1062/fastpin_arm_mxrt1062.h | 91 | ||||
-rw-r--r-- | platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h | 140 | ||||
-rw-r--r-- | platforms/arm/mxrt1062/led_sysdefs_arm_mxrt1062.h | 43 | ||||
-rw-r--r-- | platforms/arm/nrf51/fastspi_arm_nrf51.h | 2 | ||||
-rw-r--r-- | platforms/arm/nrf52/fastspi_arm_nrf52.h | 10 | ||||
-rw-r--r-- | platforms/arm/sam/fastspi_arm_sam.h | 2 | ||||
-rw-r--r-- | platforms/avr/fastspi_avr.h | 8 | ||||
-rw-r--r-- | release_notes.md | 4 |
22 files changed, 746 insertions, 59 deletions
@@ -77,7 +77,7 @@ protected: /// @tparam CLOCK_PIN the clock pin for these leds /// @tparam RGB_ORDER the RGB ordering for these leds /// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(12) -template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(12) > +template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(12) > class LPD8806Controller : public CPixelLEDController<RGB_ORDER> { typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI; @@ -118,7 +118,7 @@ protected: /// @tparam CLOCK_PIN the clock pin for these leds /// @tparam RGB_ORDER the RGB ordering for these leds /// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(1) -template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(1)> +template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(1)> class WS2801Controller : public CPixelLEDController<RGB_ORDER> { typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI; SPI mSPI; @@ -140,7 +140,7 @@ protected: } }; -template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(25)> +template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(25)> class WS2803Controller : public WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, SPI_SPEED> {}; /// LPD6803 controller class (LPD1101). @@ -151,7 +151,7 @@ class WS2803Controller : public WS2801Controller<DATA_PIN, CLOCK_PIN, RGB_ORDER, /// @tparam CLOCK_PIN the clock pin for these leds /// @tparam RGB_ORDER the RGB ordering for these leds /// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(12) -template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(12)> +template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(12)> class LPD6803Controller : public CPixelLEDController<RGB_ORDER> { typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI; SPI mSPI; @@ -201,7 +201,7 @@ protected: /// @tparam CLOCK_PIN the clock pin for these leds /// @tparam RGB_ORDER the RGB ordering for these leds /// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(12) -template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(12)> +template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(12)> class APA102Controller : public CPixelLEDController<RGB_ORDER> { typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI; SPI mSPI; @@ -266,7 +266,7 @@ protected: /// @tparam CLOCK_PIN the clock pin for these leds /// @tparam RGB_ORDER the RGB ordering for these leds /// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(24) -template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(24)> +template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(24)> class SK9822Controller : public CPixelLEDController<RGB_ORDER> { typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI; SPI mSPI; @@ -340,7 +340,7 @@ protected: /// @tparam CLOCK_PIN the clock pin for these leds /// @tparam RGB_ORDER the RGB ordering for these leds /// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(10) -template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(10)> +template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(10)> class P9813Controller : public CPixelLEDController<RGB_ORDER> { typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI; SPI mSPI; @@ -390,7 +390,7 @@ protected: /// @tparam CLOCK_PIN the clock pin for these leds /// @tparam RGB_ORDER the RGB ordering for these leds /// @tparam SPI_SPEED the clock divider used for these leds. Set using the DATA_RATE_MHZ/DATA_RATE_KHZ macros. Defaults to DATA_RATE_MHZ(16) -template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint8_t SPI_SPEED = DATA_RATE_MHZ(16)> +template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER = RGB, uint32_t SPI_SPEED = DATA_RATE_MHZ(16)> class SM16716Controller : public CPixelLEDController<RGB_ORDER> { typedef SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> SPI; SPI mSPI; @@ -398,10 +398,15 @@ class SM16716Controller : public CPixelLEDController<RGB_ORDER> { void writeHeader() { // Write out 50 zeros to the spi line (6 blocks of 8 followed by two single bit writes) mSPI.select(); - mSPI.writeBytesValueRaw(0, 6); - mSPI.waitFully(); mSPI.template writeBit<0>(0); + mSPI.writeByte(0); + mSPI.writeByte(0); + mSPI.writeByte(0); mSPI.template writeBit<0>(0); + mSPI.writeByte(0); + mSPI.writeByte(0); + mSPI.writeByte(0); + mSPI.waitFully(); mSPI.release(); } @@ -524,7 +529,13 @@ class PL9823Controller : public ClocklessController<DATA_PIN, 3 * FMUL, 8 * FMUL // Similar to NS() macro, this calculates the number of cycles for // the clockless chipset (which may differ from CPU cycles) + +#ifdef FASTLED_TEENSY4 +// just use raw nanosecond values for the teensy4 +#define C_NS(_NS) _NS +#else #define C_NS(_NS) (((_NS * ((CLOCKLESS_FREQUENCY / 1000000L)) + 999)) / 1000) +#endif // GE8822 - 350ns 660ns 350ns template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> diff --git a/examples/Pintest/Pintest.ino b/examples/Pintest/Pintest.ino index a63f0d46..f0a0dadc 100644 --- a/examples/Pintest/Pintest.ino +++ b/examples/Pintest/Pintest.ino @@ -1,7 +1,10 @@ -#include <FastSPI_LED.h> +#include <FastLED.h> + +char fullstrBuffer[64]; const char *getPort(void *portPtr) { +// AVR port checks #ifdef PORTA if(portPtr == (void*)&PORTA) { return "PORTA"; } #endif @@ -38,6 +41,8 @@ const char *getPort(void *portPtr) { #ifdef PORTL if(portPtr == (void*)&PORTL) { return "PORTL"; } #endif + +// Teensy 3.x port checks #ifdef GPIO_A_PDOR if(portPtr == (void*)&GPIO_A_PDOR) { return "GPIO_A_PDOR"; } #endif @@ -65,7 +70,24 @@ const char *getPort(void *portPtr) { #ifdef REG_PIO_D_ODSR if(portPtr == (void*)®_PIO_D_ODSR) { return "REG_PIO_D_ODSR"; } #endif - return "unknown"; + +// Teensy 4 port checks +#ifdef GPIO1_DR + if(portPtr == (void*)&GPIO1_DR) { return "GPIO1_DR"; } +#endif +#ifdef GPIO2_DR +if(portPtr == (void*)&GPIO2_DR) { return "GPIO21_DR"; } +#endif +#ifdef GPIO3_DR +if(portPtr == (void*)&GPIO3_DR) { return "GPIO3_DR"; } +#endif +#ifdef GPIO4_DR +if(portPtr == (void*)&GPIO4_DR) { return "GPIO4_DR"; } +#endif + String unknown_str = "Unknown: " + String((size_t)portPtr, HEX); + strncpy(fullstrBuffer, unknown_str.c_str(), unknown_str.length()); + fullstrBuffer[sizeof(fullstrBuffer)-1] = '\0'; + return fullstrBuffer; } template<uint8_t PIN> void CheckPin() @@ -74,32 +96,35 @@ template<uint8_t PIN> void CheckPin() RwReg *systemThinksPortIs = portOutputRegister(digitalPinToPort(PIN)); RwReg systemThinksMaskIs = digitalPinToBitMask(PIN); - + Serial.print("Pin "); Serial.print(PIN); Serial.print(": Port "); - - if(systemThinksPortIs == FastPin<PIN>::port()) { + + if(systemThinksPortIs == FastPin<PIN>::port()) { Serial.print("valid & mask "); - } else { - Serial.print("invalid, is "); Serial.print(getPort((void*)FastPin<PIN>::port())); Serial.print(" should be "); + } else { + Serial.print("invalid, is "); Serial.print(getPort((void*)FastPin<PIN>::port())); Serial.print(" should be "); Serial.print(getPort((void*)systemThinksPortIs)); Serial.print(" & mask "); } if(systemThinksMaskIs == FastPin<PIN>::mask()) { Serial.println("valid."); - } else { + } else { Serial.print("invalid, is "); Serial.print(FastPin<PIN>::mask()); Serial.print(" should be "); Serial.println(systemThinksMaskIs); } -} +} template<> void CheckPin<-1> () {} -void setup() { +void setup() { + delay(5000); Serial.begin(38400); Serial.println("resetting!"); } -void loop() { +void loop() { CheckPin<MAX_PIN>(); - delay(10000); + delay(100000); + + Serial.print("GPIO_1_DR is: "); Serial.print(getPort((void*)&(GPIO1_DR))); } @@ -13,6 +13,10 @@ FASTLED_NAMESPACE_BEGIN #if defined(FASTLED_TEENSY3) && (F_CPU > 48000000) #define DATA_RATE_MHZ(X) (((48000000L / 1000000L) / X)) #define DATA_RATE_KHZ(X) (((48000000L / 1000L) / X)) +#elif defined(FASTLED_TEENSY4) // && (ARM_HARDWARE_SPI) +// just use clocks +#define DATA_RATE_MHZ(X) (1000000 * (X)) +#define DATA_RATE_KHZ(X) (1000 * (X)) #else #define DATA_RATE_MHZ(X) ((F_CPU / 1000000L) / X) #define DATA_RATE_KHZ(X) ((F_CPU / 1000L) / X) @@ -26,22 +30,22 @@ FASTLED_NAMESPACE_BEGIN ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #if !defined(FASTLED_ALL_PINS_HARDWARE_SPI) -template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> +template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> class SPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {}; #endif -template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> +template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> class SoftwareSPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {}; #ifndef FASTLED_FORCE_SOFTWARE_SPI #if defined(NRF51) && defined(FASTLED_ALL_PINS_HARDWARE_SPI) -template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> +template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> class SPIOutput : public NRF51SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {}; #endif #if defined(NRF52_SERIES) && defined(FASTLED_ALL_PINS_HARDWARE_SPI) -template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> +template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> class SPIOutput : public NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {}; #endif @@ -49,26 +53,37 @@ class SPIOutput : public NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDE #if defined(FASTLED_TEENSY3) && defined(ARM_HARDWARE_SPI) -template<uint8_t SPI_SPEED> +template<uint32_t SPI_SPEED> class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED, 0x4002C000> {}; #if defined(SPI2_DATA) -template<uint8_t SPI_SPEED> +template<uint32_t SPI_SPEED> class SPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED, 0x4002C000> {}; -template<uint8_t SPI_SPEED> +template<uint32_t SPI_SPEED> class SPIOutput<SPI_DATA, SPI2_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI_DATA, SPI2_CLOCK, SPI_SPEED, 0x4002C000> {}; -template<uint8_t SPI_SPEED> +template<uint32_t SPI_SPEED> class SPIOutput<SPI2_DATA, SPI_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI2_DATA, SPI_CLOCK, SPI_SPEED, 0x4002C000> {}; #endif +#elif defined(FASTLED_TEENSY4) && defined(ARM_HARDWARE_SPI) + +template<uint32_t SPI_SPEED> +class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public Teesy4HardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED, SPI, 0> {}; + +template<uint32_t SPI_SPEED> +class SPIOutput<SPI1_DATA, SPI_CLOCK, SPI_SPEED> : public Teesy4HardwareSPIOutput<SPI1_DATA, SPI1_CLOCK, SPI_SPEED, SPI1, 1> {}; + +template<uint32_t SPI_SPEED> +class SPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED> : public Teesy4HardwareSPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED, SPI2, 2> {}; + #elif defined(FASTLED_TEENSYLC) && defined(ARM_HARDWARE_SPI) -#define DECLARE_SPI0(__DATA,__CLOCK) template<uint8_t SPI_SPEED>\ +#define DECLARE_SPI0(__DATA,__CLOCK) template<uint32_t SPI_SPEED>\ class SPIOutput<__DATA, __CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<__DATA, __CLOCK, SPI_SPEED, 0x40076000> {}; - #define DECLARE_SPI1(__DATA,__CLOCK) template<uint8_t SPI_SPEED>\ + #define DECLARE_SPI1(__DATA,__CLOCK) template<uint32_t SPI_SPEED>\ class SPIOutput<__DATA, __CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<__DATA, __CLOCK, SPI_SPEED, 0x40077000> {}; DECLARE_SPI0(7,13); @@ -85,24 +100,24 @@ DECLARE_SPI1(21,20); #elif defined(__SAM3X8E__) -template<uint8_t SPI_SPEED> +template<uint32_t SPI_SPEED> class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public SAMHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> {}; #elif defined(AVR_HARDWARE_SPI) -template<uint8_t SPI_SPEED> +template<uint32_t SPI_SPEED> class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public AVRHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> {}; #if defined(SPI_UART0_DATA) -template<uint8_t SPI_SPEED> +template<uint32_t SPI_SPEED> class SPIOutput<SPI_UART0_DATA, SPI_UART0_CLOCK, SPI_SPEED> : public AVRUSART0SPIOutput<SPI_UART0_DATA, SPI_UART0_CLOCK, SPI_SPEED> {}; #endif #if defined(SPI_UART1_DATA) -template<uint8_t SPI_SPEED> +template<uint32_t SPI_SPEED> class SPIOutput<SPI_UART1_DATA, SPI_UART1_CLOCK, SPI_SPEED> : public AVRUSART1SPIOutput<SPI_UART1_DATA, SPI_UART1_CLOCK, SPI_SPEED> {}; #endif @@ -120,7 +135,7 @@ class SPIOutput<SPI_UART1_DATA, SPI_UART1_CLOCK, SPI_SPEED> : public AVRUSART1SP #endif // #if defined(USART_DATA) && defined(USART_CLOCK) -// template<uint8_t SPI_SPEED> +// template<uint32_t SPI_SPEED> // class AVRSPIOutput<USART_DATA, USART_CLOCK, SPI_SPEED> : public AVRUSARTSPIOutput<USART_DATA, USART_CLOCK, SPI_SPEED> {}; // #endif diff --git a/fastspi_bitbang.h b/fastspi_bitbang.h index d48e32bc..70795e8b 100644 --- a/fastspi_bitbang.h +++ b/fastspi_bitbang.h @@ -15,7 +15,7 @@ FASTLED_NAMESPACE_BEGIN // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint8_t SPI_SPEED> +template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint32_t SPI_SPEED> class AVRSoftwareSPIOutput { // The data types for pointers to the pin port - typedef'd here from the Pin definition because on avr these // are pointers to 8 bit values, while on arm they are 32 bit @@ -113,10 +113,16 @@ private: public: // We want to make sure that the clock pulse is held high for a nininum of 35ns. +#if defined(FASTLED_TEENSY4) + #define DELAY_NS (1000 / (SPI_SPEED/1000000)) + #define CLOCK_HI_DELAY do { delayNanoseconds((DELAY_NS/4)); } while(0); + #define CLOCK_LO_DELAY do { delayNanoseconds((DELAY_NS/4)); } while(0); +#else #define MIN_DELAY (NS(35) - 3) - #define CLOCK_HI_DELAY delaycycles<MIN_DELAY>(); delaycycles<(((SPI_SPEED-6) / 2) - MIN_DELAY)>(); - #define CLOCK_LO_DELAY delaycycles<(((SPI_SPEED-6) / 4))>(); + #define CLOCK_HI_DELAY do { delaycycles<MIN_DELAY>(); delaycycles<(((SPI_SPEED-6) / 2) - MIN_DELAY)>(); } while(0); + #define CLOCK_LO_DELAY do { delaycycles<(((SPI_SPEED-6) / 4))>(); } while(0); +#endif // write the BIT'th bit out via spi, setting the data pin then strobing the clcok template <uint8_t BIT> __attribute__((always_inline, hot)) inline static void writeBit(uint8_t b) { @@ -126,8 +132,8 @@ public: #ifdef ESP32 // try to ensure we never have adjacent write opcodes to the same register FastPin<CLOCK_PIN>::lo(); - FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY; - FastPin<CLOCK_PIN>::toggle(); CLOCK_LO_DELAY; + FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY; + FastPin<CLOCK_PIN>::toggle(); CLOCK_LO_DELAY; #else FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY; FastPin<CLOCK_PIN>::lo(); CLOCK_LO_DELAY; @@ -137,7 +143,7 @@ public: FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY; #ifdef ESP32 // try to ensure we never have adjacent write opcodes to the same register - FastPin<CLOCK_PIN>::toggle(); CLOCK_HI_DELAY; + FastPin<CLOCK_PIN>::toggle(); CLOCK_HI_DELAY; #else FastPin<CLOCK_PIN>::lo(); CLOCK_LO_DELAY; #endif diff --git a/fastspi_nop.h b/fastspi_nop.h index 5c5da010..1dcd2961 100644 --- a/fastspi_nop.h +++ b/fastspi_nop.h @@ -10,7 +10,7 @@ FASTLED_NAMESPACE_BEGIN /// A nop/stub class, mostly to show the SPI methods that are needed/used by the various SPI chipset implementations. Should /// be used as a definition for the set of methods that the spi implementation classes should use (since C++ doesn't support the /// idea of interfaces - it's possible this could be done with virtual classes, need to decide if i want that overhead) -template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> +template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> class NOPSPIOutput { Selectable *m_pSelect; diff --git a/fastspi_ref.h b/fastspi_ref.h index f68e63ef..00c41d34 100644 --- a/fastspi_ref.h +++ b/fastspi_ref.h @@ -8,7 +8,7 @@ FASTLED_NAMESPACE_BEGIN // A skeletal implementation of hardware SPI support. Fill in the necessary code for init, waiting, and writing. The rest of // the method implementations should provide a starting point, even if not hte most efficient to start with -template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> +template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> class REFHardwareSPIOutput { Selectable *m_pSelect; public: diff --git a/led_sysdefs.h b/led_sysdefs.h index 7abcd15e..27da24a0 100644 --- a/led_sysdefs.h +++ b/led_sysdefs.h @@ -18,6 +18,9 @@ #elif defined(__MKL26Z64__) // Include kl26/T-LC headers #include "platforms/arm/kl26/led_sysdefs_arm_kl26.h" +#elif defined(__IMXRT1062__) +// teensy4 +#include "platforms/arm/mxrt1062/led_sysdefs_arm_mxrt1062.h" #elif defined(__SAM3X8E__) // Include sam/due headers #include "platforms/arm/sam/led_sysdefs_arm_sam.h" diff --git a/platforms.h b/platforms.h index 82d7d993..f66599fd 100644 --- a/platforms.h +++ b/platforms.h @@ -18,6 +18,9 @@ #elif defined(__MKL26Z64__) // Include kl26/T-LC headers #include "platforms/arm/kl26/fastled_arm_kl26.h" +#elif defined(__IMXRT1062__) +// teensy4 +#include "platforms/arm/mxrt1062/fastled_arm_mxrt1062.h" #elif defined(__SAM3X8E__) // Include sam/due headers #include "platforms/arm/sam/fastled_arm_sam.h" diff --git a/platforms/arm/k20/fastspi_arm_k20.h b/platforms/arm/k20/fastspi_arm_k20.h index 70210a39..05123243 100644 --- a/platforms/arm/k20/fastspi_arm_k20.h +++ b/platforms/arm/k20/fastspi_arm_k20.h @@ -94,7 +94,7 @@ template <int VAL> void getScalars(uint32_t & preScalar, uint32_t & scalar, uint #define SPIX (*(SPI_t*)pSPIX) -template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX> +template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX> class ARMHardwareSPIOutput { Selectable *m_pSelect; SPIState gState; diff --git a/platforms/arm/k66/fastspi_arm_k66.h b/platforms/arm/k66/fastspi_arm_k66.h index 7e598cff..a40e5985 100644 --- a/platforms/arm/k66/fastspi_arm_k66.h +++ b/platforms/arm/k66/fastspi_arm_k66.h @@ -102,7 +102,7 @@ template <int VAL> void getScalars(uint32_t & preScalar, uint32_t & scalar, uint #define SPIX (*(SPI_t*)pSPIX) -template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX> +template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX> class ARMHardwareSPIOutput { Selectable *m_pSelect; SPIState gState; diff --git a/platforms/arm/kl26/fastspi_arm_kl26.h b/platforms/arm/kl26/fastspi_arm_kl26.h index 869b6054..b1e76677 100644 --- a/platforms/arm/kl26/fastspi_arm_kl26.h +++ b/platforms/arm/kl26/fastspi_arm_kl26.h @@ -82,7 +82,7 @@ template <int VAL> void getScalars(uint8_t & sppr, uint8_t & spr) { #define SPIX (*(KINETISL_SPI_t*)pSPIX) #define ARM_HARDWARE_SPI -template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX> +template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX> class ARMHardwareSPIOutput { Selectable *m_pSelect; diff --git a/platforms/arm/mxrt1062/block_clockless_arm_mxrt1062.h b/platforms/arm/mxrt1062/block_clockless_arm_mxrt1062.h new file mode 100644 index 00000000..6655ca79 --- /dev/null +++ b/platforms/arm/mxrt1062/block_clockless_arm_mxrt1062.h @@ -0,0 +1,209 @@ +#ifndef __INC_BLOCK_CLOCKLESS_ARM_MXRT1062_H +#define __INC_BLOCK_CLOCKLESS_ARM_MXRT1062_H + +FASTLED_NAMESPACE_BEGIN + +// Definition for a single channel clockless controller for the teensy4 +// See clockless.h for detailed info on how the template parameters are used. +#if defined(FASTLED_TEENSY4) + +#define __FL_T4_MASK ((1<<(LANES))-1) +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 = 50> +class InlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, __FL_T4_MASK> { + + uint8_t m_bitOffsets[16]; + uint8_t m_nActualLanes; + uint8_t m_nLowBit; + uint8_t m_nHighBit; + uint32_t m_nWriteMask; + uint8_t m_nOutBlocks; + uint32_t m_offsets[3]; + CMinWait<WAIT_TIME> mWait; +public: + + virtual int size() { return CLEDController::size() * m_nActualLanes; } + +// For each pin, if we've hit our lane count, break, otherwise set the pin to output, +// store the bit offset in our offset array, add this pin to the write mask, and if this +// pin ends a block sequence, then break out of the switch as well +#define _BLOCK_PIN(P) case P: { \ + if(m_nActualLanes == LANES) break; \ + FastPin<P>::setOutput(); \ + m_bitOffsets[m_nActualLanes++] = FastPin<P>::pinbit(); \ + m_nWriteMask |= FastPin<P>::mask(); \ + if( P == 27 || P == 7 || P == 30) break; \ +} + + virtual void init() { + // pre-initialize + memset(m_bitOffsets,0,16); + m_nActualLanes = 0; + m_nLowBit = 33; + m_nHighBit = 0; + m_nWriteMask = 0; + + // setup the bits and data tracking for parallel output + switch(FIRST_PIN) { + // GPIO6 block output + _BLOCK_PIN( 1); + _BLOCK_PIN( 0); + _BLOCK_PIN(24); + _BLOCK_PIN(25); + _BLOCK_PIN(19); + _BLOCK_PIN(18); + _BLOCK_PIN(14); + _BLOCK_PIN(15); + _BLOCK_PIN(17); + _BLOCK_PIN(16); + _BLOCK_PIN(22); + _BLOCK_PIN(23); + _BLOCK_PIN(20); + _BLOCK_PIN(21); + _BLOCK_PIN(26); + _BLOCK_PIN(27); + // GPIO7 block output + _BLOCK_PIN(10); + _BLOCK_PIN(12); + _BLOCK_PIN(11); + _BLOCK_PIN(13); + _BLOCK_PIN( 6); + _BLOCK_PIN( 9); + _BLOCK_PIN(32); + _BLOCK_PIN( 8); + _BLOCK_PIN( 7); + // GPIO 37 block output + _BLOCK_PIN(37); + _BLOCK_PIN(36); + _BLOCK_PIN(35); + _BLOCK_PIN(34); + _BLOCK_PIN(39); + _BLOCK_PIN(38); + _BLOCK_PIN(28); + _BLOCK_PIN(31); + _BLOCK_PIN(30); + } + + for(int i = 0; i < m_nActualLanes; i++) { + if(m_bitOffsets[i] < m_nLowBit) { m_nLowBit = m_bitOffsets[i]; } + if(m_bitOffsets[i] > m_nHighBit) { m_nHighBit = m_bitOffsets[i]; } + } + + m_nOutBlocks = (m_nHighBit + 8)/8; + + } + + + virtual void showPixels(PixelController<RGB_ORDER, LANES, __FL_T4_MASK> & pixels) { + mWait.wait(); + #if FASTLED_ALLOW_INTERRUPTS == 0 + uint32_t clocks = showRGBInternal(pixels); + // Adjust the timer + long microsTaken = CLKS_TO_MICROS(clocks); + MS_COUNTER += (1 + (microsTaken / 1000)); + #else + showRGBInternal(pixels); + #endif + + mWait.mark(); + } + + typedef union { + uint8_t bytes[32]; + uint8_t bg[4][8]; + uint16_t shorts[16]; + uint32_t raw[8]; + } _outlines; + + + template<int BITS,int PX> __attribute__ ((always_inline)) inline void writeBits(register uint32_t & next_mark, register _outlines & b, PixelController<RGB_ORDER, LANES, __FL_T4_MASK> &pixels) { + _outlines b2; + transpose8x1(b.bg[3], b2.bg[3]); + transpose8x1(b.bg[2], b2.bg[2]); + transpose8x1(b.bg[1], b2.bg[1]); + transpose8x1(b.bg[0], b2.bg[0]); + + register uint8_t d = pixels.template getd<PX>(pixels); + register uint8_t scale = pixels.template getscale<PX>(pixels); + + int x = 0; + for(uint32_t i = 8; i > 0;) { + i--; + while(ARM_DWT_CYCCNT < next_mark); + *FastPin<FIRST_PIN>::sport() = m_nWriteMask; + next_mark = ARM_DWT_CYCCNT + m_offsets[0]; + + uint32_t out = (b2.bg[3][i] << 24) | (b2.bg[2][i] << 16) | (b2.bg[1][i] << 8) | b2.bg[0][i]; + + out = ((~out) & m_nWriteMask); + while((next_mark - ARM_DWT_CYCCNT) > m_offsets[1]); + *FastPin<FIRST_PIN>::cport() = out; + + out = m_nWriteMask; + while((next_mark - ARM_DWT_CYCCNT) > m_offsets[2]); + *FastPin<FIRST_PIN>::cport() = out; + + // Read and store up to two bytes + if (x < m_nActualLanes) { + b.bytes[m_bitOffsets[x]] = pixels.template loadAndScale<PX>(pixels,x,d,scale); + x++; + if (x < m_nActualLanes) { + b.bytes[m_bitOffsets[x]] = pixels.template loadAndScale<PX>(pixels,x,d,scale); + x++; + } + } + } + } + + uint32_t showRGBInternal(PixelController<RGB_ORDER,LANES, __FL_T4_MASK> &allpixels) { + allpixels.preStepFirstByteDithering(); + _outlines b0; + uint32_t start = ARM_DWT_CYCCNT; + + for(int i = 0; i < m_nActualLanes; i++) { + b0.bytes[m_bitOffsets[i]] = allpixels.loadAndScale0(i); + } + + cli(); + m_offsets[0] = _FASTLED_NS_TO_DWT(T1+T2+T3); + m_offsets[1] = _FASTLED_NS_TO_DWT(T2+T3); + m_offsets[2] = _FASTLED_NS_TO_DWT(T3); + uint32_t wait_off = _FASTLED_NS_TO_DWT((WAIT_TIME-INTERRUPT_THRESHOLD)); + + uint32_t next_mark = ARM_DWT_CYCCNT + m_offsets[0]; + + while(allpixels.has(1)) { + allpixels.stepDithering(); + #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_off) { sei(); return ARM_DWT_CYCCNT - start; } + } + #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 (FASTLED_ALLOW_INTERRUPTS == 1) + sei(); + #endif + } + + sei(); + + return ARM_DWT_CYCCNT - start; + } +}; + +#endif //defined(FASTLED_TEENSY4) + +FASTLED_NAMESPACE_END + +#endif diff --git a/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h b/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h new file mode 100644 index 00000000..468c15dd --- /dev/null +++ b/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h @@ -0,0 +1,128 @@ +#ifndef __INC_CLOCKLESS_ARM_MXRT1062_H +#define __INC_CLOCKLESS_ARM_MXRT1062_H + +FASTLED_NAMESPACE_BEGIN + +// Definition for a single channel clockless controller for the teensy4 +// See clockless.h for detailed info on how the template parameters are used. +#if defined(FASTLED_TEENSY4) + +#define FASTLED_HAS_CLOCKLESS 1 + +#define _FASTLED_NS_TO_DWT(_NS) (((F_CPU_ACTUAL>>16)*(_NS)) / (1000000000UL>>16)) + +template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50> +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; + uint32_t off[3]; + +public: + static constexpr int __DATA_PIN() { return DATA_PIN; } + static constexpr int __T1() { return T1; } + static constexpr int __T2() { return T2; } + static constexpr int __T3() { return T3; } + static constexpr EOrder __RGB_ORDER() { return RGB_ORDER; } + static constexpr int __XTRA0() { return XTRA0; } + static constexpr bool __FLIP() { return FLIP; } + static constexpr int __WAIT_TIME() { return WAIT_TIME; } + + virtual void init() { + FastPin<DATA_PIN>::setOutput(); + mPinMask = FastPin<DATA_PIN>::mask(); + mPort = FastPin<DATA_PIN>::port(); + FastPin<DATA_PIN>::lo(); + } + +protected: + + virtual void showPixels(PixelController<RGB_ORDER> & pixels) { + mWait.wait(); + if(!showRGBInternal(pixels)) { + sei(); delayMicroseconds(WAIT_TIME); cli(); + showRGBInternal(pixels); + } + mWait.mark(); + } + + template<int BITS> __attribute__ ((always_inline)) inline void writeBits(register uint32_t & next_mark, register uint32_t & b) { + for(register uint32_t i = BITS-1; i > 0; i--) { + while(ARM_DWT_CYCCNT < next_mark); + next_mark = ARM_DWT_CYCCNT + off[0]; + FastPin<DATA_PIN>::hi(); + if(b&0x80) { + while((next_mark - ARM_DWT_CYCCNT) > off[1]); + FastPin<DATA_PIN>::lo(); + } else { + while((next_mark - ARM_DWT_CYCCNT) > off[2]); + FastPin<DATA_PIN>::lo(); + } + b <<= 1; + } + + while(ARM_DWT_CYCCNT < next_mark); + next_mark = ARM_DWT_CYCCNT + off[1]; + FastPin<DATA_PIN>::hi(); + + if(b&0x80) { + while((next_mark - ARM_DWT_CYCCNT) > off[2]); + FastPin<DATA_PIN>::lo(); + } else { + while((next_mark - ARM_DWT_CYCCNT) > off[2]); + FastPin<DATA_PIN>::lo(); + } + } + + uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) { + uint32_t start = ARM_DWT_CYCCNT; + + // Setup the pixel controller and load/scale the first byte + pixels.preStepFirstByteDithering(); + register uint32_t b = pixels.loadAndScale0(); + + cli(); + off[0] = _FASTLED_NS_TO_DWT(T1+T2+T3); + off[1] = _FASTLED_NS_TO_DWT(T2+T3); + off[2] = _FASTLED_NS_TO_DWT(T3); + uint32_t wait_off = _FASTLED_NS_TO_DWT((WAIT_TIME-INTERRUPT_THRESHOLD)); + + uint32_t next_mark = ARM_DWT_CYCCNT + off[0]; + + while(pixels.has(1)) { + pixels.stepDithering(); + #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_off) { sei(); return ARM_DWT_CYCCNT - start; } + } + #endif + // Write first byte, read next byte + writeBits<8+XTRA0>(next_mark, b); + b = pixels.loadAndScale1(); + + // Write second byte, read 3rd byte + writeBits<8+XTRA0>(next_mark, b); + b = pixels.loadAndScale2(); + + // Write third byte, read 1st byte of next pixel + writeBits<8+XTRA0>(next_mark, b); + b = pixels.advanceAndLoadAndScale0(); + #if (FASTLED_ALLOW_INTERRUPTS == 1) + sei(); + #endif + }; + + sei(); + return ARM_DWT_CYCCNT - start; + } +}; +#endif + +FASTLED_NAMESPACE_END + +#endif diff --git a/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h b/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h new file mode 100644 index 00000000..0814c7fa --- /dev/null +++ b/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h @@ -0,0 +1,9 @@ +#ifndef __INC_FASTLED_ARM_MXRT1062_H +#define __INC_FASTLED_ARM_MXRT1062_H + +#include "fastpin_arm_mxrt1062.h" +#include "fastspi_arm_mxrt1062.h" +#include "clockless_arm_mxrt1062.h" +#include "block_clockless_arm_mxrt1062.h" + +#endif diff --git a/platforms/arm/mxrt1062/fastpin_arm_mxrt1062.h b/platforms/arm/mxrt1062/fastpin_arm_mxrt1062.h new file mode 100644 index 00000000..e1b15674 --- /dev/null +++ b/platforms/arm/mxrt1062/fastpin_arm_mxrt1062.h @@ -0,0 +1,91 @@ +#ifndef __FASTPIN_ARM_MXRT1062_H +#define __FASTPIN_ARM_MXRT1062_H + +FASTLED_NAMESPACE_BEGIN + +#if defined(FASTLED_FORCE_SOFTWARE_PINS) +#warning "Software pin support forced, pin access will be slightly slower." +#define NO_HARDWARE_PIN_SUPPORT +#undef HAS_HARDWARE_PIN_SUPPORT + +#else + +/// Template definition for teensy 4.0 style ARM pins, providing direct access to the various GPIO registers. Note that this +/// uses the full port GPIO registers. It calls through to pinMode for setting input/output on pins +/// The registers are data output, set output, clear output, toggle output, input, and direction +template<uint8_t PIN, uint32_t _BIT, uint32_t _MASK, typename _GPIO_DR, typename _GPIO_DR_SET, typename _GPIO_DR_CLEAR, typename _GPIO_DR_TOGGLE> class _ARMPIN { +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; } + inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } + + inline static void hi() __attribute__ ((always_inline)) { _GPIO_DR_SET::r() = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { _GPIO_DR_CLEAR::r() = _MASK; } + inline static void set(register port_t val) __attribute__ ((always_inline)) { _GPIO_DR::r() = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { _GPIO_DR_TOGGLE::r() = _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)) { return _GPIO_DR::r() | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return _GPIO_DR::r() & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_GPIO_DR::r(); } + inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPIO_DR_SET::r(); } + inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPIO_DR_CLEAR::r(); } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } + inline static uint32_t pinbit() __attribute__ ((always_inline)) { return _BIT; } +}; + + +#define _R(T) struct __gen_struct_ ## T +#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } }; +#define _IO32(L) _RD32(GPIO ## L ## _DR); _RD32(GPIO ## L ## _DR_SET); _RD32(GPIO ## L ## _DR_CLEAR); _RD32(GPIO ## L ## _DR_TOGGLE); + +// From the teensy core - it looks like there's the "default set" of port registers at GPIO1-5 - but then there +// are a mirrored set for GPIO1-4 at GPIO6-9, which in the teensy core is referred to as "fast" - while the pin definitiosn +// at https://forum.pjrc.com/threads/54711-Teensy-4-0-First-Beta-Test?p=193716&viewfull=1#post193716 +// refer to GPIO1-4, we're going to use GPIO6-9 in the definitions below because the fast registers are what +// the teensy core is using internally +#define _DEFPIN_T4(PIN, L, BIT) template<> class FastPin<PIN> : public _ARMPIN<PIN, BIT, 1 << BIT, _R(GPIO ## L ## _DR), _R(GPIO ## L ## _DR_SET), _R(GPIO ## L ## _DR_CLEAR), _R(GPIO ## L ## _DR_TOGGLE)> {}; + +#if defined(FASTLED_TEENSY4) && defined(CORE_TEENSY) +_IO32(1); _IO32(2); _IO32(3); _IO32(4); _IO32(5); +_IO32(6); _IO32(7); _IO32(8); _IO32(9); + +#define MAX_PIN 39 +_DEFPIN_T4( 0,6, 3); _DEFPIN_T4( 1,6, 2); _DEFPIN_T4( 2,9, 4); _DEFPIN_T4( 3,9, 5); +_DEFPIN_T4( 4,9, 6); _DEFPIN_T4( 5,9, 8); _DEFPIN_T4( 6,7,10); _DEFPIN_T4( 7,7,17); +_DEFPIN_T4( 8,7,16); _DEFPIN_T4( 9,7,11); _DEFPIN_T4(10,7, 0); _DEFPIN_T4(11,7, 2); +_DEFPIN_T4(12,7, 1); _DEFPIN_T4(13,7, 3); _DEFPIN_T4(14,6,18); _DEFPIN_T4(15,6,19); +_DEFPIN_T4(16,6,23); _DEFPIN_T4(17,6,22); _DEFPIN_T4(18,6,17); _DEFPIN_T4(19,6,16); +_DEFPIN_T4(20,6,26); _DEFPIN_T4(21,6,27); _DEFPIN_T4(22,6,24); _DEFPIN_T4(23,6,25); +_DEFPIN_T4(24,6,12); _DEFPIN_T4(25,6,13); _DEFPIN_T4(26,6,30); _DEFPIN_T4(27,6,31); +_DEFPIN_T4(28,8,18); _DEFPIN_T4(29,9,31); _DEFPIN_T4(30,8,23); _DEFPIN_T4(31,8,22); +_DEFPIN_T4(32,7,12); _DEFPIN_T4(33,9, 7); _DEFPIN_T4(34,8,15); _DEFPIN_T4(35,8,14); +_DEFPIN_T4(36,8,13); _DEFPIN_T4(37,8,12); _DEFPIN_T4(38,8,17); _DEFPIN_T4(39,8,16); + +#define HAS_HARDWARE_PIN_SUPPORT + +#define ARM_HARDWARE_SPI +#define SPI_DATA 11 +#define SPI_CLOCK 13 + +#define SPI1_DATA 26 +#define SPI1_CLOCK 27 + +#define SPI2_DATA 35 +#define SPI2_CLOCK 37 + +#endif // defined FASTLED_TEENSY4 + +#endif // FASTLED_FORCE_SOFTWARE_PINSs + +FASTLED_NAMESPACE_END + +#endif diff --git a/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h b/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h new file mode 100644 index 00000000..fa6b81ff --- /dev/null +++ b/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h @@ -0,0 +1,140 @@ +#ifndef __INC_FASTSPI_ARM_MXRT1062_H +#define __INC_FASTSPI_ARM_MXRT1062_H + +FASTLED_NAMESPACE_BEGIN + +#if defined (FASTLED_TEENSY4) && defined(ARM_HARDWARE_SPI) +#include <SPI.h> + +template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_RATE, SPIClass & _SPIObject, int _SPI_INDEX> +class Teesy4HardwareSPIOutput { + Selectable *m_pSelect; + uint32_t m_bitCount; + uint32_t m_bitData; + inline IMXRT_LPSPI_t & port() __attribute__((always_inline)) { + switch(_SPI_INDEX) { + case 0: + return IMXRT_LPSPI4_S; + case 1: + return IMXRT_LPSPI3_S; + case 2: + return IMXRT_LPSPI1_S; + } + } + +public: + Teesy4HardwareSPIOutput() { m_pSelect = NULL; m_bitCount = 0;} + Teesy4HardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; m_bitCount = 0;} + + // set the object representing the selectable -- ignore for now + void setSelect(Selectable *pSelect) { /* TODO */ } + + // initialize the SPI subssytem + void init() { _SPIObject.begin(); } + + // latch the CS select + void inline select() __attribute__((always_inline)) { + // begin the SPI transaction + _SPIObject.beginTransaction(SPISettings(_SPI_CLOCK_RATE, MSBFIRST, SPI_MODE0)); + if(m_pSelect != NULL) { m_pSelect->select(); } + } + + // release the CS select + void inline release() __attribute__((always_inline)) { + if(m_pSelect != NULL) { m_pSelect->release(); } + _SPIObject.endTransaction(); + } + + // wait until all queued up data has been written + static void waitFully() { /* TODO */ } + + // write a byte out via SPI (returns immediately on writing register) - + void inline writeByte(uint8_t b) __attribute__((always_inline)) { + if(m_bitCount == 0) { + _SPIObject.transfer(b); + } else { + // There's been a bit of data written, add that to the output as well + uint32_t outData = (m_bitData << 8) | b; + uint32_t tcr = port().TCR; + port().TCR = (tcr & 0xfffff000) | LPSPI_TCR_FRAMESZ((8+m_bitCount) - 1); // turn on 9 bit mode + port().TDR = outData; // output 9 bit data. + while ((port().RSR & LPSPI_RSR_RXEMPTY)) ; // wait while the RSR fifo is empty... + port().TCR = (tcr & 0xfffff000) | LPSPI_TCR_FRAMESZ((8) - 1); // turn back on 8 bit mode + port().RDR; + m_bitCount = 0; + } + } + + // write a word out via SPI (returns immediately on writing register) + void inline writeWord(uint16_t w) __attribute__((always_inline)) { + writeByte(((w>>8) & 0xFF)); + _SPIObject.transfer(w & 0xFF); + } + + // A raw set of writing byte values, assumes setup/init/waiting done elsewhere + static void writeBytesValueRaw(uint8_t value, int len) { + while(len--) { _SPIObject.transfer(value); } + } + + // A full cycle of writing a value for len bytes, including select, release, and waiting + void writeBytesValue(uint8_t value, int len) { + select(); writeBytesValueRaw(value, len); release(); + } + + // A full cycle of writing a value for len bytes, including select, release, and waiting + template <class D> void writeBytes(register uint8_t *data, int len) { + uint8_t *end = data + len; + select(); + // could be optimized to write 16bit words out instead of 8bit bytes + while(data != end) { + writeByte(D::adjust(*data++)); + } + D::postBlock(len); + waitFully(); + release(); + } + + // A full cycle of writing a value for len bytes, including select, release, and waiting + void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); } + + // write a single bit out, which bit from the passed in byte is determined by template parameter + template <uint8_t BIT> inline void writeBit(uint8_t b) { + m_bitData = (m_bitData<<1) | ((b&(1<<BIT)) != 0); + // If this is the 8th bit we've collected, just write it out raw + register uint32_t bc = m_bitCount; + bc = (bc + 1) & 0x07; + if (!bc) { + m_bitCount = 0; + _SPIObject.transfer(m_bitData); + } + m_bitCount = bc; + } + + // write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template + // parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping + template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) { + select(); + int len = pixels.mLen; + + while(pixels.has(1)) { + if(FLAGS & FLAG_START_BIT) { + writeBit<0>(1); + } + writeByte(D::adjust(pixels.loadAndScale0())); + writeByte(D::adjust(pixels.loadAndScale1())); + writeByte(D::adjust(pixels.loadAndScale2())); + + pixels.advanceData(); + pixels.stepDithering(); + } + D::postBlock(len); + release(); + } + +}; + + +#endif + +FASTLED_NAMESPACE_END +#endif diff --git a/platforms/arm/mxrt1062/led_sysdefs_arm_mxrt1062.h b/platforms/arm/mxrt1062/led_sysdefs_arm_mxrt1062.h new file mode 100644 index 00000000..ac490825 --- /dev/null +++ b/platforms/arm/mxrt1062/led_sysdefs_arm_mxrt1062.h @@ -0,0 +1,43 @@ +#ifndef __INC_LED_SYSDEFS_ARM_MXRT1062_H +#define __INC_LED_SYSDEFS_ARM_MXRT1062_H + +#define FASTLED_TEENSY4 +#define FASTLED_ARM + +#ifndef INTERRUPT_THRESHOLD +#define INTERRUPT_THRESHOLD 1 +#endif + +// Default to allowing interrupts +#ifndef FASTLED_ALLOW_INTERRUPTS +#define FASTLED_ALLOW_INTERRUPTS 1 +#endif + +#if FASTLED_ALLOW_INTERRUPTS == 1 +#define FASTLED_ACCURATE_CLOCK +#endif + +#if (F_CPU == 96000000) +#define CLK_DBL 1 +#endif + +// Get some system include files +#include <avr/io.h> +#include <avr/interrupt.h> // for cli/se definitions + +// Define the register types +#if defined(ARDUINO) // && ARDUINO < 150 +typedef volatile uint32_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */ +typedef volatile uint32_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */ +#endif + +// extern volatile uint32_t systick_millis_count; +// # define MS_COUNTER systick_millis_count + +// Teensy4 provides progmem +#ifndef FASTLED_USE_PROGMEM +#define FASTLED_USE_PROGMEM 1 +#endif + + +#endif diff --git a/platforms/arm/nrf51/fastspi_arm_nrf51.h b/platforms/arm/nrf51/fastspi_arm_nrf51.h index 539fd656..6299e89d 100644 --- a/platforms/arm/nrf51/fastspi_arm_nrf51.h +++ b/platforms/arm/nrf51/fastspi_arm_nrf51.h @@ -9,7 +9,7 @@ // A nop/stub class, mostly to show the SPI methods that are needed/used by the various SPI chipset implementations. Should // be used as a definition for the set of methods that the spi implementation classes should use (since C++ doesn't support the // idea of interfaces - it's possible this could be done with virtual classes, need to decide if i want that overhead) -template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> +template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> class NRF51SPIOutput { struct saveData { diff --git a/platforms/arm/nrf52/fastspi_arm_nrf52.h b/platforms/arm/nrf52/fastspi_arm_nrf52.h index 8492282b..9c1a2198 100644 --- a/platforms/arm/nrf52/fastspi_arm_nrf52.h +++ b/platforms/arm/nrf52/fastspi_arm_nrf52.h @@ -21,7 +21,7 @@ */ /// SPI_CLOCK_DIVIDER is number of CPU clock cycles per SPI transmission bit? - template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> + template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> class NRF52SPIOutput { private: @@ -325,13 +325,13 @@ // Static member definition and initialization using templates. // see https://stackoverflow.com/questions/3229883/static-member-initialization-in-a-class-template#answer-3229919 - template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> + template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> bool NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_InUse = false; - template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> + template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> bool NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_NeedToWait = false; - template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> + template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> uint8_t NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_BufferIndex = 0; - template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> + template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> uint8_t NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_Buffer[2][2] = {{0,0},{0,0}}; #endif // #ifndef FASTLED_FORCE_SOFTWARE_SPI diff --git a/platforms/arm/sam/fastspi_arm_sam.h b/platforms/arm/sam/fastspi_arm_sam.h index eb9abe4c..a9446439 100644 --- a/platforms/arm/sam/fastspi_arm_sam.h +++ b/platforms/arm/sam/fastspi_arm_sam.h @@ -6,7 +6,7 @@ FASTLED_NAMESPACE_BEGIN #if defined(__SAM3X8E__) #define m_SPI ((Spi*)SPI0) -template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> +template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> class SAMHardwareSPIOutput { Selectable *m_pSelect; diff --git a/platforms/avr/fastspi_avr.h b/platforms/avr/fastspi_avr.h index fc14d596..d2edc966 100644 --- a/platforms/avr/fastspi_avr.h +++ b/platforms/avr/fastspi_avr.h @@ -20,7 +20,7 @@ FASTLED_NAMESPACE_BEGIN #define UCPHA1 1 #endif -template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> +template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> class AVRUSART1SPIOutput { Selectable *m_pSelect; @@ -167,7 +167,7 @@ public: #endif #if defined(UBRR0) -template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> +template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> class AVRUSART0SPIOutput { Selectable *m_pSelect; @@ -329,7 +329,7 @@ public: // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> +template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> class AVRHardwareSPIOutput { Selectable *m_pSelect; bool mWait; @@ -506,7 +506,7 @@ public: // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _SPI_CLOCK_DIVIDER> +template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> class AVRHardwareSPIOutput { Selectable *m_pSelect; bool mWait; diff --git a/release_notes.md b/release_notes.md index 81d16f33..925f054a 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,3 +1,7 @@ +FastLED 3.2.11 +============== +* Preliminary Teensy 4 support + FastLED 3.2.10 ============== * Adafruit Metro M4 Airlift support |