-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support PWM phase shifts on ESP8266 #4165
base: 0_15
Are you sure you want to change the base?
Conversation
Use the phase-locked soft PWM from the Arduino core to implement the same PWM phase management as ESP32s are using. The soft PWM code is vendored in, as it was previously, to add the NMI workaround from Aircoookie#4035. Completes Aircoookie#4034
- Better phase updates without dropping samples - Make second pin duty cycle always after first, even inverted
What I found when testing H-bridge boards (intended for motor use) is that on an ESP32 you have to set PWM to "slowest" with dithering enabled for it to have the best range. One board which is just made from transistors is way too slow for even that, it can maybe handle 1kHz at best and the crossover protection we implemented is still too fast: the crossover delay would have to be increased by a factor of 5 or so. Boards with proper ICs on them already have internal crossover protection so they are safe (I still managed to kill one at 2A at 40kHz though. |
The interesting thing about the soft PWM implementation on ESP8266 is that it has extremely high precision - "on" and "off" times are essentially specified to the exact CPU clock cycle (so the effective "bit depth" is log2(80MHz/frequency)) - but poor accuracy (can be delayed by microseconds). For the "normal" 880Hz PWM frequency used on ESP8266, that's about 16 bits of depth, though it'll get dicier when you're very close to all-off or all-on. Since it's based on the NMI - the highest priority interrupt - the delays are only a problem if you've got independent channels running. Phase locked outputs only get in to trouble if there are very short gaps between them, hence the fairly large dead time value I put in. In retrospect, this PR is the "lamest" possible approach, with the smallest changes to the PWM code from the Arduino core. It phase-locks the signals for each bus, but doesn't try to synchronize between busses -- this could lead to inaccuracies on multi-output systems if the timings end up being very close together. The PWM core could instead be rewritten to use a shared "base" frequency, storing the timings and phase offsets for each channel, which could potentially be faster and more accurate for PWM signals like we use, but at the cost of generality. |
@willmmiles IMO we only need to have phase locked (and synchronised) PWM outputs on a single bus (i.e. 2 channel PWM bus). There is no (justifiable) need to have all buses synchronised. |
I agree that there's no functional requirement for phase-locking between buses. However I also think it might be more CPU efficient to do so -- guaranteeing that transition edges line up as often as possible could mean fewer timer interrupts, particularly back-to-back ones. |
I would assume not many people have ESP8266 utilizing it as a 5 output PWM White controller. And even if used as 5 output PWM White controller that's only 5 pixels worth of processing. For anything else we can just suggest to replace ESP8266 with ESP32. That's just an idea if it is worth the effort in pursuing it further. IMO it is better to spend time elsewhere. |
Agreed! I mention it mostly for completeness, and in case someone's searching in the future. In hindsight it probably would've been faster to develop it that way from the start, but the solution presented in this PR should work well enough for all practical use cases. |
In some cases it was possible for the computed phase shift to skip a cycle. Update the shift calculation logic to prevent this.
Use the phase-locked soft PWM from the Arduino core to implement the same PWM phase output as ESP32s are using. The soft PWM code is vendored in, as it was previously, to add the NMI workaround from #4035. Testing revealed a need for additional tweaks to support adjusting relative signal phase without a stop/restart cycle (which interlocks with the NMI twice) so I've added that here too.
Completes #4034.
This PR includes a logic change that also affects ESP32:
I've tested this with an ESP8266 and an ESP32-WROVER using a scope to check for regressions. Additional testing would be greatly appreciated.
Finally: while this code does its best replicate the 2-channel CW/WW output pattern intended for H-bridge usage, I still wouldn't recommend using one with ESP8266. There isn't (currently) a way to atomically update several pins phase at once, leading to the possibility of overlap while an update is being applied.