-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Commit 0e735e3
Waveform: fix significant jitter, that stresses servos and is clearly audible in Tone output (#7022)
* Allow 100% high or low periods.
Let output remain at current level on stopping instead of always turning to low.
* Fix serious jitter issues in previous versions.
* Use ESP.getCycleCount() just like everyone else.
* Highest timer rate at which this runs stable appears to be 2µs (500kHz).
* Guard for zero period length undefined waveforms.
Fix for zero duty or off cycles and expiring from them.
* Cycle precision for expiry instead of special treatment for 0 value.
* Give expiry proper precedence over updating a waveform
* Important comment
* Refactored, identical behavior.
* Use plural for bit arrays.
* Fix for completely duty or all off cycle period case.
* Expiration is explicitly relative to service time.
* Comment updated, here it's about cycles not usecs.
* Revert misconception of how waveformToEnable/Disable communicates with the NMI handler.
* Rewrite to keep phase in sync if period remains same during duty cycle change.
Refactor identifies to distinguish CPU clock cycle from waveform cycle.
* Rather iterate even if full-duty or no-duty cycle in period, than too many calculations in NMI handler.
* Must fire timer early to reach waveform deadlines, otherwise under some load aggressive jitter occurs.
* Schedule expiry explicitly, too.
Needed to keep track of next timer ccy in each iteration, not just when changing level.
* Quick change lets analogWrite keep phase for any duty cycle (including 0% and 100%).
* Set duration to multiple of period, so tone stops on LOW pin output.
* Improve phase timing
* Eror causing next Timer IRQ to fail busy-to-off cycle transitions.
* Regression fix, don't reset timer if pending shortly.
* Rather reschedule ISR instead of busy looping during permitted maximum time.
* Lead time improved for ISR
* Reduce number of cycle calculations.
* Reactive the gcc optimize pragmas.
* Simplify calculation.
* handles overshoot where an updated period is shorter than the previous duty cycle
* Misleading code, there must ever be only one bit set at a time, start and stop block until the ISR has handled and reset the token.
* Prevent missing a duty cycle unless it is overshot already.
* Continuously remove distant pending waveform edges from the loop, continuously update now.
* Replace volatile for one-way exchange into ISR with memory fence.
* Remove redundant stack object.
* Revert pending waveform removal from loop - corrupts continuous next event computation.
* Reduce if/do ... while to while
* Convert relative timings to absolute.
* Relax waveform start to possibly cluster phases into same IRQ interval.
* max 12us in ISR seems to work best for servo/fan/led/tone combo test.
* Restructured code in ISR for expiration, this saves 36 byte IRAM, and improves PWM resolution.
* Simplified overshot detection and 0% / 100% duty cycle.
* Leave ISR early if rescheduling is more promising than busy-waiting until next edge.
* Stabilized timings.
* Prevent WDT under load.
* Use clock cycle resolution instead of us for analogWrite.
* Reduce idle calculations in ISR.
* Optimize in-ISR time.
* Support starting new waveform in phase with another running waveform.
* Align phase for analogWrite PWMs.
* Tune preshoot, add lost period fast forward.
* Adapt phase sync code from analogWrite to Servo
* Fix for going off 100% duty cycle period.
* Eschew obfuscation.
* Fixed logic for zero duty cycle.
* Determine generator quantum during same IRQ - this is better than timer resolution, but non-zero.
* Tune timings, fix write barriers and overshoot logic.
* Migrate Tone to waveform with CPU cycle precision
* Can do 60kHz PWM.
* Recalibrated timings after performance optimizations.
Initialize GPIO if needed.
* Fix regression for waveform runtime.
* Test cycle duration values for signed arithmetic safety.
* Performance tuning.
* Performance tweak, in-ISR quantum is now 1.12µs.
* Round up duration instead of down - possibly to zero, which means forever.
* Extend phase alignment with optional phase offset.
* Slightly better in-ISR quantum approximation for steadier increments.
* Waveform stopped by runtime limit in iSR doesn't deinit the timer, but stopWaveform refuses
to do anything if the waveform was stopped by runtime, either.
* Improved quantum correction code.
* Fix broken multi-wave generation.
* Aggregate GPIO output across inner loop. True phase sync, and now better performance.
* IRQ latency can be reduced from 2 to 1 us now, no WDT etc.
* Improved handling of complete idle cycle miss, progress directly into duty cycle.
* Recalibrated after latest changes and reverts.
* Overshoot compensation for duty cycle results in PWM milestone.
* Adjustments to duty/idle cycle to mitigate effects of floating duty cycle logic.
* Remove implicit condition from loop guard and fix timer restart duration
* Host all static globals in an anonymous static struct.
* Busy wait directly for next pending event and go to that pin.
* Record nextEventCcy in waveform struct to save a few cycles.
* Adapt duty cycle modification to only fix full duty and all idle cases.
* Remember next pin to operate between IRQs.
* Don't set pinMode each time on already running PWM or Tone.
* Remove quantum, correct irq latency from testing,reuse isr timeout from master et al
* Move updating "now" out of inner loop, prevents float between pins that are in phase lock.
* Merge init loop with action loop again.
* Adaptive PWM frequency and floating duty cycle.
* Predictive static frequency scaling.
* Dynamic frequency down-scaling
* Frequency scaling is only for PWM-like applications, anything needing real time duty cycles or frequency must be able to fail on overload.
* Conserve IRAM cache, resort to best effort.
* Directly scale frequency for all duty/all idle waves to reasonable maximum, reduces thrashing.
* Getting the math right beats permanently reducing PWM frequency.
* Rename identifier to help think about the problem.
* AutoPwm correction moved to correct location - after overshoot recalc - and allow limited duty floating
* Finish overshoot math fixes.
* First set pin mode, then digital write.
* Simplify calculations, fix non-autoPwm for servo use, where exact duty is needed, idle is elastic.
* Move wave initialization and modification outside the inner loop.
* Some optimizing.
* Updating "now" in the inner loop should lessen interference
* Finally get rid of volatile and use atomic thread fence memory barriers, great for ISR performance.
* Improved idle cycle overshoot mitigation.
* Improved duty cycle overshoot mitigation.
Case for investigation: 3% (shl 5) vs. 1.5% (shl 6), either less fuzz, but a few marked stray spots, or more fuzz, but no bumps in counter-PWM travel test.
* Move startPin etc. into common static struct
* Persist next event cycle across ISR invocations, like initPin was before.
* Recalibrated DELTAIRQ and IRQLATENCY. Tested @ 3x 40kHz PWM + 440Hz Tone
* CPU clock to Timer1 ccy correction must be dynamic even when BSP is compiled for fixed CPU clock.
* Corrected use of Timer1 registers and add rationale to Timer1 use in comment.
Recalibrate for improved frequence downscaling @ 80MHz and 160MHz.
* Let duty cycle overshoot correction depend on relative impact compareed to both period and duty.
* 80MHz/160MHz specific code can be compile-time selected in general, only NMI is affected by
apparent CPU frequency scaling in SDK code.
* Seems that removing the redudant resetting of edge interrupt mode shaves 0.5us off rearm latency.
* Recalibrated delta irq ccys.
* Off-by-one in 100% duty overshoot correction.
* Simple register writes.
* Memory fences checked and joining events into same loop iteration that are close to one another.
* Shorten progression when going off 100% duty.
* Code simplifications.
* Dynamically map pins out from in-ISR handling based on next event timing.
Major performance boost.
* Reverting maximum IRQ period to 10ms. This sets the wave reprogramming rate to 100Hz max.
* Revert recent change that is the most likely cause of reported PWM frequency drop regression.
* Much simplified overshoot mitigation code.
* Fixing overshoot mitigation, 3x 880Hz, 256 states now.
* Increase resolution by keeping reference time moving forward earlier.
* Mitigation logic for ESP8266 SDK boosting to 160MHz during some WiFi ops.
* Event timestamps are all recorded for compile-time CPU frequency, the timer ticks conversion
must be set at compile-time also. The SDK WiFi 160MHz boost mitigation temporarily handles
the CPU clock running twice as fast.
* Expired pins must not be checked for next event.
* Recalibrate after latest changes.
* Save a few bytes code.
* Guards are in place, so xor rather than and bitwise not.
* Reduce memory use.
* SDK boost to 160MHz may last across multiple ISR invocations, therefore adjust target ccy instead of ccount.
* Overshoot mitigation w/o PWM frequency change.
* New PWM overshoot mitigation code keeps frequency. Averages duty between consecutive periods.
* Small refactoring, remove code path that is never taken even at 3x25kHz/1023 PWM.
* Don't ever skip off duty, no matter if late or infinitely short.
* Shed speed-up code that didn't speed up things.
* Must always recompute new waveform.nextEventCcy if there is any busy pin.
* Break out of ISR if timespan to next event allows, instead of busy waiting and stealing CPU cycles from userland.
* Minor code simplification.
* Improve code efficiency.
* Improved performance of loop.
* Recalibrated.
* No positive effect of lead time inclusion was found during testing, remove this code.
Maximum period duration limit is implicit to timer, consider it documented constraint, don't runtime
check in ISR.
* Fix WDT when at 160MHz CPU clock the Timer1 is set below 1µs.
* Consolidate 160MHz constexpr check, finish 1µs minimum for Timer1 fix.
* Test for non-zero before subtract should improve performance.
* Reviewers/tested noted they were seeing WDT, and this change appeared to fix that.
* More expressive use of parentheses and alias CPU2X for reduced code size.
* Bug fix: at 160MHz compiled, don't force minimum Timer1 latency to 2µs.
* Alternate CPU frequency scaling mitigation.
* Handle time-of-flight in the right spot.
* Remove _toneMap from Tone.cpp
Co-authored-by: david gauchard <[email protected]>1 parent 8fe80f1 commit 0e735e3Copy full SHA for 0e735e3
File tree
5 files changed
+344
-212
lines changedFilter options
- cores/esp8266
- libraries/Servo/src
5 files changed
+344
-212
lines changed+1-8
Original file line number | Diff line number | Diff line change | |
---|---|---|---|
| |||
25 | 25 |
| |
26 | 26 |
| |
27 | 27 |
| |
28 |
| - | |
29 |
| - | |
30 |
| - | |
31 |
| - | |
32 | 28 |
| |
33 | 29 |
| |
34 | 30 |
| |
| |||
42 | 38 |
| |
43 | 39 |
| |
44 | 40 |
| |
45 |
| - | |
46 |
| - | |
47 |
| - | |
| 41 | + | |
48 | 42 |
| |
49 | 43 |
| |
50 | 44 |
| |
| |||
86 | 80 |
| |
87 | 81 |
| |
88 | 82 |
| |
89 |
| - | |
90 | 83 |
| |
91 | 84 |
|
0 commit comments