forked from Makuna/NeoPixelBus
ESP32 force bitbang in IRAM (#446)
* ESP32 force bitbang in IRAM * Split send_pixels into inverted and non-inverted Co-authored-by: Stephan Hadinger <stephan.hadinger@gmail.com>
This commit is contained in:
154
src/internal/NeoEspBitBangMethod.cpp
Normal file
154
src/internal/NeoEspBitBangMethod.cpp
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
NeoPixel library helper functions for Esp8266 and Esp32
|
||||||
|
|
||||||
|
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
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
static inline uint32_t getCycleCount(void)
|
||||||
|
{
|
||||||
|
uint32_t ccount;
|
||||||
|
__asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
|
||||||
|
return ccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICACHE_RAM_ATTR NeoEspBitBangBase_send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin, uint32_t t0h, uint32_t t1h, uint32_t period)
|
||||||
|
{
|
||||||
|
const uint32_t pinRegister = _BV(pin);
|
||||||
|
uint8_t mask = 0x80;
|
||||||
|
uint8_t subpix = *pixels++;
|
||||||
|
uint32_t cyclesStart = 0; // trigger emediately
|
||||||
|
uint32_t cyclesNext = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// do the checks here while we are waiting on time to pass
|
||||||
|
uint32_t cyclesBit = t0h;
|
||||||
|
if (subpix & mask)
|
||||||
|
{
|
||||||
|
cyclesBit = t1h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// after we have done as much work as needed for this next bit
|
||||||
|
// now wait for the HIGH
|
||||||
|
while (((cyclesStart = getCycleCount()) - cyclesNext) < period);
|
||||||
|
|
||||||
|
// set pin state
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
GPIO.out_w1ts = pinRegister;
|
||||||
|
#else
|
||||||
|
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// wait for the LOW
|
||||||
|
while ((getCycleCount() - cyclesStart) < cyclesBit);
|
||||||
|
|
||||||
|
// reset pin start
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
GPIO.out_w1tc = pinRegister;
|
||||||
|
#else
|
||||||
|
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICACHE_RAM_ATTR NeoEspBitBangBase_send_pixels_inv(uint8_t* pixels, uint8_t* end, uint8_t pin, uint32_t t0h, uint32_t t1h, uint32_t period)
|
||||||
|
{
|
||||||
|
const uint32_t pinRegister = _BV(pin);
|
||||||
|
uint8_t mask = 0x80;
|
||||||
|
uint8_t subpix = *pixels++;
|
||||||
|
uint32_t cyclesStart = 0; // trigger emediately
|
||||||
|
uint32_t cyclesNext = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// do the checks here while we are waiting on time to pass
|
||||||
|
uint32_t cyclesBit = t0h;
|
||||||
|
if (subpix & mask)
|
||||||
|
{
|
||||||
|
cyclesBit = t1h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// after we have done as much work as needed for this next bit
|
||||||
|
// now wait for the HIGH
|
||||||
|
while (((cyclesStart = getCycleCount()) - cyclesNext) < period);
|
||||||
|
|
||||||
|
// set pin state
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
GPIO.out_w1tc = pinRegister;
|
||||||
|
#else
|
||||||
|
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// wait for the LOW
|
||||||
|
while ((getCycleCount() - cyclesStart) < cyclesBit);
|
||||||
|
|
||||||
|
// reset pin start
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
GPIO.out_w1ts = pinRegister;
|
||||||
|
#else
|
||||||
|
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@@ -87,27 +87,17 @@ public:
|
|||||||
const static uint32_t Period = (F_CPU / 606061 - CYCLES_LOOPTEST); // 1.65us
|
const static uint32_t Period = (F_CPU / 606061 - CYCLES_LOOPTEST); // 1.65us
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern void NeoEspBitBangBase_send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin, uint32_t t0h, uint32_t t1h, uint32_t period);
|
||||||
|
extern void NeoEspBitBangBase_send_pixels_inv(uint8_t* pixels, uint8_t* end, uint8_t pin, uint32_t t0h, uint32_t t1h, uint32_t period);
|
||||||
|
|
||||||
class NeoEspPinset
|
class NeoEspPinset
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const static uint8_t IdleLevel = LOW;
|
const static uint8_t IdleLevel = LOW;
|
||||||
|
|
||||||
inline static void setPin(const uint32_t pinRegister)
|
inline static void send_pixels_impl(uint8_t* pixels, uint8_t* end, uint8_t pin, uint32_t t0h, uint32_t t1h, uint32_t period)
|
||||||
{
|
{
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
NeoEspBitBangBase_send_pixels(pixels, end, pin, t0h, t1h, period);
|
||||||
GPIO.out_w1ts = pinRegister;
|
|
||||||
#else
|
|
||||||
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
inline static void resetPin(const uint32_t pinRegister)
|
|
||||||
{
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
|
||||||
GPIO.out_w1tc = pinRegister;
|
|
||||||
#else
|
|
||||||
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -116,84 +106,18 @@ class NeoEspPinsetInverted
|
|||||||
public:
|
public:
|
||||||
const static uint8_t IdleLevel = HIGH;
|
const static uint8_t IdleLevel = HIGH;
|
||||||
|
|
||||||
inline static void setPin(const uint32_t pinRegister)
|
inline static void send_pixels_impl(uint8_t* pixels, uint8_t* end, uint8_t pin, uint32_t t0h, uint32_t t1h, uint32_t period)
|
||||||
{
|
{
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
NeoEspBitBangBase_send_pixels_inv(pixels, end, pin, t0h, t1h, period);
|
||||||
GPIO.out_w1tc = pinRegister;
|
|
||||||
#else
|
|
||||||
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
inline static void resetPin(const uint32_t pinRegister)
|
|
||||||
{
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
|
||||||
GPIO.out_w1ts = pinRegister;
|
|
||||||
#else
|
|
||||||
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T_SPEED, typename T_PINSET> class NeoEspBitBangBase
|
template<typename T_SPEED, typename T_PINSET> class NeoEspBitBangBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
__attribute__((noinline)) static void ICACHE_RAM_ATTR send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin)
|
static void send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin)
|
||||||
{
|
{
|
||||||
const uint32_t pinRegister = _BV(pin);
|
T_PINSET::send_pixels_impl(pixels, end, pin, T_SPEED::T0H, T_SPEED::T1H, T_SPEED::Period);
|
||||||
uint8_t mask = 0x80;
|
|
||||||
uint8_t subpix = *pixels++;
|
|
||||||
uint32_t cyclesStart = 0; // trigger emediately
|
|
||||||
uint32_t cyclesNext = 0;
|
|
||||||
|
|
||||||
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(pinRegister);
|
|
||||||
|
|
||||||
// wait for the LOW
|
|
||||||
while ((getCycleCount() - cyclesStart) < cyclesBit);
|
|
||||||
|
|
||||||
// reset pin start
|
|
||||||
T_PINSET::resetPin(pinRegister);
|
|
||||||
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
static inline uint32_t getCycleCount(void)
|
|
||||||
{
|
|
||||||
uint32_t ccount;
|
|
||||||
__asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
|
|
||||||
return ccount;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user