PWM Parameters
How PWM Works in Digital Logic
A free-running N-bit counter increments on every clock edge and wraps from 2N−1 back to 0. The PWM output is a simple comparator: HIGH when counter < threshold, LOW otherwise.
| Threshold (8-bit) | Duty Cycle | Vavg @ 3.3V | Use Case |
|---|---|---|---|
| 0 | 0% | 0.00V | Off |
| 32 | 12.5% | 0.41V | Dim LED |
| 64 | 25% | 0.83V | Quarter power |
| 128 | 50% | 1.65V | Half power / servo center |
| 192 | 75% | 2.48V | Three-quarter power |
| 255 | ~100% | 3.30V | Full on |
PWM Waveform Visualizer
Where PWM Is Used
| Application | PWM Frequency | Duty Cycle Effect | Key Detail |
|---|---|---|---|
| LED Brightness | 1–20kHz | Higher duty = brighter | Above ~50Hz the eye sees smooth brightness, not flicker |
| DC Motor Speed | 1–100kHz | Higher duty = faster | Inductance of motor smooths current; back-EMF must be considered |
| Servo Motor | 50Hz (20ms frame) | Pulse width → angle | 1ms = 0°, 1.5ms = 90°, 2ms = 180°; duty cycle is tiny (~5–10%) |
| DAC Approximation | High (audio: >44kHz) | Duty cycle = output level | RC low-pass filter after PWM output; ripple = supply noise |
| SMPS (Buck/Boost) | 100kHz–1MHz | Duty cycle sets Vout | Buck: Vout = D × Vin; feedback loop controls D |
| Class-D Amplifier | >400kHz | Audio waveform encoded | High efficiency (90%+) — output filter reconstructs audio |
PWM Generator in Verilog
Basic 8-bit PWM Generator
// 8-bit counter-based PWM generator
// pwm_out = HIGH when counter < duty_cycle
module pwm_gen #(
parameter N = 8 // counter bit-width
)(
input wire clk,
input wire rst_n,
input wire [N-1:0] duty, // 0=0%, 255=~100% (8-bit)
output wire pwm_out
);
reg [N-1:0] cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) cnt <= '0;
else cnt <= cnt + 1'b1; // wraps at 2^N
end
assign pwm_out = (cnt < duty);
endmodule
Variable-Frequency PWM (Period Register)
// PWM with configurable period AND duty cycle
module pwm_vf #(
parameter N = 16
)(
input wire clk,
input wire rst_n,
input wire [N-1:0] period, // clock cycles per PWM period
input wire [N-1:0] duty, // HIGH clock cycles (must be <= period)
output reg pwm_out
);
reg [N-1:0] cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= '0;
pwm_out <= 1'b0;
end else begin
if (cnt >= period - 1) cnt <= '0;
else cnt <= cnt + 1;
pwm_out <= (cnt < duty);
end
end
endmodule
Servo PWM Controller (50Hz, 1–2ms pulse)
// Servo PWM: 50Hz frame, 1ms–2ms pulse for 0°–180°
// Assumes 50MHz clock. 50Hz → 20ms period = 1_000_000 clk cycles
module servo_pwm (
input wire clk, // 50MHz
input wire rst_n,
input wire [7:0] angle, // 0–180 degrees
output reg pwm_out
);
// At 50MHz: 1ms = 50000 clk, 2ms = 100000 clk
localparam PERIOD = 1_000_000; // 20ms at 50MHz
localparam PULSE_MIN = 50_000; // 1ms
localparam PULSE_MAX = 100_000; // 2ms
reg [19:0] cnt;
reg [16:0] pulse_w;
always @(*) begin
// Linear interpolation: pulse_w = 50000 + angle*(50000/180)
pulse_w = PULSE_MIN + (angle * 277); // ~50000/180 ≈ 277
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) cnt <= 0;
else if (cnt >= PERIOD-1) cnt <= 0;
else cnt <= cnt + 1;
pwm_out <= (cnt < pulse_w);
end
endmodule
PWM Fan Controller with Temperature Lookup
// 8-bit PWM fan controller: hotter = faster fan
module fan_ctrl (
input wire clk,
input wire rst_n,
input wire [7:0] temp_c, // 0–100°C
output wire pwm_out
);
reg [7:0] duty;
reg [7:0] cnt;
always @(*) begin
if (temp_c < 40) duty = 8'd0; // fan off below 40°C
else if (temp_c < 50) duty = 8'd64; // 25% — quiet
else if (temp_c < 65) duty = 8'd128; // 50% — moderate
else if (temp_c < 80) duty = 8'd192; // 75% — warm
else duty = 8'd255; // 100% — hot
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) cnt <= 0;
else cnt <= cnt + 1;
end
assign pwm_out = (cnt < duty);
endmodule
Resolution vs PWM Frequency
For a fixed clock frequency, increasing the counter bit-width (resolution) reduces the PWM output frequency. To keep both high, you need a faster clock.
| Clock | 8-bit (256 steps) | 10-bit (1024 steps) | 16-bit (65536 steps) |
|---|---|---|---|
| 1 MHz | 3.9 kHz | 977 Hz | 15.3 Hz |
| 25 MHz | 97.7 kHz | 24.4 kHz | 381 Hz |
| 100 MHz | 390.6 kHz | 97.7 kHz | 1.53 kHz |
| 500 MHz | 1.95 MHz | 488 kHz | 7.63 kHz |
Rule of thumb: Choose bit-width based on the smoothness required by the load. For LED dimming, 8 bits is usually enough (the human eye can't distinguish more than ~256 brightness levels at typical frequencies). For precision servo or audio DAC, use 12–16 bits with a high-speed clock.
Frequently Asked Questions
What is PWM and how does it work?
PWM (Pulse Width Modulation) generates an analog-equivalent output using a digital signal that rapidly switches HIGH/LOW. The load (LED, motor) responds to the average voltage, which equals duty cycle × Vcc.
What is duty cycle?
Duty cycle = (Ton / Tperiod) × 100%. A 75% duty cycle on 3.3V gives 2.475V average. In an 8-bit PWM, setting threshold=192 out of 256 gives 75% duty.
How does counter-based PWM work?
A free-running N-bit counter increments each clock cycle and wraps at 2N. The output is HIGH when counter < threshold, LOW otherwise. Threshold = duty cycle × 2N.
What PWM frequency is needed for LEDs vs motors?
LEDs: above ~50Hz eliminates visible flicker; 1–20kHz is typical. DC motors: 1–50kHz avoids audible humming and reduces EMI. Servos: exactly 50Hz (20ms frame). SMPS: 100kHz–1MHz for compact magnetics.
How does PWM resolution relate to bit-width?
An N-bit counter gives 2N duty cycle steps. 8-bit = 256 steps (0.39% granularity). 10-bit = 1024 steps. More bits → finer control but lower PWM frequency for the same clock. Tradeoff: fpwm = fclk / 2N.
How do servos use PWM?
Servos expect a 50Hz (20ms) frame with a pulse of 1–2ms. The pulse width encodes angle: 1ms = 0°, 1.5ms = 90°, 2ms = 180°. The duty cycle is only 5–10%, but the absolute pulse time is what matters, not the percentage.
How do you write a PWM generator in Verilog?
Declare an N-bit register cnt that increments on every posedge clk. Assign pwm_out = (cnt < duty). That's it — the counter wraps naturally, creating the period; the comparator creates the pulse width.