Skip to content

uPesy only delivers currently in France.

Contents Menu Expand Light mode Dark mode Auto light/dark mode

Generate variable voltages thanks to the PWM of the Pi Pico in MicroPython

By variable voltages, -> constant voltage value that can be adjsuted

PWM, a trick to generate variable voltages on digital pins

PWM is a technique that generates a voltage between 0 and 3.3V using only digital outputs . PWM stands for Pulse With Modulation . It’s based on the period ratio of a logic signal in its high state (3.3V) and in its low state (0V): the PWM consists in varying the duty of an electrical pulse.

PWM micropython operation

PWM operation

The succession of pulses with a given duty is seen on average as a constant voltage between 0V and 3.3V, whose value is determined by:

\[V_{out} = V_{in} \times \alpha\]

with α the duty cycle

A PWM signal is configured via the pulse duty and its frequency. We can tune these 2 parameters in MicroPython.

In practice, PWM is used for:
  • Controlling a motor speed

  • Control LED brightness

  • Generate square signals (with α = 0.5)

  • Generate music notes (like the sound in retro consoles)

PWM performance on the Raspberry Pi Pico

The PWM signals are not permanently generated by the microprocessor but by dedicated hardware blocks. Therefore, it is sufficient to configure the PWM blocks only once in the script: the signal will be generated continuously in the background. We associate an output of a PWM block with a pin on your Pico board. Processor resources will be free to perform other tasks.

Each PWM block can have an independent frequency.

Raspberry Pi Pico PWM Features

Pi Pico

PWM signal frequency range

7 Hz to 125 Mhz

Independent PWM frequency

8

PWM output

16

Duty cycle resolution

16 bits

Note

In most cases, a PWM frequency of 1000 Hz will be adequate in practice.

PWM on MicroPython with the Pico

Using PWM with MicroPython is very easy. MicroPython takes care of automatically selecting an available PWM block: it is not necessary to indicate which one you intend to use. The hardware stuff described above is completely hidden.

The configuration involves associating a PWM object with a Pin object and choosing the PWM frequency.

from machine import Pin, PWM

pin = Pin(25, mode=Pin.OUT)
pin_with_pwm = PWM(pin) # Attach PWM object on a pin

Note

Since the PWM object is also in the machine module, we can combine the 2 imports on a single line:

from machine import Pin
from machine import PWM

is equivalent to

from machine import Pin, PWM

Once we have linked an ESP32 pin to our PWM object, all the functions are done directly on the PWM object (Unlike the Arduino code where we specify the pin number with the analogWrite(pin_number) .

Hint

In Python, you can insert underscores _ to make numbers easier to read (and thus avoid counting zeros on huge digits)

For example, to write 1 MHz , instead of having 1000000 , you can set 1_000_000 .

We use the .duty() function to choose the duty cycle with a value between 0 and 2 16 (0-65535). The voltage is set instantly at the output of the pin selected previously.

Note

On ESP32 boards, you must use the .duty() function instead of .duty_u16() . _u16 specifies that the function expects a 16-bit value on input.

To see the voltage variation, you can use the built-in LED of your Pico board with the following script:

from machine import Pin, PWM

LED_BUITLTIN = 25 # For Raspberry Pi Pico

pwm_led = PWM(Pin(LED_BUITLTIN, mode=Pin.OUT)) # Attach PWM object on the LED pin

# Settings
pwm_led.freq(1_000)

while True:
    for duty in range(0,65025):
        pwm_led.duty_u16(duty) # For Pi Pico
        #pwm_led.duty(duty) # For ESP32

To be easier, we could specify the duty cycle percentage and then apply a formula.

from machine import Pin, PWM

LED_BUITLTIN = 25 # For Raspberry Pi Pico

pwm_led = PWM(Pin(LED_BUITLTIN, mode=Pin.OUT)) # Attach PWM object on the LED pin
pwm_led.freq(1_000)

duty_cycle = 50 # Between 0 - 100 %
pwm_led.duty_u16(int((duty_cycle/100)*65535))

The LED brightness can be changed by ranging the duty cycle between 0 and 100%.

from machine import Pin, PWM
import time

LED_BUITLTIN = 25 # For Raspberry Pi Pico

pwm_led = PWM(Pin(LED_BUITLTIN, mode=Pin.OUT)) # Attach PWM object on the LED pin
pwm_led.freq(1_000)

while True:
    for duty in range(100): # Duty from 0 to 100 %
        pwm_led.duty_u16(int((duty/100)*65535))
        time.sleep_ms(10)

Project: Diming the Raspberry Pi Pico built-in LED

With everything we have seen, the script is straightforward to achieve:

from machine import Pin, PWM

LED_BUITLTIN = 25 # For Raspberry Pi Pico

pwm_led = PWM(Pin(LED_BUITLTIN, mode=Pin.OUT)) # Attach PWM object on the LED pin

# Settings
pwm_led.freq(1_000)

while True:
    for duty in range(0,65025, 5):
        pwm_led.duty_u16(duty)
    for duty in range(65025,0, -5):
        pwm_led.duty_u16(duty)

Note

To vary the voltage faster, we change the duty from 5 to 5 with range(0, 65025, 5) . To decrement a value, we put a step of -5: range(65025, 0, -5) .

We use cookies to make your visit to the site as pleasant as possible. Privacy Policy