A PWM (Pulse Width Modulation) signal generator is a hardware or software mechanism that outputs a digital square wave with a configurable duty cycle to simulate variable analog power delivery. In a real circuit or installation, it changes the average power delivered to a load without altering the actual supply voltage or wasting energy as heat, which is exactly what happens if you try to use a linear resistor or voltage divider to drop voltage. By rapidly switching a MOSFET or transistor fully ON and fully OFF, a PWM signal generator achieves high-efficiency control over motor speeds, LED brightness, and switching power supplies.
Hardware vs. Software PWM Signal Generator Specs
When configuring a microcontroller, you must decide between a hardware PWM signal generator and a software (bit-banged) fallback. Hardware PWM relies on dedicated internal timers that toggle GPIO pins automatically, freeing the CPU to handle other tasks like WiFi stacks or sensor polling. Software PWM uses CPU interrupts or delay loops to toggle pins manually; it is flexible with pin assignments but suffers from jitter and consumes heavy CPU cycles.
Below is a comparison of the hardware PWM capabilities across the most common embedded development boards used in 2026. Note that maximum frequency is inversely proportional to resolution: pushing a timer to its highest frequency forces you to drop the bit-depth of the duty cycle.
| Microcontroller | PWM Peripheral | Max Hardware Channels | Base Clock / Max Freq | Max Resolution |
|---|---|---|---|---|
| ATmega328P (Arduino Uno) | Timer0, Timer1, Timer2 | 6 | 16 MHz / ~62.5 kHz | 10-bit (Timer1) |
| ESP32-WROOM-32 | LEDC (LED Control) | 16 | 80 MHz / 40 MHz | 20-bit |
| RP2040 (Raspberry Pi Pico) | PWM Slices | 16 (8 slices, 2 ch each) | 125 MHz / ~62.5 MHz | 16-bit |
| STM32F103C8T6 (Blue Pill) | Advanced/General Timers | 15 | 72 MHz / ~36 MHz | 16-bit |
Sources: Espressif LEDC API Documentation, Raspberry Pi RP2040 Datasheet.
millis() and delay() functions. If you change the PWM frequency on pins 5 or 6 (which use Timer0) via direct register manipulation, you will break all Arduino timing functions. Always use Timer1 (pins 9 and 10) or Timer2 (pins 3 and 11) for custom PWM frequencies on 8-bit AVRs.
The Math: A Worked Numeric Example
Let’s calculate the exact register values needed to drive a 12V DC brushless cooling fan at roughly 60% speed using an ESP32 and a logic-level N-channel MOSFET (like the IRLZ44N). We want to avoid the audible whine that cheaper fans produce at lower frequencies, so we will target a PWM frequency of 25 kHz, which sits above the upper limit of typical human hearing.
The Parameters:
- Supply Voltage ($V_{supply}$): 12V DC
- Target Average Voltage ($V_{avg}$): 7.2V (which is 60% of 12V)
- Target Frequency: 25,000 Hz
- Resolution: 10-bit (Maximum value = $2^{10} - 1 = 1023$)
The Calculation:
The formula for average voltage is $V_{avg} = V_{supply} \times (Duty Cycle / Max Resolution)$. Rearranging to solve for the Duty Cycle value:
$Duty = (V_{avg} / V_{supply}) \times Max Resolution$
$Duty = (7.2 / 12) \times 1023$
$Duty = 0.6 \times 1023 = 613.8$
We round to the nearest integer: 614. Writing a value of 614 to a 10-bit PWM channel yields a 60.01% duty cycle.
The Code (ESP32 Arduino Core):
// ESP32 LEDC PWM Setup for 12V Fan Control
const int fanPin = 18;
const int pwmChannel = 0;
const int pwmFreq = 25000; // 25 kHz
const int pwmResolution = 10; // 10-bit (0-1023)
void setup() {
// Configure the PWM signal generator channel
ledcSetup(pwmChannel, pwmFreq, pwmResolution);
// Attach the GPIO pin to the channel
ledcAttachPin(fanPin, pwmChannel);
// Write the calculated duty cycle (60%)
ledcWrite(pwmChannel, 614);
}
void loop() {
// Fan runs continuously at 60%
}
Reference: Arduino analogWrite() and PWM basics.
Where You Meet PWM Signal Generators in Practice
You will encounter PWM signal generators across three primary domains in embedded electronics, each with strict frequency and duty cycle requirements.
1. Motor Control and RC ESCs
Standard hobby Electronic Speed Controllers (ESCs) for brushless drones and RC cars do not use standard percentage-based duty cycles. Instead, they expect a very specific 50 Hz PWM signal (a 20 ms period). Within that 20 ms window, the pulse width dictates the throttle:
- 1.0 ms pulse: 0% throttle (motor off)
- 1.5 ms pulse: 50% throttle (midpoint)
- 2.0 ms pulse: 100% throttle (full speed)
If you feed an ESC a 25 kHz signal meant for a DC fan, the ESC will simply ignore it or throw a calibration error.
2. LED Dimming and Display Backlights
When dimming high-power LEDs via a buck converter or direct MOSFET switching, frequency selection is critical to avoid the "flicker effect." While the human eye fuses flicker around 90 Hz, smartphone cameras and machine vision systems operate with rolling shutters that will capture severe banding at anything under 1 kHz. For video-grade LED lighting, configure your PWM signal generator to a minimum of 2 kHz to 5 kHz.
3. Switch-Mode Power Supplies (SMPS)
In custom DC-DC buck or boost converters, the microcontroller’s PWM signal generator replaces the dedicated analog control IC. The microcontroller reads the output voltage via an ADC, runs a PID control loop, and adjusts the PWM duty cycle on the fly to maintain a stable output. These applications demand high-frequency PWM (100 kHz to 500 kHz) to keep the physical size of the inductors and capacitors small.
Common Confusions: PWM vs. True Analog and PFM
Because a standard digital multimeter (DMM) in DC voltage mode averages the incoming signal, it creates a dangerous illusion for beginners. If you probe a 12V PWM line running at a 50% duty cycle, your multimeter will read 6.0V DC. This leads to the first major confusion:
The second common confusion is mixing up PWM (Pulse Width Modulation) with PFM (Pulse Frequency Modulation).
- PWM: The frequency remains strictly constant (e.g., exactly 50 kHz), and the width of the ON pulse changes to regulate power.
- PFM: The width of the ON pulse remains constant, but the frequency changes. PFM is heavily used in modern "burst mode" or "eco mode" switching regulators. When the load is very light, the regulator stops switching entirely and only fires single, fixed-width pulses when the output voltage drops below a threshold. This drastically reduces the quiescent current drawn by the controller IC at light loads, but introduces variable frequency noise that is much harder to filter out than fixed-frequency PWM.
Frequently Asked Questions
Can I use any GPIO pin for hardware PWM?
No. On the Arduino Uno, only pins 3, 5, 6, 9, 10, and 11 are tied to hardware timers. On the ESP32, the LEDC peripheral can be routed to almost any output-capable GPIO via the internal matrix, but on the RP2040, specific pins are tied to specific PWM slices (e.g., GPIO 0 and 1 share Slice 0). Always check the pinout diagram for your specific board variant.
Why does my motor squeal when using software PWM?
Software PWM relies on CPU interrupts. If your code executes a blocking function (like reading a slow I2C sensor or driving NeoPixels), the CPU misses the interrupt window, causing severe timing jitter. The motor coils vibrate at these jitter frequencies, resulting in an audible squeal. Always use hardware PWM for inductive loads.






