PWM control is a technique that varies the average power delivered to a load by rapidly switching a digital signal on and off at a fixed frequency while changing the ratio of on-time to off-time. Unlike a linear regulator or a potentiometer that burns excess voltage as waste heat, PWM changes the effective voltage by chopping the power rail, keeping your microcontroller and switching components cool. What people most commonly confuse PWM with is a true Digital-to-Analog Converter (DAC). A DAC outputs a steady, continuous voltage, whereas PWM is strictly a digital square wave. If you feed raw PWM into an audio amplifier without a low-pass RC filter, you will get harsh switching noise, not a clean analog waveform.

What PWM Control Actually Does to Your Circuit

When you apply PWM to a circuit, you are not actually lowering the voltage. You are delivering the full supply voltage in rapid bursts. The load's physical properties—like the thermal mass of a heating element, the persistence of human vision for an LED, or the mechanical inertia of a motor rotor—act as a natural low-pass filter, smoothing those pulses into an 'average' effect.

The Core Difference: If you use a resistor to drop a 12V supply to 6V for a 1A motor, the resistor dissipates 6W of heat. If you use a MOSFET driven by a 50% duty cycle PWM signal, the MOSFET is either fully on (near-zero resistance) or fully off (infinite resistance). The average voltage is 6V, but the switching component dissipates less than 0.1W of heat.

The Math: Duty Cycle, Frequency, and a Worked Example

To use PWM effectively, you need to calculate both your duty cycle (the percentage of time the signal is HIGH) and your timer resolution (the number of discrete steps your microcontroller can divide the period into).

Let's look at a real-world scenario: You are driving a 12V, 2A DC cooling fan using an ESP32 and an IRLZ44N logic-level MOSFET. You want the fan to run at roughly 60% speed.

  1. Average Voltage: 12V × 0.60 (duty cycle) = 7.2V average.
  2. Frequency Selection: We choose 25,000 Hz (25 kHz). This is above the upper limit of human hearing (20 kHz), preventing the fan coils from whining audibly.
  3. Resolution Selection: The ESP32's LEDC peripheral allows us to set the bit depth. We choose 10-bit resolution, which gives us 2^10 = 1024 discrete steps (0 to 1023).
  4. Duty Value Calculation: 1023 × 0.60 = 614.

In your ESP-IDF or Arduino code, you don't pass '60%' to the hardware. You pass the integer 614 to the duty register. The hardware timer handles the exact microsecond toggling in the background, freeing your CPU to handle Wi-Fi or sensor readings. For exact API implementation details, refer to the official Espressif LEDC documentation.

Where You Meet PWM Control in Practice

The 'correct' PWM settings change drastically depending on the physical load you are driving. Using the wrong frequency can result in flickering lights, screaming motors, or burnt-out MOSFETs.

  • LED Dimming: Use 1 kHz to 5 kHz. While LEDs will technically stop flickering to the human eye around 100 Hz, smartphone cameras and machine vision sensors will detect severe banding at low frequencies. 5 kHz ensures flicker-free video recording.
  • DC Motors: Use 16 kHz to 25 kHz. Motors are inductive loads. If you drive them at 1 kHz, the magnetic coils will physically vibrate at that frequency, creating an annoying audible whine. Pushing it past 20 kHz keeps it silent.
  • RC Servos: Use exactly 50 Hz. Standard hobby servos do not care about duty cycle percentage; they measure the absolute width of the HIGH pulse. A 50 Hz signal has a 20 ms period. A 1 ms pulse means 0 degrees, 1.5 ms means 90 degrees, and 2 ms means 180 degrees.
  • PID Heater Control: Use 1 Hz to 10 Hz. Heating elements have massive thermal inertia. Switching a 10A solid-state relay at 20 kHz wastes energy in switching losses and generates EMI. Switching it once per second (1 Hz) is perfectly smooth for a thermal mass.

Hardware vs. Software PWM: The Decision Tree

Microcontrollers can generate PWM via dedicated hardware timers or by bit-banging a GPIO pin in software. Here is how to decide which to use, terminating in a concrete setup.

Load RequirementSoftware PWM (Bit-Banging)Hardware PWM (Timers)Concrete Pick / Action
Jitter Tolerance Fails if interrupts occur (Wi-Fi, I2C) Zero jitter, runs independently Pick Hardware for any load sensitive to timing (Servos, LEDs).
Frequency Needed Maxes out around 1-2 kHz reliably Can reach 40+ MHz on modern MCUs Pick Hardware for DC motors requiring >16 kHz.
Pin Availability Any GPIO pin works Restricted to specific timer-mapped pins (e.g., Arduino Uno) Pick Software ONLY for slow loads (Heaters) on arbitrary pins.
Resolution Needed Limited by CPU clock and loop overhead Up to 20-bit on ESP32, 16-bit on RP2040 Pick Hardware for smooth, high-res LED fading.
Pro-Tip for Arduino Uno Users: The standard analogWrite() function uses hardware PWM, but only on pins 3, 5, 6, 9, 10, and 11. Furthermore, pins 5 and 6 default to 980 Hz, while the others default to 490 Hz. If you need a specific frequency, you must manually configure the AVR timer registers (TCCR1B, etc.). See the Arduino analogWrite reference for pin-specific timer mappings.

Default Recommendations and Common Mistakes

If you are prototyping a new circuit and don't want to get bogged down in datasheet calculations, use this default baseline: Hardware PWM at 5 kHz with 10-bit resolution. This frequency is high enough to prevent LED camera flicker, low enough to avoid excessive MOSFET switching losses, and 10-bit provides 1024 steps for ultra-smooth fading.

Why is my MOSFET getting incredibly hot?

You are likely using a standard MOSFET (like the IRF520) instead of a logic-level MOSFET (like the IRLZ44N or AO3400). Standard MOSFETs require 10V+ on the gate to fully turn on. An ESP32 or Arduino only outputs 3.3V or 5V. At 3.3V, a standard MOSFET operates in its linear (partially on) region, acting like a resistor and burning up. Always check the datasheet for the RDS(on) specification at VGS = 4.5V or 2.5V.

Why does my DC motor whine loudly at low speeds?

Your PWM frequency is in the audible range (typically 490 Hz or 1 kHz). The motor's stator coils are physically vibrating at that frequency. Increase your hardware timer frequency to at least 18 kHz to push the noise above human hearing. For Raspberry Pi Pico users, the Pico SDK PWM documentation details how to set the slice wrap and clock divider to achieve ultrasonic frequencies.

Do I need a flyback diode for my PWM circuit?

Yes, if your load is inductive (motors, solenoids, relays). When the MOSFET switches off, the collapsing magnetic field in the motor generates a massive reverse voltage spike that will instantly punch through your MOSFET's drain-source junction. Place a Schottky diode (like a 1N5819) in reverse bias across the motor terminals to safely route that spike back into the power rail.

Stop guessing your timer values. Match your frequency to the physical nature of your load, use hardware timers to eliminate CPU jitter, and always pair your microcontroller's 3.3V logic with a proper logic-level gate driver or MOSFET.