adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.09k stars 1.21k forks source link

Validate RP2040 requestedPWMOut frequencies #7644

Open SeattleDavid opened 1 year ago

SeattleDavid commented 1 year ago

There shoudlbe a way to generate an accurate PWM waveform at frequencies that are in the 1hz to 100Hz range, (or even slower).

While it has been suggested that these can be generated in a timing loop, that is not a viable solution when reliable timing or a specific duty cycle is required. Real world examples would be generating a square waveform for ringing a telephone bell (20hz or 30hz), pulse dialing a phone in an alarm system, sending tones of precise duration, or for phase-control lighting applications. Timing loops have a lot of jitter and it is almost impossible to form a consistent duty-cycle. There are cases where you just want/need a reliable, consistent waveform.

The pwmio.PWMOut function is optimized for very high frequencies, such as generating square-wave tones or maybe generating microwaves.

jepler commented 1 year ago

I'm not sure that it's possible to do substantially better than we currently do with the PWM hardware. If you have a specific idea about how to call functions in the pico_sdk that would achieve your goals, please give more information.

Instead, I'd recommend using the pio peripheral to get cycle-accurate delays and transitions. For instance, this example blinks an LED with a frequency of 5, 8, and 30Hz (and 50% duty cycle): https://learn.adafruit.com/intro-to-rp2040-pio-with-circuitpython/using-pio-to-blink-a-led-quickly-or-slowly —the on/off times can be any specific number of CPU cycles.

A more complicated example at https://learn.adafruit.com/intro-to-rp2040-pio-with-circuitpython/advanced-control-servos-with-pio-and-background-writes shows a program that can set any number of output pins to a desired state and then delay a specific number of CPU cycles. By looping a small buffer of data to the pio forever, this becomes a very flexible PWM-like peripheral. The full example is concerned with driving a bunch of RC-type servo motors with specific phase relationships, so while it doesn't exactly match your use cases it may give you something to think about.

If you're using an RP2040 then keep pio in mind as a way to do this kind of advanced stuff.

SeattleDavid commented 1 year ago

I’ll look into the PIO. My impression has been that Raspberry Pi Foundation created PIO as a way to prove that there can be something more difficult and obscure than assembly language.I don’t know how to make constructive suggestions as I don’t have low-level skills.

SeattleDavid commented 1 year ago

The link you gave is a 404 error:

jepler commented 1 year ago

Looks like my colleague edited the original post on github so that the auto-link behavior was correct -- try https://learn.adafruit.com/intro-to-rp2040-pio-with-circuitpython/using-pio-to-blink-a-led-quickly-or-slowly

SeattleDavid commented 1 year ago

Thanks. By the way, taking your suggestion a little tongue in cheek the logical extension of your argument is that there is no need or use for CircuitPython since anything can be better accomplished in assembly language.My point is that working at a high level with tools that have a minimum of misbehaviors lets end results get accomplished without falling into the infinite learning recursion syndrome.If the PWM just worked at lower frequencies then there would be one less case of “death by a thousand small cuts.” I realize there are certainly a number of explanations as to why low frequencies just don’t work. It’s easy to be self-sympathetic as to why things don’t work. But the fact that tjis doesn’t work as reasonably expected AND this is not documented makes this a “gotcha” akin to not blinking at all on Tuesday’s for no outwardly apparent reason.

gneverov commented 1 year ago

The RP2040 PWM hardware can support frequencies as low as 7.5 Hz. @SeattleDavid let us know if it is not working at say 10 Hz, as there may be a bug there.

If the frequency is too low for a specific port's PWM hardware, it would be a more seamless developer experience if PWMOut could automatically fall back to a timer interrupt based implementation.

dhalbert commented 1 year ago

For right now, we could raise an error when the requested frequency is out of range.

SeattleDavid commented 1 year ago

I like that!At, say, <6.5 it would (behind the scenes) switch how it does things. This could make it possible to support under 1hz.

SeattleDavid commented 1 year ago

Not as good as doing it the correct way by switching methods under 7.5, but certainly better than now when it is just wrong.