Recently I built a toy stroboscope1 which used the PWM timer on a Raspberry Pi Pico to flash a high-power LED.
Hardware
The hardware is simple. Besides the Pico, LED and a battery, we need two other components to flash the LED:
A MOSFET to switch the current flowing through the LED.
A chunky capacitor to stiffen the output of the battery.
There’s also a TM1638 module2 which provides buttons and 7-segment LEDs for a simple user interface.
Software
The software is straightforward too: if you ignore the UI code it’s basically a case of just configuring the Pico’s PWM. Happily, this is clearly explained in Section 4.5 of the RP2040 datasheet3.
Searching for Pico PWM examples4 will furnish you with plenty of examples written in the language of your choice.
PWM control
I found that I thought about PWM configuration in a slightly different way after the project, which I thought worth noting down for my future self.
If you’re just interested in generating a signal with a given period \(\tau\) though it boils down to finding \(a\), \(b\), and \(c\) such that,
$$ \tau = \tau_0 \times a \times b \times c, $$
where,
$$ \begin{eqnarray} \tau_0 &=& 8\textrm{ns}, \\ a &\in& [1, 2], \\ b &\in& [1, 256], \\ c &\in& [1, 65536]. \end{eqnarray} $$
Here \(a\) is determined by whether the PWM is running in phase-correct mode (where it counts up then down) or not (where it just counts up). In phase-correct mode \(a\) is 2, otherwise it’s 1.
\(b\) is the clock divider. The hardware supports a fractional divisor, but for simplicity’s sake we consider only integer values here.
\(c\) is the counter limit.
In many cases, the choice will not be unique: for example you might be able to double \(a\) and halve \(b\). Keeping the counter limit high and the divider low usually helps make the duty-cycle more precise.
Parameter space
It’s worth noting that there are 25 bits of configuration, which is about 3 x 10^7 (or about the number of seconds in a year). I think that’s too many to iterate over if you want a real-time response, but it’s perfectly reasonable to explore offline.
For example, suppose you want to find a divisor which gives good approximations to a set of frequencies: just consider all the divisors, accumulate some sort of misfit statistic for each target frequency, then pick the best. No thought is required!
This was obvious is retrospect, but I wasted time thinking about clever ways to do it all in real-time on the Pico.
Multiple frequencies
Suppose we’re going to change between a discrete set of frequencies. In some applications, if you make a small change in the period it’s helpful if the time between the last pulse of the old regime and the first pulse of the new one is roughly a whole number of periods. If you don’t do this, you’re effectively adding a phase jump.
The easiest way to do this is to keep the divisor the same: that way the counter remains set to a sensible number after the change.
If you do change the divisor then you either need to change the count value as well, or do the update when the counter’s at zero.
Again all this was all obvious in retrospect.
The UI
The TM1638 board gave me eight push-buttons which I treated as four pairs:
Increase/decrease the duty-cycle.
Increase/decrease the frequency with auto-repeat and acceleration.
Increase/decrease the frequency by 0.1Hz, no repeat.
Increase/decrease the frequency by 0.01Hz, no repeat.
It worked better to group the buttons by speed rather than function so that the button to reverse the last change was adjacent to the button which caused it.
References
- 1. https://en.wikipedia.org/wiki/Stroboscope
- 2. https://www.aliexpress.com/w/wholesale-tm1638.html
- 3. https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf
- 4. https://www.google.com/search?q=pico+pwm+examples