Pulse Width Modulation (PWM) is a technique that uses rapid digital switching to simulate a variable analog voltage by changing the ratio of on-time to off-time within a fixed cycle. Instead of lowering the actual voltage level, a microcontroller rapidly toggles a digital pin between its maximum HIGH state (e.g., 3.3V or 5V) and its LOW state (0V). By adjusting how long the signal stays HIGH versus LOW, you control the average power delivered to a component, allowing digital systems to dim LEDs, control motor speeds, and synthesize audio waveforms without needing complex digital-to-analog converters (DACs).
The Core Mechanism: Duty Cycle, Frequency, and the Math
To understand PWM, you must separate the signal into two independent variables: duty cycle and frequency.
The duty cycle is the percentage of one period that the signal remains HIGH. A 100% duty cycle means the pin is constantly on (maximum voltage). A 0% duty cycle means it is constantly off (0V). A 50% duty cycle means the pin is on for exactly half the time.
The frequency dictates how many of these on/off cycles occur per second, measured in Hertz (Hz). If the frequency is high enough, the human eye cannot perceive an LED flickering, and the mechanical inertia of a DC motor smooths out the power pulses into continuous rotation.
Imagine you are using an ESP32 to control a 12V DC cooling fan. The ESP32 GPIO pin outputs 3.3V, which is too low to drive the fan directly, so you use the 3.3V PWM signal to switch an IRLZ44N N-channel MOSFET. The MOSFET connects the fan to a 12V power supply.
If you program the ESP32 for a 75% duty cycle, the MOSFET acts as a perfect switch: it feeds the full 12V to the fan for 75% of the time, and cuts it to 0V for 25% of the time. The fan's inductance and mechanical inertia average this out. The average voltage the fan experiences is 9V (12V × 0.75). The fan spins at roughly 75% of its maximum speed, yet the ESP32 never actually generated a 9V analog signal.
Here is how duty cycle translates to average voltage on a standard 5V Arduino Uno logic pin:
| Duty Cycle | Arduino Value (8-bit) | On-Time Ratio | Average Output Voltage |
|---|---|---|---|
| 0% | 0 | Always OFF | 0.00V |
| 25% | 64 | 1/4 ON | 1.25V |
| 50% | 128 | 1/2 ON | 2.50V |
| 75% | 191 | 3/4 ON | 3.75V |
| 100% | 255 | Always ON | 5.00V |
What PWM Actually Changes in a Real Circuit
A common misconception is that PWM changes the peak voltage of the circuit. It does not. When an Arduino pin is set to a 50% duty cycle, an oscilloscope will still show the signal spiking to exactly 5.0V during the ON phase and dropping to 0.0V during the OFF phase. The peak voltage remains unchanged.
What PWM actually changes is the average energy delivery over time. Think of a digital water faucet that only has two states: fully blasting or completely shut off. If you want to fill a bucket halfway in one minute, you don't reduce the water pressure; you leave the faucet fully open for 30 seconds, then shut it off for 30 seconds. The bucket receives a 50% duty cycle of maximum flow.
In electrical terms, this switching behavior drastically alters power dissipation. When a MOSFET is fully ON, the voltage drop across it is near zero. When it is fully OFF, the current through it is zero. Since Power = Voltage × Current, the switching element wastes almost no energy as heat. This is why PWM is the foundational principle behind highly efficient switching power supplies and modern motor controllers, as documented in All About Circuits' technical guides on power electronics.
Where You Meet PWM in Practice (Microcontroller Bench Guide)
Different microcontroller families handle PWM generation via distinct hardware peripherals. Here is what you need to know when wiring up your bench.
Arduino Uno / Nano (ATmega328P)
On classic 8-bit Arduinos, PWM is handled by the `analogWrite()` function. Despite the name, it does not output analog voltage. It outputs an 8-bit PWM signal (values 0 to 255). The default frequency is roughly 490 Hz on most pins, but pins 5 and 6 operate at 980 Hz due to how the internal timers are mapped. For driving standard LEDs, 490 Hz is fine, but if you are driving audio circuits or high-speed motors, you may need to manually reconfigure the ATmega's timer registers to push the frequency above 20 kHz to avoid audible whining.
ESP32 (Xtensa / RISC-V Cores)
The ESP32 uses a dedicated peripheral called the LED Control (LEDC) module, which is vastly more powerful than the Arduino's timers. It supports up to 16 channels and configurable resolutions up to 14-bit. If you are using the modern ESP32 Arduino Core (v3.0 and newer), the API has been simplified. You no longer need to manually configure channels and timers using the deprecated `ledcSetup()` functions. Instead, you use the updated LEDC API:
// ESP32 Arduino Core 3.x PWM setup
const int pwmPin = 16;
void setup() {
// Attach pin, set 5000 Hz frequency, 10-bit resolution (0-1023)
ledcAttach(pwmPin, 5000, 10);
}
void loop() {
// Set to 75% duty cycle (75% of 1023 = ~768)
ledcWrite(pwmPin, 768);
delay(1000);
}
Raspberry Pi (BCM283x / BCM2711)
The Raspberry Pi is primarily a Linux computer, not a real-time microcontroller, so software-based PWM can suffer from jitter due to OS task scheduling. For stable signals, you must use the Pi's hardware PWM, which is strictly limited to specific GPIO pins (typically 12, 13, 18, and 19). If you need PWM on other pins, use the `gpiozero` library's `PWMLED` or `PWMOutputDevice` classes, but reserve them for non-critical loads like heating elements or slow-moving servos where microsecond jitter won't cause failures.
Common Confusions: What People Get Wrong About PWM
PWM vs. True Analog (DAC): PWM is a square wave; a Digital-to-Analog Converter (DAC) outputs a smooth, continuous voltage curve. If you feed a raw PWM signal into an audio amplifier, you will hear harsh, high-frequency switching noise. To use PWM for audio or true analog control, you must pass the signal through a passive RC (resistor-capacitor) low-pass filter to smooth the square wave into a DC voltage.
PWM vs. Linear Voltage Regulation: If you want to run a 5V fan off a 12V battery, you could use a linear voltage regulator (like an LM7805) to drop the voltage. However, the regulator burns the excess 7V as heat (Power = 7V × Current). If you use a 12V PWM signal at a 41% duty cycle, the fan receives the correct average power, and the switching MOSFET stays cool because it is never operating in a high-resistance, high-voltage-drop state simultaneously.
FAQ: Long-Tail Questions About PWM Modulation
What is the difference between PWM frequency and duty cycle?
Duty cycle determines how much power is delivered (the width of the pulse), while frequency determines how fast the pulses repeat. You can have a 50% duty cycle at 1 Hz (the signal is on for half a second, off for half a second, which makes an LED visibly flash) or a 50% duty cycle at 10,000 Hz (the signal switches every 50 microseconds, which makes an LED appear steadily dimmed to the human eye).
Why does my PWM-controlled LED flicker on camera?
This happens because of the rolling shutter effect in CMOS camera sensors and a mismatch between your PWM frequency and the camera's frame rate. Standard Arduino PWM runs at ~490 Hz. While the human eye's persistence of vision blends this into a steady glow, a camera capturing at 60fps or 120fps will catch the microsecond OFF phases, resulting in visible banding or flickering on screen. To fix this for video lighting, increase your PWM frequency to at least 20 kHz to 25 kHz, well above the camera's sampling threshold.
Can I use PWM to control a standard AC dimmer switch?
No. Standard AC wall dimmers and AC mains loads operate on alternating current (50/60 Hz sine waves). PWM is a DC (Direct Current) square-wave technique. If you attempt to switch AC mains voltage using standard DC PWM logic, you will cause catastrophic short circuits, destroy your microcontroller, and create severe fire hazards. Dimming AC loads requires entirely different techniques, such as Phase Angle Control (using TRIACs and zero-crossing detection) or burst-fire control.
How do I convert a PWM signal to a true DC analog voltage?
You can smooth a PWM square wave into a steady DC voltage by building a simple RC (Resistor-Capacitor) low-pass filter. By placing a resistor (e.g., 10kΩ) in series with the PWM output and a capacitor (e.g., 1µF) from the other side of the resistor to ground, the capacitor charges during the ON pulses and discharges slowly during the OFF pulses. The cutoff frequency of the filter must be set significantly lower than your PWM frequency to effectively eliminate the AC ripple and leave a clean, steady DC voltage proportional to the duty cycle.






