Re-implement PWM generator logic by earlephilhower · Pull Request #7231 · esp8266/Arduino

added 2 commits

April 19, 2020 12:36
Add 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.
Use fixed point math to adjust running PWM channels to the new
frequency.
Copy over full high/low periods only on the falling edge of a cycle,
ensuring phase alignment for Tone and Servo.

@earlephilhower

@earlephilhower

Ensure both the general purpose waveform generator and the PWM generator
are disabled on a pin used for Tone/digitalWrite.

@earlephilhower

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.

@earlephilhower

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.

@earlephilhower

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.

@earlephilhower

@earlephilhower

Jason2866 added a commit to tasmota/Arduino that referenced this pull request

Sep 5, 2020

This was referenced

Oct 5, 2020

@d-a-v

Includes 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).

@earlephilhower

@earlephilhower