From 01d9f190d2488d35d3a7b2e5618e1fee3985a7f6 Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Wed, 24 Mar 2021 15:28:23 -0700 Subject: [PATCH] Initial DWT general purpose bitbang --- src/NeoPixelBus.h | 5 + src/internal/NeoArmBitBangMethod.h | 400 +++++++++++++++++++++++++++++ 2 files changed, 405 insertions(+) create mode 100644 src/internal/NeoArmBitBangMethod.h 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