diff --git a/src/NeoPixelBus.h b/src/NeoPixelBus.h
index 990405e..ed4a44b 100644
--- a/src/NeoPixelBus.h
+++ b/src/NeoPixelBus.h
@@ -109,6 +109,11 @@ License along with NeoPixel. If not, see
#include "internal/NeoNrf52xMethod.h"
+#elif defined(ARDUINO_ARCH_ARM) && defined(__SAME53N19A__) // must be before __arm__,
+
+// use the more general purpose ARM bitbang
+#include "internal/NeoArmBitBangMethod.h"
+
#elif defined(__arm__) // must be before ARDUINO_ARCH_AVR due to Teensy incorrectly having it set
#include "internal/NeoArmMethod.h"
diff --git a/src/internal/NeoArmBitBangMethod.h b/src/internal/NeoArmBitBangMethod.h
new file mode 100644
index 0000000..0ff0892
--- /dev/null
+++ b/src/internal/NeoArmBitBangMethod.h
@@ -0,0 +1,400 @@
+/*-------------------------------------------------------------------------
+NeoPixel library helper functions for ARM that support a cycle count
+
+Written by Michael C. Miller.
+
+I invest time and resources providing this open source code,
+please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
+
+-------------------------------------------------------------------------
+This file is part of the Makuna/NeoPixelBus library.
+
+NeoPixelBus is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation, either version 3 of
+the License, or (at your option) any later version.
+
+NeoPixelBus is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with NeoPixel. If not, see
+.
+-------------------------------------------------------------------------*/
+
+#pragma once
+
+#if defined(ARDUINO_ARCH_ARM)
+
+#define CYCLES_LOOPTEST (4) // adjustment due to loop exit test instruction cycles
+
+class NeoArmCcSpeedWs2811
+{
+public:
+ const static uint32_t T0H = (F_CPU / 3333333 - CYCLES_LOOPTEST); // 0.3us
+ const static uint32_t T1H = (F_CPU / 1052632 - CYCLES_LOOPTEST); // 0.95us
+ const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit
+};
+
+class NeoArmCcSpeedTm1814
+{
+public:
+ const static uint32_t T0H = (F_CPU / 2916666 - CYCLES_LOOPTEST); // 0.35us
+ const static uint32_t T1H = (F_CPU / 1666666 - CYCLES_LOOPTEST); // 0.75us
+ const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit
+};
+
+class NeoArmCcSpeedTm1829
+{
+public:
+ const static uint32_t T0H = (F_CPU / 3333333 - CYCLES_LOOPTEST); // 0.3us
+ const static uint32_t T1H = (F_CPU / 1250000 - CYCLES_LOOPTEST); // 0.8us
+ const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit
+};
+
+class NeoArmCcSpeed800Mhz
+{
+public:
+ const static uint32_t T0H = (F_CPU / 2500000 - CYCLES_LOOPTEST); // 0.4us
+ const static uint32_t T1H = (F_CPU / 1250000 - CYCLES_LOOPTEST); // 0.8us
+ const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit
+};
+
+class NeoArmCcSpeed400Mhz
+{
+public:
+ const static uint32_t T0H = (F_CPU / 2000000 - CYCLES_LOOPTEST);
+ const static uint32_t T1H = (F_CPU / 833333 - CYCLES_LOOPTEST);
+ const static uint32_t Period = (F_CPU / 400000 - CYCLES_LOOPTEST);
+};
+
+class NeoArmCcSpeedApa106
+{
+public:
+ const static uint32_t T0H = (F_CPU / 2857143 - CYCLES_LOOPTEST); // 0.35us
+ const static uint32_t T1H = (F_CPU / 740741 - CYCLES_LOOPTEST); // 1.35
+ const static uint32_t Period = (F_CPU / 606061 - CYCLES_LOOPTEST); // 1.65us
+};
+
+class NeoArmCcPinset
+{
+public:
+ const static uint8_t IdleLevel = LOW;
+
+ inline static void setPin(const uint8_t pin)
+ {
+ digitalWrite(pin, HIGH);
+ }
+
+ inline static void resetPin(const uint8_t pin)
+ {
+ digitalWrite(pin, LOW);
+ }
+};
+
+class NeoArmCcPinsetInverted
+{
+public:
+ const static uint8_t IdleLevel = HIGH;
+
+ inline static void setPin(const uint8_t pin)
+ {
+ digitalWrite(pin, LOW);
+ }
+
+ inline static void resetPin(const uint8_t pin)
+ {
+ digitalWrite(pin, HIGH);
+ }
+};
+
+template class NeoArmCcBitBangBase
+{
+public:
+ static void send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin)
+ {
+ uint8_t mask = 0x80;
+ uint8_t subpix = *pixels++;
+ uint32_t cyclesStart = 0; // trigger emediately
+ uint32_t cyclesNext = 0;
+
+ startCycleCount();
+
+ for (;;)
+ {
+ // do the checks here while we are waiting on time to pass
+ uint32_t cyclesBit = T_SPEED::T0H;
+ if (subpix & mask)
+ {
+ cyclesBit = T_SPEED::T1H;
+ }
+
+ // after we have done as much work as needed for this next bit
+ // now wait for the HIGH
+ while (((cyclesStart = getCycleCount()) - cyclesNext) < T_SPEED::Period);
+
+ // set pin state
+ T_PINSET::setPin(pin);
+
+ // wait for the LOW
+ while ((getCycleCount() - cyclesStart) < cyclesBit);
+
+ // reset pin start
+ T_PINSET::resetPin(pin);
+
+ cyclesNext = cyclesStart;
+
+ // next bit
+ mask >>= 1;
+ if (mask == 0)
+ {
+ // no more bits to send in this byte
+ // check for another byte
+ if (pixels >= end)
+ {
+ // no more bytes to send so stop
+ break;
+ }
+ // reset mask to first bit and get the next byte
+ mask = 0x80;
+ subpix = *pixels++;
+ }
+ }
+
+ stopCycleCount();
+ }
+
+protected:
+ static inline uint32_t getCycleCount(void)
+ {
+ return *((volatile uint32_t*)0xE0001004);
+ }
+
+ static inline void startCycleCount()
+ {
+ // init DWT feature
+ (*((volatile uint32_t*)0xE000EDFC)) |= 0x01000000;
+ // init DWT Count to zero
+ *((volatile uint32_t*)0xE0001004) = 0;
+ // start DWT
+ (*(volatile uint32_t*)0xe0001000) |= 0x40000001;
+ }
+
+ static inline void stopCycleCount()
+ {
+ // stop DWT
+ (*(volatile uint32_t*)0xe0001000) &= ~0x00000001;
+ }
+};
+
+class NeoArmCcBitBangSpeedWs2811 : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 300;
+};
+
+class NeoArmCcBitBangSpeedWs2812x : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 300;
+};
+
+class NeoArmCcBitBangSpeedSk6812 : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 80;
+};
+
+// normal is inverted signal
+class NeoArmCcBitBangSpeedTm1814 : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 200;
+};
+
+// normal is inverted signal
+class NeoArmCcBitBangSpeedTm1829 : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 200;
+};
+
+class NeoArmCcBitBangSpeed800Kbps : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 50;
+};
+
+class NeoArmCcBitBangSpeed400Kbps : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 50;
+};
+
+class NeoArmCcBitBangSpeedApa106 : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 50;
+};
+
+class NeoArmCcBitBangInvertedSpeedWs2811 : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 300;
+};
+
+class NeoArmCcBitBangInvertedSpeedWs2812x : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 300;
+};
+
+class NeoArmCcBitBangInvertedSpeedSk6812 : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 80;
+};
+
+// normal is inverted signal, so inverted is normal
+class NeoArmCcBitBangInvertedSpeedTm1814 : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 200;
+};
+
+// normal is inverted signal, so inverted is normal
+class NeoArmCcBitBangInvertedSpeedTm1829 : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 200;
+};
+
+class NeoArmCcBitBangInvertedSpeed800Kbps : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 50;
+};
+
+class NeoArmCcBitBangInvertedSpeed400Kbps : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 50;
+};
+
+class NeoArmCcBitBangInvertedSpeedApa106 : public NeoArmCcBitBangBase
+{
+public:
+ static const uint32_t ResetTimeUs = 50;
+};
+
+template class NeoArmCcBitBangMethodBase
+{
+public:
+ typedef NeoNoSettings SettingsObject;
+
+ NeoArmCcBitBangMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
+ _sizeData(pixelCount * elementSize + settingsSize),
+ _pin(pin)
+ {
+ pinMode(pin, OUTPUT);
+
+ _data = static_cast(malloc(_sizeData));
+ // data cleared later in Begin()
+ }
+
+ ~NeoArmCcBitBangMethodBase()
+ {
+ pinMode(_pin, INPUT);
+
+ free(_data);
+ }
+
+ bool IsReadyToUpdate() const
+ {
+ uint32_t delta = micros() - _endTime;
+
+ return (delta >= T_SPEED::ResetTimeUs);
+ }
+
+ void Initialize()
+ {
+ digitalWrite(_pin, T_PINSET::IdleLevel);
+
+ _endTime = micros();
+ }
+
+ void Update(bool)
+ {
+ // Data latch = 50+ microsecond pause in the output stream. Rather than
+ // put a delay at the end of the function, the ending time is noted and
+ // the function will simply hold off (if needed) on issuing the
+ // subsequent round of data until the latch time has elapsed. This
+ // allows the mainline code to start generating the next frame of data
+ // rather than stalling for the latch.
+ while (!IsReadyToUpdate())
+ {
+ yield(); // allows for system yield if needed
+ }
+
+ // Need 100% focus on instruction timing
+ noInterrupts();
+
+ T_SPEED::send_pixels(_data, _data + _sizeData, _pin);
+
+ interrupts();
+
+ // save EOD time for latch on next call
+ _endTime = micros();
+ }
+
+ uint8_t* getData() const
+ {
+ return _data;
+ };
+
+ size_t getDataSize() const
+ {
+ return _sizeData;
+ };
+
+ void applySettings(const SettingsObject& settings)
+ {
+ }
+
+private:
+ const size_t _sizeData; // Size of '_data' buffer below
+ const uint8_t _pin; // output pin number
+
+ uint32_t _endTime; // Latch timing reference
+ uint8_t* _data; // Holds LED color values
+};
+
+typedef NeoArmCcBitBangMethodBase NeoArmBitBangWs2811Method;
+typedef NeoArmCcBitBangMethodBase NeoArmBitBangWs2812xMethod;
+typedef NeoArmCcBitBangMethodBase NeoArmBitBangSk6812Method;
+typedef NeoArmCcBitBangMethodBase NeoArmBitBangTm1814Method;
+typedef NeoArmCcBitBangMethodBase NeoArmBitBangTm1829Method;
+typedef NeoArmCcBitBangMethodBase NeoArmBitBang800KbpsMethod;
+typedef NeoArmCcBitBangMethodBase NeoArmBitBang400KbpsMethod;
+typedef NeoArmCcBitBangMethodBase NeoArmBitBangApa106Method;
+
+typedef NeoArmBitBangWs2812xMethod NeoArmBitBangWs2813Method;
+typedef NeoArmBitBang800KbpsMethod NeoArmBitBangWs2812Method;
+typedef NeoArmBitBangSk6812Method NeoArmBitBangLc8812Method;
+
+typedef NeoArmCcBitBangMethodBase NeoArmBitBangWs2811InvertedMethod;
+typedef NeoArmCcBitBangMethodBase NeoArmBitBangWs2812xInvertedMethod;
+typedef NeoArmCcBitBangMethodBase NeoArmBitBangSk6812InvertedMethod;
+typedef NeoArmCcBitBangMethodBase NeoArmBitBangTm1814InvertedMethod;
+typedef NeoArmCcBitBangMethodBase NeoArmBitBangTm1829InvertedMethod;
+typedef NeoArmCcBitBangMethodBase NeoArmBitBang800KbpsInvertedMethod;
+typedef NeoArmCcBitBangMethodBase NeoArmBitBang400KbpsInvertedMethod;
+typedef NeoArmCcBitBangMethodBase NeoArmBitBangApa106InvertedMethod;
+
+typedef NeoArmBitBangWs2812xInvertedMethod NeoArmBitBangWs2813InvertedMethod;
+typedef NeoArmBitBang800KbpsInvertedMethod NeoArmBitBangWs2812InvertedMethod;
+typedef NeoArmBitBangSk6812InvertedMethod NeoArmBitBangLc8812InvertedMethod;
+
+#endif