Raspberry Pi Pico – PWM Primer

Sometimes ago I wrote an introduction to Raspberry Pi Pico. Today I will show how to exploit its pulse width modulation features. However, if you want to do some serious projects starting from this blog post, then I strongly recommend you do your own research on this topic to broaden your knowledge base. Okay, let’s get it started!

Rpi Pico PWM

RPi Pico PWM

In this session, I will briefly talk about the RPi Pico PWM (pulse width modulation), and let’s see how to start making related projects with it.

The RP2040 microcontroller at the core of the Raspberry Pi Pico has 8 Slices PWM, and each Slice is independent of the others. This simply means that we can set the frequency of a Slice without affecting the others. Luckily, there is a table in the RP2040 official datasheet (https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) to disclose which Slice/Output is associated with each pin.

GPIO PWM Table

As you can see in the datasheet, pulse width modulation is a scheme where a digital signal provides a smoothly varying average voltage. This is achieved with positive pulses of some controlled width, at regular intervals. The fraction of time spent high is known as the duty cycle. This may be used to approximate an analog output, or control switch-mode power electronics.

The RP2040 PWM block has 8 identical PWM slices, each with two output channels (A/B), where the B pin can also be used as an input for frequency and duty cycle measurement. That means each slice can drive two PWM output signals, or measure the frequency or duty cycle of an input signal. This gives a total of up to 16 controllable PWM outputs. All 30 GPIO pins can be driven by the PWM block.

An interesting thing at this moment to recall about PWM on the RP2040 is the possibility of having Duty Cycle values at 0% and at 100% without spurious peaks as happens on most other microcontrollers. Besides, it might be possible to have the two outputs of the same Slice with inverted phase signals (more on this shortly).

Look, there is a diagram in the official RPi Pico documentation (https://hackspace.raspberrypi.org/books/micropython-pico/pdf/download) to show the belonging of each pin of the Raspberry Pi Pico to the relative PWM channel. Below you will find that quick reference diagram.

Raspberry Pi Pico PWM Pins

Here, the Slice number is depicted in square brackets on the orange labels. For example, pins 21/GP16 and 22/GP17 belong to the same slice – GP16  is output A and GP17 is output B. Also see the other two mirrored pins GP0 and GP1.

Pico PWM & MicroPython

Happily, handling Pico PWM in MicroPython is ridiculously simple and reduced to the bare minimum. The good news is that you can set both the duty cycle and frequency of PWM!

In order to use PWM in MicroPython, we will use just three trivial instructions:

from machine import Pin , PWM

pwm = PWM ( Pin ( 16 ) ) # GP16
pwm. freq ( 100000 ) # 100kHz 
pwm. duty_u16 ( 32768 ) # duty 50% (65535/2)

Thonny ScreenShot

If you run the test code pointed above, in principle, it should generate a 100kHz PWM signal with a 50% duty cycle. My trusty oscilloscope, however, shows me about 99.92kHz with 49.94 duty cycle. This may be due to some inadequacies of MicroPython (correct me if I’m wrong)!

RPi Pico PWM GPIO16 DSO

This is not a grievous issue in many situations. That means, we can still use this PWM signal to drive a DC motor or an LED as it works happily even if the drive frequency is slightly deviated from what it looks for. Anyway, I’m not going into the observed anomaly right now, but I will continue to investigate and post relevant updates here.

And on a side note, pulse width modulation allows you to gift analog deportments to digital devices, such as motors and lamps. This means that rather than the motor/lamp being simply on or off, you can regulate its speed/brightness. The following little code can be used to pulse a regular LED ( with 220Ω series resistor) wired to GPIO15 of RPi Pico dim and bright in a continuous cycle (courtesy https://projects.raspberrypi.org/).

from machine import Pin, PWM
from time import sleep

pwm = PWM(Pin(15))

pwm.freq(1000)

while True:
    for duty in range(65025):
    pwm.duty_u16(duty)
    sleep(0.0001)
  for duty in range(65025, 0, -1):
    pwm.duty_u16(duty)
    sleep(0.0001)

Wrapping Up

I started my play with RPi Pico a few months ago. But as a new toy for me, I am still trying to understand its secrets. So, if you spot a mistake, do not despise me. Instead, help me learn it properly.

Finally, you can certainly see more RP Pico projects in the coming weeks. If you liked this post or it’s useful to you, could you take a minute to go through other articles in this site where I have shared some ideas that might make you happy.

Leave a Comment

2 Comments

  • My trusty oscilloscope, however, shows me about 99.92kHz with 49.94 duty cycle.

    That is the difference between the calibration on your oscilloscope and the absolute frequency of the crystal clock on the Pico. To get identical results would actually be very odd.