forked from Makuna/NeoPixelBus
Esp bitbang interrupt workaround (#759)
* Move interrupt handling in EspBitBangMethod Move the interrupt enable/disable in to the common neoEspBitBangWriteSpacingPixels function. * Check interrupts during ESP bit-banging Rather than leave interrupts completely disabled, check in between elements. If interrupted for long enough to complete an update, retry from the beginning. We don't yield to the system context, so only interrupt processing goes on. This is intended to stave off interrupt related crashes (hard WDT resets) experienced when a too-long strand is used with a too-busy network. Essentially it trades off correctness for stability. This logic is inspired by the workaround used in FastLED. * Replace TInterPixel with TLatch when bit-banging In NeoESPBitBangMethod, remove the minimum time between pixels, and replace it with a maximum time between pixels. Use the outer function to handle the retry logic. * Support no interrupt mode when bit-banging * Add no-interrupt bit-banging method typedefs
This commit is contained in:
@@ -41,14 +41,60 @@ static inline uint32_t getCycleCount(void)
|
||||
return ccount;
|
||||
}
|
||||
|
||||
void IRAM_ATTR neoEspBitBangWriteSpacingPixels(const uint8_t* pixels,
|
||||
// Interrupt lock class, used for RAII interrupt disabling
|
||||
class InterruptLock {
|
||||
#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
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
inline void poll()
|
||||
{
|
||||
unlock();
|
||||
lock();
|
||||
}
|
||||
|
||||
inline InterruptLock()
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
: updateMux(portMUX_INITIALIZER_UNLOCKED)
|
||||
#endif
|
||||
{
|
||||
lock();
|
||||
}
|
||||
|
||||
inline ~InterruptLock()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
};
|
||||
|
||||
bool IRAM_ATTR neoEspBitBangWriteSpacingPixels(const uint8_t* pixels,
|
||||
const uint8_t* end,
|
||||
uint8_t pin,
|
||||
uint32_t t0h,
|
||||
uint32_t t1h,
|
||||
uint32_t period,
|
||||
size_t sizePixel,
|
||||
uint32_t tSpacing,
|
||||
uint32_t tLatch,
|
||||
bool invert)
|
||||
{
|
||||
uint32_t setValue = _BV(pin);
|
||||
@@ -89,6 +135,9 @@ void IRAM_ATTR neoEspBitBangWriteSpacingPixels(const uint8_t* pixels,
|
||||
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
|
||||
@@ -136,20 +185,28 @@ void IRAM_ATTR neoEspBitBangWriteSpacingPixels(const uint8_t* pixels,
|
||||
mask = 0x80;
|
||||
subpix = *pixels++;
|
||||
|
||||
// if pixel spacing is needed
|
||||
if (tSpacing)
|
||||
// Hack: permit interrupts to fire
|
||||
// If we get held up more than the latch period, stop.
|
||||
// We do this at the end of an element to ensure that each
|
||||
// element always gets valid data, even if we don't update
|
||||
// every element.
|
||||
if (tLatch)
|
||||
{
|
||||
element++;
|
||||
++element;
|
||||
if (element == sizePixel)
|
||||
{
|
||||
isrGuard.poll();
|
||||
if ((getCycleCount() - cyclesNext) > tLatch)
|
||||
{
|
||||
return false; // failed
|
||||
}
|
||||
element = 0;
|
||||
|
||||
// wait for pixel spacing
|
||||
while ((getCycleCount() - cyclesNext) < tSpacing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true; // update complete
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user