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:
authorDaniel Garcia <danielgarcia@gmail.com>2019-08-13 04:42:12 +0300
committerGitHub <noreply@github.com>2019-08-13 04:42:12 +0300
commit032ae7c606b918d7e135bffe62f49fbb8b4828ea (patch)
treebaa9542fd25df5e507a104ac18b18b0049cb50e0
parente68b1856ffe1d2196c75f11eda72059340903744 (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.h31
-rw-r--r--examples/Pintest/Pintest.ino49
-rw-r--r--fastspi.h45
-rw-r--r--fastspi_bitbang.h18
-rw-r--r--fastspi_nop.h2
-rw-r--r--fastspi_ref.h2
-rw-r--r--led_sysdefs.h3
-rw-r--r--platforms.h3
-rw-r--r--platforms/arm/k20/fastspi_arm_k20.h2
-rw-r--r--platforms/arm/k66/fastspi_arm_k66.h2
-rw-r--r--platforms/arm/kl26/fastspi_arm_kl26.h2
-rw-r--r--platforms/arm/mxrt1062/block_clockless_arm_mxrt1062.h209
-rw-r--r--platforms/arm/mxrt1062/clockless_arm_mxrt1062.h128
-rw-r--r--platforms/arm/mxrt1062/fastled_arm_mxrt1062.h9
-rw-r--r--platforms/arm/mxrt1062/fastpin_arm_mxrt1062.h91
-rw-r--r--platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h140
-rw-r--r--platforms/arm/mxrt1062/led_sysdefs_arm_mxrt1062.h43
-rw-r--r--platforms/arm/nrf51/fastspi_arm_nrf51.h2
-rw-r--r--platforms/arm/nrf52/fastspi_arm_nrf52.h10
-rw-r--r--platforms/arm/sam/fastspi_arm_sam.h2
-rw-r--r--platforms/avr/fastspi_avr.h8
-rw-r--r--release_notes.md4
22 files changed, 746 insertions, 59 deletions
diff --git a/chipsets.h b/chipsets.h
index d452abfe..8e9051d5 100644
--- a/chipsets.h
+++ b/chipsets.h
@@ -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*)&REG_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)));
}
diff --git a/fastspi.h b/fastspi.h
index fc0843be..38e8eabf 100644
--- a/fastspi.h
+++ b/fastspi.h
@@ -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