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 <dgarcia@dgarcia.net>2019-08-12 06:53:44 +0300
committerDaniel Garcia <dgarcia@dgarcia.net>2019-08-12 06:53:44 +0300
commit3f2f9121d591751ecb20b8725dbdcf79d2bd833b (patch)
tree4fa1f471b03868b6f0c90bd3017dca728672f64d
parent0b337fb865a26dfa350b5220b0c54a3819edd82a (diff)
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
-rw-r--r--fastspi.h15
-rw-r--r--fastspi_bitbang.h16
-rw-r--r--led_sysdefs.h3
-rw-r--r--platforms.h3
-rw-r--r--platforms/arm/mxrt1062/clockless_arm_mxrt1062.h115
-rw-r--r--platforms/arm/mxrt1062/fastled_arm_mxrt1062.h7
-rw-r--r--platforms/arm/mxrt1062/fastpin_arm_mxrt1062.h90
-rw-r--r--platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h140
-rw-r--r--platforms/arm/mxrt1062/led_sysdefs_arm_mxrt1062.h43
-rw-r--r--release_notes.md4
10 files changed, 431 insertions, 5 deletions
diff --git a/fastspi.h b/fastspi.h
index bf6709a9..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)
@@ -64,6 +68,17 @@ 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<uint32_t SPI_SPEED>\
diff --git a/fastspi_bitbang.h b/fastspi_bitbang.h
index 292c8ecd..70795e8b 100644
--- a/fastspi_bitbang.h
+++ b/fastspi_bitbang.h
@@ -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/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/mxrt1062/clockless_arm_mxrt1062.h b/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h
new file mode 100644
index 00000000..ce0d972e
--- /dev/null
+++ b/platforms/arm/mxrt1062/clockless_arm_mxrt1062.h
@@ -0,0 +1,115 @@
+#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;
+public:
+ 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 static void writeBits(register uint32_t & next_mark, register uint32_t off1, register uint32_t off2, register uint32_t off3, 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 + off1;
+ FastPin<DATA_PIN>::hi();
+ if(b&0x80) {
+ while((next_mark - ARM_DWT_CYCCNT) > off2);
+ FastPin<DATA_PIN>::lo();
+ } else {
+ while((next_mark - ARM_DWT_CYCCNT) > off3);
+ FastPin<DATA_PIN>::lo();
+ }
+ b <<= 1;
+ }
+
+ while(ARM_DWT_CYCCNT < next_mark);
+ next_mark = ARM_DWT_CYCCNT + off1;
+ FastPin<DATA_PIN>::hi();
+
+ if(b&0x80) {
+ while((next_mark - ARM_DWT_CYCCNT) > off2);
+ FastPin<DATA_PIN>::lo();
+ } else {
+ while((next_mark - ARM_DWT_CYCCNT) > off3);
+ FastPin<DATA_PIN>::lo();
+ }
+ }
+
+ uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
+ // Setup the pixel controller and load/scale the first byte
+ pixels.preStepFirstByteDithering();
+ register uint32_t b = pixels.loadAndScale0();
+
+ cli();
+ uint32_t off1 = _FASTLED_NS_TO_DWT(T1+T2+T3);
+ uint32_t off2 = _FASTLED_NS_TO_DWT(T3);
+ uint32_t off3 = _FASTLED_NS_TO_DWT(T2+T3);
+ uint32_t wait_off = _FASTLED_NS_TO_DWT((WAIT_TIME-INTERRUPT_THRESHOLD));
+
+ uint32_t next_mark = ARM_DWT_CYCCNT + off1;
+
+ 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 0; }
+ }
+ #endif
+ // Write first byte, read next byte
+ writeBits<8+XTRA0>(next_mark, off1, off2, off3, b);
+ b = pixels.loadAndScale1();
+
+ // Write second byte, read 3rd byte
+ writeBits<8+XTRA0>(next_mark, off1, off2, off3, b);
+ b = pixels.loadAndScale2();
+
+ // Write third byte, read 1st byte of next pixel
+ writeBits<8+XTRA0>(next_mark, off1, off2, off3, b);
+ b = pixels.advanceAndLoadAndScale0();
+ #if (FASTLED_ALLOW_INTERRUPTS == 1)
+ sei();
+ #endif
+ };
+
+ sei();
+ return ARM_DWT_CYCCNT;
+ }
+};
+#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..313ab0d3
--- /dev/null
+++ b/platforms/arm/mxrt1062/fastled_arm_mxrt1062.h
@@ -0,0 +1,7 @@
+#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"
+#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..bfb1cb47
--- /dev/null
+++ b/platforms/arm/mxrt1062/fastpin_arm_mxrt1062.h
@@ -0,0 +1,90 @@
+#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 _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; }
+};
+
+
+#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, 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/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