Learn how to generate PWM signals with the Raspberry Pi Pico using MicroPython firmware. As an example, we’ll show you how to dim the brightness of an LED by changing the duty cycle over time.
We have a similar guide using Arduino IDE: Raspberry Pi Pico: Fading an LED using PWM (Arduino IDE).
Table of Contents
- Install and Run MicroPython on the Raspberry Pi Pico
- Introducing PWM
- Raspberry Pi Pico GPIOs and PWM
- The machine.PWM class
- Fading an LED – MicroPython Script
Prerequisites
To follow this tutorial you need MicroPython firmware installed in your Raspberry Pi Pico board. You also need an IDE to write and upload the code to your board.
The recommended MicroPython IDE for the Raspberry Pi Pico is Thonny IDE. Follow the next tutorial to learn how to install Thonny IDE, flash MicroPython firmware, and upload code to the board.
Introducing PWM (Pulse-Width Modulation)
PWM, or Pulse Width Modulation, is a technique that controls the power delivered to devices. It achieves this by turning the power on and off very quickly.
For example, if you alternate an LED’s voltage between HIGH and LOW very fast, your eyes can’t keep up with the speed at which the LED switches on and off; you’ll simply see some gradations in brightness.
That’s basically how PWM works — by producing an output that changes between HIGH and LOW at a very high frequency.
The duty cycle refers to the percentage of time the power is on compared to the total time of the on-off cycle—take a look at the following diagram.
By adjusting the duty cycle, we can control the average power delivered to the device. For instance, a higher duty cycle means the device receives more power, while a lower duty cycle results in less power. This enables us to control things like brightness in LEDs, speed in motors, or volume in speakers.
For example, a duty cycle of 50 percent results in 50 percent LED brightness, a duty cycle of 0 means the LED is fully off, and a duty cycle of 100 means the LED is fully on. Changing the duty cycle is how you produce different levels of brightness.
Raspberry Pi Pico GPIOs
The Raspberry Pi Pico comes with 40 pins, 26 of which are programmable GPIOs that you can use to connect peripherals. All Raspberry Pi Pico GPIOs can output PWM signals—they are marked in the diagrams in light green color.
You can use the following pinouts as a reference to identify and locate each GPIO on your board. The pinout is slightly different for the Pico and Pico W.
The following picture shows the Raspberry Pi Pico pinout (which functions are supported by each pin).
The pins marked in red are power pins that output 3.3V. The black pins are GND pins. All pins in light green can be used as “regular” GPIOs (input and output).
To learn more about the Pico Pinout, read the following guide: Raspberry Pi Pico and Pico W Pinout Guide: GPIOs Explained.
Raspberry Pi Pico – PWM Channels
The Raspberry Pi Pico has 8 independent PWM generators called slices. Each slice has two channels, which makes a total of 16 PWM channels.
The frequency of the PWM signal can range between 8Hz and 62.5MHz, while the microcontroller is running at a frequency of 125MHz.
Two channels of the same slice run at the same frequency, but can have a different duty rate.
The machine.PWM class
To handle PWM signals with the Raspberry Pi Pico using MicroPython, use the machine.PWM class. After creating a PWM object, called led_pwm for example:
led_pwm = PWM(Pin(20))
You can use the following methods:
- led_pwm.freq(FREQUENCY) to set the frequency of the PWM signal.
- led_pwm.duty_u16(DUTY_CYCLE) to set the duty cycle. It should be a value between 0 and 65535 (16-bit)
- led_pwm.deinit() to turn off PWM on the PWM slice used by the LED (in this example, it’s GPIO 20, so it will also stop PWM on GPIO 21 because they belong to the same PWM slice).
Schematic
Now, let’s create an example to better understand those PWM concepts. We’ll create a simple example to dim an LED (increase and decrease brightness smoothly).
Before proceeding, connect an LED to the Raspberry Pi Pico. We’re connecting to GPIO 20, but you can use any other GPIO.
Parts Required
Here’s a list of the parts you need to build the circuit:
You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!
Schematic – Raspberry Pi Pico
You can use the following diagram as a reference to connect the LED to the Raspberry Pi Pico board.
Script – RPi Pico Fading an LED
The following code creates a PWM signal on GPIO 20 and increases and decreases the duty cycle over time to dim the LED.
# Complete project details at https://RandomNerdTutorials.com/raspberry-pi-pico-pwm-micropython/
from machine import Pin, PWM
from time import sleep
# Set up PWM Pin
led = machine.Pin(20)
led_pwm = PWM(led)
duty_step = 129 # Step size for changing the duty cycle
#Set PWM frequency
frequency = 5000
led_pwm.freq (frequency)
try:
while True:
# Increase the duty cycle gradually
for duty_cycle in range(0, 65536, duty_step):
led_pwm.duty_u16(duty_cycle)
sleep(0.005)
# Decrease the duty cycle gradually
for duty_cycle in range(65536, 0, -duty_step):
led_pwm.duty_u16(duty_cycle)
sleep(0.005)
except KeyboardInterrupt:
print("Keyboard interrupt")
led_pwm.duty_u16(0)
print(led_pwm)
led_pwm.deinit()
How the code works
To create a PWM pin, import the PWM class in addition to the Pin class from the machine module.
from machine import Pin, PWM
You also need to import the sleep function from the time module to add delays to the code.
from time import sleep
First, create a Pin object on the GPIO of your choice. In this case, we’re using GPIO 20.
led = machine.Pin(20)
Then, create a PWM object on that pin, called led_pwm as follows:
led_pwm = PWM(led)
Set the duty cycle step—lower values result in smoother and slower transitions while higher values result in more abrupt and rapid changes. We’re using the value 129, because it is an integer divisor of 65535.
duty_step = 129 # Step size for changing the duty cycle
Note: The duty cycle can be a value between 0 and 65535 (16-bit). Which 65535 corresponds to 100% duty cycle (full brightness), and 0 corresponds to 0% duty cycle (unlit LED).
Set the frequency of the signal. We’re using a 5000Hz frequency, but you can choose a different value. The frequency can be a value between 0 and 62500.
#Set PWM frequency
frequency = 5000
led_pwm.freq (frequency)
To set the duty cycle use the duty_16() method on the PWM object and pass the duty cycle as an argument:
led.duty(duty_cycle)
Inside the while loop, we create a for loop that increases the duty cycle by 129 in each loop with an interval of 5 ms between each change.
for duty_cycle in range(0, 65536, duty_step):
led_pwm.duty_u16(duty_cycle)
sleep(0.005)
The range() function has the following syntax:
range(start, stop, step)
- Start: a number that specifies at which position to start. We want to start with 0 duty cycle;
- Stop: a number that specifies at which position we want to stop, excluding that value. The maximum duty cycle is 65535. So, we use 65535 +1, which is 65536.
- Step: an integer number that specifies the incrementation. By default, incrementation is 1.
In each for loop, we set the LED’s duty cycle to the current duty_cycle value:
led_pwm.duty_u16(duty_cycle)
After that, the duty_cycle variable is incremented by the step you specified.
When the duty cycle reaches full brightness, we decrease the brightness gradually in a similar way.
# Decrease the duty cycle gradually
for duty_cycle in range(65536, 0, -duty_step):
led_pwm.duty_u16(duty_cycle)
sleep(0.005)
Notice that we use try and except statements in this example.
The try block encloses the code that we want to run.
The except block is executed if a specific error occurs within the try block. In this case, it catches the KeyboardInterrupt error, which is raised when the user interrupts the program by pressing Ctrl+C in the terminal or stops the program in the IDE. When this error is detected, the code within the except block is executed.
In the except block, we set the duty cycle of the LED to 0 (fully off) using led.duty_u16(0) and call the led.deinit() to turn off PWM.
except KeyboardInterrupt:
print("Keyboard interrupt")
led_pwm.duty_u16(0)
print(led_pwm)
led_pwm.deinit()
This ensures that the LED is properly turned off and any necessary cleanup is performed, even if the program is interrupted by the user.
Demonstration
Save the code to your Raspberry Pi Pico board using Thonny IDE or any other MicroPython IDE of your choice.
Follow the next instructions if you’re using Thonny IDE.
Copy the code provided to the Thonny IDE untitled file.
With the code copied to the file, click on the Save icon. Then, select Raspberry Pi Pico.
Save the file with the following name: main.py. Overwrite any existing files with the same name.
Note: When you name a file main.py, the Raspberry Pi Pico will run that file automatically on boot. If you call it a different name, it will still be saved on the board filesystem, but it will not run automatically on boot.
Reset your board (unplug and plug it into your computer). Click the little green button “Run Current Script” or press F5.
Notice that the LED connected to GPIO 20 will start “breathing”, it will increase and decrease brightness over time gradually.
You can stop the execution of the program by pressing CTRL+C or by clicking on the STOP button. Notice that the LED will turn off, and a message will be printed on the Terminal shell.
Wrapping Up
To wrap up, to output PWM signals on the Raspberry Pi Pico GPIOs, we can use the machine.PWM class. First, you create a PWM object on a GPIO of your choice. After that, you can use the freq(), duty_u16 and deinit() methods on the PWM object, to set the PWM frequency, duty cycle, and stop PWM, respectively.
Learn more about the Raspberry Pi Pico with our eBook:
We hope you’ve found this tutorial useful. If you’re just getting started with the Raspberry Pi Pico, make sure you read our getting started guides:
- Getting Started with Raspberry Pi Pico (and Pico W)
- Raspberry Pi Pico and Pico W Pinout Guide: GPIOs Explained
- Raspberry Pi Pico: Control Digital Outputs and Read Digital Inputs (MicroPython)
- Raspberry Pi Pico: Read Analog Inputs (MicroPython)
Check out all our Raspberry Pi Pico Guides »
If you prefer to program the Raspberry Pi Pico using Arduino IDE, you can get started with the following tutorial:
Thanks for reading.
Awesome project 😎
This will help a great deal with a costume i am working on
That’s great!
In paragraph
“Raspberry Pi Pico – PWM Channels”
you wrote
“The frequency of the PWM signal can range between 8Hz and 62.5MHz, while the microcontroller is running at a frequency of 125MHz.”
But in
“How the code works”
is written
“Set the frequency of the signal. We’re using a 5000Hz frequency, but you can choose a different value. The frequency can be a value between 0 and 62500.”
How does this match?
With best regards, Gerhard
Hi.
One thing is the actual output value in terms of hardware.
But, in terms of software, the function accepts a number between 0 and 62500.
Regards,
Sara
Thank you for your prompt answer!