diff --git a/cores/esp8266/core_esp8266_waveform.cpp b/cores/esp8266/core_esp8266_waveform.cpp index 597a8e88ab..0338dbc952 100644 --- a/cores/esp8266/core_esp8266_waveform.cpp +++ b/cores/esp8266/core_esp8266_waveform.cpp @@ -170,6 +170,13 @@ static inline ICACHE_RAM_ATTR uint32_t min_u32(uint32_t a, uint32_t b) { return b; } +static inline ICACHE_RAM_ATTR int32_t max_32(int32_t a, int32_t b) { + if (a < b) { + return b; + } + return a; +} + // Stops a waveform on a pin int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) { // Can't possibly need to stop anything if there is no timer active @@ -259,6 +266,17 @@ static ICACHE_RAM_ATTR void timer1Interrupt() { // Check for toggles int32_t cyclesToGo = wave->nextServiceCycle - now; if (cyclesToGo < 0) { + // See #7057 + // The following is a no-op unless we have overshot by an entire waveform cycle. + // As modulus is an expensive operation, this code is removed for now: + // cyclesToGo = -((-cyclesToGo) % (wave->nextTimeHighCycles + wave->nextTimeLowCycles)); + // + // Alternative version with lower CPU impact: + // while (-cyclesToGo > wave->nextTimeHighCycles + wave->nextTimeLowCycles) { cyclesToGo += wave->nextTimeHighCycles + wave->nextTimeLowCycles); } + // + // detect interrupt storm, for example during wifi connection. + // if we overshoot the cycle by more than 25%, we forget phase and keep PWM duration + int32_t overshoot = ((uint32_t)-cyclesToGo) > ((wave->nextTimeHighCycles + wave->nextTimeLowCycles) >> 2); waveformState ^= mask; if (waveformState & mask) { if (i == 16) { @@ -266,16 +284,26 @@ static ICACHE_RAM_ATTR void timer1Interrupt() { } else { SetGPIO(mask); } - wave->nextServiceCycle = now + wave->nextTimeHighCycles; - nextEventCycles = min_u32(nextEventCycles, wave->nextTimeHighCycles); + if (overshoot) { + wave->nextServiceCycle = now + wave->nextTimeHighCycles; + nextEventCycles = min_u32(nextEventCycles, wave->nextTimeHighCycles); + } else { + wave->nextServiceCycle += wave->nextTimeHighCycles; + nextEventCycles = min_u32(nextEventCycles, max_32(wave->nextTimeHighCycles + cyclesToGo, microsecondsToClockCycles(1))); + } } else { if (i == 16) { GP16O &= ~1; // GPIO16 write slow as it's RMW } else { ClearGPIO(mask); } - wave->nextServiceCycle = now + wave->nextTimeLowCycles; - nextEventCycles = min_u32(nextEventCycles, wave->nextTimeLowCycles); + if (overshoot) { + wave->nextServiceCycle = now + wave->nextTimeLowCycles; + nextEventCycles = min_u32(nextEventCycles, wave->nextTimeLowCycles); + } else { + wave->nextServiceCycle += wave->nextTimeLowCycles; + nextEventCycles = min_u32(nextEventCycles, max_32(wave->nextTimeLowCycles + cyclesToGo, microsecondsToClockCycles(1))); + } } } else { uint32_t deltaCycles = wave->nextServiceCycle - now;