Using PWM on the Pyboard

Contents

What is PWM?

PWM (Pulse Width Modulation) is a digital technique used to emulate analog power. A digital signal is rapidly switched ON and OFF, varying the "on-time" (duty cycle) to change the average power delivered to a device.

Waveform illustrating the concept of PWM
Fig 1 - Waveform illustrating the concept of PWM[1]

The switching happens at a fixed and high frequency making the changes appear analog to the device.

Pyboard Timers, Channels and Pins

PWM is generated on Pyboard digital pins using timers and channels. This article from our MicroPython for Pyboard  series discusses the Pyboard's timers.

Each timer has one or more channels associated with it in a parent/child relationship. The timer acts as a master clock for each of its channels. Each channel can be assigned a separate task that is initiated when its timer is triggered.

Most channels have one or more digital pins assigned to them. In this way when the parent timer is triggered, a channel can perform its assigned action on an associated pin e.g. set its value to High or Low.

The Pyboard pinout is used to find the relationship between timers and their available channels as well as the pins referenced by a channel. Here is a link to the Pyboard V1.1 pinout.

For example: Tim2 has four channels; CH1, CH2, CH3 and CH4. Channel CH1 references two digital pins; X1 and X6.

Setting Up PWM on the Pyboard

Channels are created as TimerChannel  objects from the pyb.Timer.channel() method. This method associates the TimerChannel  object with a Timer   object and a digital pin.

PWM is setup on the digital pin using the pyb.TimerChannel.pulse_width_percent() method to assign a percentage (0 to 100) duty cycle. The duty cycle can be a float value.

The physical timer, channel and pin used must match as indicated in the Pyboard pinout otherwise an exception may be thrown.

Step 1: Create a Timer object
Timer2 with a frequency of 1 kHz 
timer = pyb.Timer(2, freq=1000)

Step 2: Create a TimerChannel object
Use Timer.channel() method to create a TimerChannel object.
The channel will be associated with Timer2 and use pin X2.
It is configured for PWM.
ch2 = timer.channel(2, pyb.Timer.PWM, pin=pyb.Pin('X2'))

Step 3: Assign a duty level to the TimerChannel object.
Use TimerChannel.pulse_width_percent() method to
assign a duty percentage to the TimerChannel object.
ch2.pulse_width_percent(50)
          

Example: PWM on the Pyboard

Two pins will be configured with PWM. The setup is as given in the program comments.


Code:
# Two pins will be setup for PWM.
# Timer 2 @ 1kHz, Channel 2, pin X2.
# Timer 4 @ 2kHz, Channel 1, pin X9.

from pyb import Timer, Pin

# Configure Timer2 at 1kHz.
timer2 = pyb.Timer(2, freq=1000)
# Configure Timer4 at 2kHz.
timer4 = pyb.Timer(4, freq=2000)

# Configure Channel 2 on Timer2 with pin X2.
ch2 = timer2.channel(2, Timer.PWM, pin=Pin('X2'))
# Configure Channel 1 on Timer4 with pin X9.
ch1 = timer4.channel(1, Timer.PWM, pin=Pin('X9'))

# Channel 2 has 25% duty cycle.
ch2.pulse_width_percent(25)
# Channel 1 has 75% duty cycle.
ch1.pulse_width_percent(75)
          

The voltage outputs on pins X2 and X9 were examined with an oscilloscope. Both waveforms and timings were as expected.

PWM output shown with an oscilloscope
Fig 2 - Yellow trace: Timer2, CH2 / Green trace: Timer4, CH1
  • Timer2, Channel 2 and pin X2
    • Wave period: 1ms (1KHz)
    • On 250µs, Off 750µs (25% duty cycle)
  • Timer4, Channel 1 and pin X9
    • Wave period: 500µs (2KHz)
    • On 375µs, Off 125µs (75% duty cycle)

References

  • [1]Author: Kriti Kohli, 2023;
    Article: What is PWM: Applications, Advantages & Challenges,
    Section: What is PWM?,
    Available at: https://www.aakash.ac.in/blog/what-is-pwm-applications-advantages-challenges/
    Accessed: December, 2025.