Re-implement PWM generator logic by earlephilhower · Pull Request #7231 · esp8266/Arduino
added 2 commits
April 19, 2020 12:36Add special-purpose PWM logic to preserve alignment of PWM signals for things like RGB LEDs. Keep a sorted list of GPIO changes in memory. At time 0 of the PWM cycle, set all pins to high. As time progresses bring down the additional pins as their duty cycle runs out. This way all PWM signals are time aligned by construction. This also reduces the number of PWM interrupts by up to 50%. Before, both the rising and falling edge of a PWM pin required an interrupt (and could shift arround accordingly). Now, a single IRQ sets all PWM rising edges (so 1 no matter how many PWM pins) and individual interrupts generate the falling edges. The code favors duty cycle accuracy over PWM period accuracy (since PWM is simulating an analog voltage it's the %age of time high that's the critical factor in most apps, not the refresh rate). Measurements give it about 35% less total error over full range at 20khz than master. @me-no-dev used something very similar in the original PWM generator.
Ensure both the general purpose waveform generator and the PWM generator are disabled on a pin used for Tone/digitalWrite.
A hump in the dueling PWMs was very prominent in prior pulls. The hump was caused by having a PWM falling edge just before the cycle restart, while having the other channel requesting a 1->0 transition just outside the busy-loop window of 10us. So it gets an IRQ for channel B 0->1, then waits 2..8us for the next PWM full cycle 0->1, and ends up returning from interrupt and not scheduling another IRQ for 10us...hence the horizontal leg of the bump... Reduce the minimum IRQ latency a little bit to minimize this effect. There will still be a (significantly smaller) hump when things cross, but it won't be anywhere near as bad or detectable.
Results in much closer PWM frequency range over any number of PWM pins, while taking 0 add'l overhead in IRAM or in the IRQ.
When _setPWMFreq was called the initial PWM mask was not set to 0 leading to occasional issues where non-PWM pins would be set to 1 on the nextPWM cycle. Manifested itself as an overtone at the PWM frequency +/-.
Borrow a trick from esp8266#7022 to exit the busy loop when the next event is too far out. Also reduce the IRQ delta subtraction because it was initially not NMI so there was much more variation than now. Keep the PWM state machine active at a higher prio than the standard tone generation when the next edge is very close (i.e. when we're at the max or min of the range and have 2 or more near edges). Adds a lot of resolution to the response at low and high ranges. Go from relative to absolute cycle counts in the main IRQ loop so that we don't mingle delta-cycles when the delta start was significantly different.
Keep PWM error <2.0% on entire range, from 0-100%, and remove the hump seen in testC by fixing the min IRQ delay setting.
The IRQ lead time was a tiny bit undersized, causing IRQs to come back too late for about .25us worth of PWM range. Adjust the constant accordingly
Since the adjustment for when a 160mhz compile is running at 80mhz is giving bad behavior, simply remove it and revert to old behavior.
This was referenced
Oct 5, 2020Includes patches to allow side-by-side existance of the two versions and a slight change such that the #define ..._PWM is unneeded (i.e. allow existing makefiles/scripts/etc. to get expected behavior w/o any changes on their part).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters