PWM
PWM (Pulse-Width Modulation) is widely used because it provides an efficient way to control power in various systems without requiring complex analog circuitry. Instead of generating a continuous analog signal, PWM rapidly switches a digital signal on and off, adjusting the ratio of on-time to off-time—known as the duty cycle—to regulate the system's behavior.
This technique is commonly used in applications such as:
- LED dimming → The brightness appears to change based on the duty cycle.
- Buzzer control → The frequency of PWM determines the sound produced.
- Motor control → PWM controls torque and indirectly influences speed.
- Heating elements → The average power delivered controls the temperature.
Understanding PWM Duty Cycle
In microcontrollers, both the frequency and duty cycle of a PWM signal can be adjusted. However, in most applications, only the duty cycle is modified. The duty cycle ranges from 0% to 100%, defining how long the signal stays in the high state within one cycle:
- 0% duty cycle → The signal remains low (logic 0) at all times.
- 100% duty cycle → The signal remains high (logic 1) at all times.
- 50% duty cycle → The output is a symmetrical square wave, meaning the signal spends equal time in high and low states.
The diagram below illustrates how the output waveform changes for different duty cycles. The average output voltage depends on the duty cycle—higher duty cycles result in a higher average voltage.
PWM waveforms with 25% and 75% duty cycles and their corresponding average DC equivalent values.
How PWM is Averaged?
Perception-Based Averaging
In some cases, the system itself does not average the PWM signal—the effect is perceived as smooth by an external observer (e.g., a human user). The key factor here is that we are controlling the voltage by switching it on and off rapidly, and the perceived effect depends on how the system responds to these voltage pulses.
-
LED Dimming
- A PWM-driven LED does not actually change brightness at any moment—it is either fully on or fully off.
- If the frequency is high enough (e.g., 1 kHz or more), the human eye cannot detect the rapid flickering. Instead, it perceives an average brightness based on the duty cycle.
- A 10% duty cycle appears dim, while a 90% duty cycle appears bright.
-
Buzzer Sound Control
- A piezoelectric buzzer or speaker driven by PWM does not produce a continuous analog sound wave. Instead, it receives rapid pulses of voltage.
- If the PWM frequency is in the audible range (e.g., 1 kHz to 5 kHz), the human ear perceives it as a continuous tone at that frequency.
- If the duty cycle is modulated over time, it can create different sound effects, such as volume control or beeping patterns.
Physical and Electrical Averaging
Unlike perception-based effects, many electronic and electromechanical systems physically smooth out the PWM signal over time. Here, the relationship between voltage, current, and system response determines how averaging works.
Voltage, Current, and Load Effects → The output depends not just on the duty cycle but also on how the load interacts with voltage and current:
- If the load is resistive, the output voltage is roughly proportional to the duty cycle.
- If the load has inductive properties (e.g., motors, coils), the current lags behind voltage changes and averages in a different way due to energy storage.
- If the load is capacitive, the response depends on the capacitor's charging and discharging characteristics.
- Electrical filtering → A passive low-pass filter (such as an RC circuit) smooths the signal into a more stable DC output.
Voltage, Current, and Load Effects
The output depends not just on the duty cycle but also on how the load interacts with voltage, current, and frequency:
- Resistive Load (e.g., heaters, simple LED circuits)
- Output voltage follows the PWM duty cycle.
- Higher frequency does not significantly change behavior.
- Inductive Load (e.g., motors, solenoids)
- Current lags behind voltage due to inductance.
- Higher frequency leads to smoother current flow, reducing torque ripple.
- Lower frequency may cause jerky motion in motors.
- Capacitive Load (e.g., power supplies, filtering circuits)
- Capacitors charge and discharge based on PWM pulses.
- Higher frequency improves voltage stability.
- Lower frequency results in larger voltage fluctuations.
Example for an RC load
By varying the duty cycle of the PWM voltage source (green) from 0% → 10% → 50% → 90% → 100%, the voltage across the capacitor (red) gradually increases in average value. The capacitor effectively smooths out the rapid transitions, producing an output that resembles a DC voltage.


-
Inductive smoothing in motors → When driving a DC motor, the motor's inductance and back-EMF filter the PWM signal, smoothing the current. This results in a torque that is proportional to the average current, which indirectly affects the motor speed under load.
-
Thermal and mechanical response → In heating systems, the slow thermal response naturally smooths the PWM pulses into a steady temperature. Similarly, in systems with high mechanical inertia, such as flywheels or hydraulic actuators, rapid switching results in an averaged force or pressure.
PWM is a versatile technique that can control various systems by leveraging different averaging effects. In some cases, the system itself averages the signal (e.g., electrical filtering, inductance, thermal response). In other cases, the observer perceives the averaging (e.g., LED brightness and sound perception). This makes PWM a powerful tool for efficiently controlling power, motion, sound, and light.
Hands-on tasks
Controlling the Robot's Buzzer
The robot's buzzer is a piezoelectric speaker, which requires more than just simple on/off control. Instead, it needs to be driven with a signal of a specific frequency to produce sound.
The speaker's audio quality is not high, and its response varies significantly across different frequencies. However, it is sufficient for generating simple sounds and is a low-cost component.
Info
The typical frequency response magnitude shown on the figure below. Over 10kHz the magnitude drops quickly. The optimal usage is around 4kHz. The highest peak magnitude is higher at 9.5kHz but the tone would be too high.

