/*-------------------------------------------------------------------------
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
.
-------------------------------------------------------------------------*/
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ESP32C6_DEV) && !defined(ARDUINO_ESP32H2_DEV)
#include
#include "..\NeoUtil.h"
#if ESP_IDF_VERSION_MAJOR>=5
#include
#endif
// Interrupt lock class, used for RAII interrupt disabling
class InterruptLock
{
public:
inline void poll()
{
unlock();
lock();
}
inline InterruptLock()
#if defined(ARDUINO_ARCH_ESP32)
: _updateMux(portMUX_INITIALIZER_UNLOCKED)
#endif
{
lock();
}
inline ~InterruptLock()
{
unlock();
}
private:
#if defined(ARDUINO_ARCH_ESP32)
portMUX_TYPE _updateMux;
#endif
inline void lock()
{
#if defined(ARDUINO_ARCH_ESP32)
portENTER_CRITICAL(&_updateMux);
#else
noInterrupts();
#endif
}
inline void unlock()
{
#if defined(ARDUINO_ARCH_ESP32)
portEXIT_CRITICAL(&_updateMux);
#else
interrupts();
#endif
}
};
__attribute__((flatten))
uint32_t IRAM_ATTR neoEspBitBangWriteSpacingPixels(const uint8_t* data,
size_t dataSize,
uint8_t pin,
uint32_t t0h,
uint32_t t1h,
uint32_t period,
bool invert)
{
const uint8_t* dataEnd = data + dataSize;
uint32_t setValue = _BV(pin);
uint32_t clearValue = _BV(pin);
uint8_t mask = 0x80;
uint8_t subpix = *data++;
uint32_t cyclesStart = 0; // trigger emediately
uint32_t cyclesNext = 0;
#if defined(ARDUINO_ARCH_ESP32)
#if defined(CONFIG_IDF_TARGET_ESP32C3)
volatile uint32_t* setRegister = &GPIO.out_w1ts.val;
volatile uint32_t* clearRegister = &GPIO.out_w1tc.val;
setValue = _BV(pin);
clearValue = _BV(pin);
#else
volatile uint32_t* setRegister = &GPIO.out_w1ts;
volatile uint32_t* clearRegister = &GPIO.out_w1tc;
#endif // defined(CONFIG_IDF_TARGET_ESP32C3)
#else
uint32_t setRegister = PERIPHS_GPIO_BASEADDR + GPIO_OUT_W1TS_ADDRESS;
uint32_t clearRegister = PERIPHS_GPIO_BASEADDR + GPIO_OUT_W1TC_ADDRESS;
if (pin == 16)
{
setRegister = RTC_GPIO_OUT;
clearRegister = RTC_GPIO_OUT;
// reading AND writing RTC_GPIO_OUT is too slow inside the loop so
// we only do writing in the loop
clearValue = (READ_PERI_REG(RTC_GPIO_OUT) & (uint32)0xfffffffe);
setValue = clearValue | 1;
}
#endif // defined(ARDUINO_ARCH_ESP32)
if (invert)
{
std::swap(setRegister, clearRegister);
std::swap(setValue, clearValue);
}
// Need 100% focus on instruction timing
InterruptLock isrGuard;
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 = getEspCycleCount()) - cyclesNext) < period);
// set pin state
#if defined(ARDUINO_ARCH_ESP32)
*setRegister = setValue;
#else
WRITE_PERI_REG(setRegister, setValue);
#endif
// wait for the LOW
while ((getEspCycleCount() - cyclesStart) < cyclesBit);
// reset pin start
#if defined(ARDUINO_ARCH_ESP32)
*clearRegister = clearValue;
#else
WRITE_PERI_REG(clearRegister, clearValue);
#endif
cyclesNext = cyclesStart;
// next bit
mask >>= 1;
if (mask == 0)
{
// no more bits to send in this byte
// check for another byte
if (data >= dataEnd)
{
// no more bytes to send so stop
break;
}
// reset mask to first bit and get the next byte
mask = 0x80;
subpix = *data++;
}
}
return getEspCycleCount();
}
#endif // defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)