By modifying the frequency and duty cycle of the generated signal, we can achieve different sound effects.
Generating a 1 kHz Tone with 1% Duty Cycle
To begin, we will generate a 1 kHz square wave with a 1% duty cycle and drive the buzzer for 1 second. The buzzer is connected to GPIO22 on the microcontroller.
Required Modules
Instead of importing the entire modules, we will import only the necessary functions:
from machine import Pin, PWM # Import PWM and Pin control functions
from time import sleep_ms # Import sleep function for millisecond delays
Instead of using sleep(), we use sleep_ms(), which accepts time in milliseconds for more precise timing.
Initializing and Configuring the PWM Output
BUZZ = PWM(Pin(22)) # Initialize PWM on GPIO22
BUZZ.freq(1000) # Set frequency to 1 kHz
BUZZ.duty_u16(int(65535 * 0.01)) # Set duty cycle to 1% (1% of 65535)
Explanation of the Duty Cycle Setting:
- The duty cycle for
PWM.duty_u16()is specified in the range 0 to 65535, which corresponds to 0% to 100% duty cycle. - We calculate 1% of 65535 using:
int(65535 * 0.01) - The
int()function ensures the result is an integer, asduty_u16()requires an integer input. - We could hardcode
655for 1% duty cycle (BUZZ.duty_u16(655)), but the formula keeps the code clearer and more flexible for changes.
Running the Tone for 1 Second & Stopping the Signal
sleep_ms(1000) # Wait for 1 second
BUZZ.duty_u16(0) # Turn off the buzzer by setting duty cycle to 0%
The buzzer stops by setting the duty cycle to 0%, effectively turning off the output signal.
Try Running the Program!
Now, test the program to ensure it produces a 1 kHz tone for 1 second and then stops.
Complete Program
from machine import Pin, PWM # Import only necessary functions
from time import sleep_ms # Millisecond-based sleep function
# Initialize PWM on GPIO22 (Buzzer)
BUZZ = PWM(Pin(22))
# Set frequency and duty cycle for the buzzer
BUZZ.freq(1000) # 1 kHz tone
BUZZ.duty_u16(int(65535 * 0.01)) # 1% duty cycle
# Keep the tone on for 1 second
sleep_ms(1000)
# Turn off the buzzer by setting duty cycle to 0%
BUZZ.duty_u16(0)
Now experiment with different tones and durations to create sound effects or simple melodies!
Additional Tasks
Task 1: Experiment with different frequencies and duty cycles. Ensure that the generated signal does not exceed 1 second in duration.
Task 2: Try playing a simple melody with 4-5 notes!
Independent Tasks (To Be Completed Individually)
- Write a program to play a melody by retrieving the frequencies of notes and their duration from separate arrays.
Hint: Use the following example structure:
The values fx and tx should be defined!
from machine import Pin, PWM # Import only necessary functions
from time import sleep_ms # Millisecond-based sleep function
melody = [f1, f2, f3, ..., fn] # Array of note frequencies
melody_time = [t1, t2, t3, ..., tn] # Array of note durations
BUZZ = PWM(Pin(22)) # Initialize buzzer on GPIO22
for i in range(len(melody)):
BUZZ.duty_u16(500) # Set volume (adjustable)
BUZZ.freq(melody[i]) # Play the current note
sleep_ms(melody_time[i]) # Hold the note for its duration
BUZZ.duty_u16(0) # Turn off the sound
sleep_ms(20) # Short pause between notes
Modify the arrays to create a custom melody!
-
Write a program that applies an effect to the programmable LEDs.
-
Create dynamic color transitions, pulsing effects, or animations using NeoPixel LEDs.
-
Combine sound and light effects in a single program.
-
Synchronize LED animations with buzzer tones to create a coordinated effect.
Self-Assessment Questions
- What properties can be configured for a microcontroller's digital I/O pin?
- List both the universally available and optional properties/settings.
- How do the daisy-chained, programmable LEDs in the robot work?
- What does a PWM signal look like?
- What are its main characteristics?
- What does it mean for a PWM signal to have a 10-bit resolution?
Research Tasks
- Study the Raspberry Pi Pico's microcontroller RP2350 Datasheet
- What is switch bouncing, and what is debouncing means?
- How would you implement debouncing for a digital input (e.g., a push button)?
- How can a PWM signal be converted into an analog voltage?
- Study the MicroPython PWM function library.