In the previous article, we learned how to turn an LED on and off using a Raspberry Pi GPIO pin. We wrote our first Pi4J application, explored digital outputs, and made an LED blink.
But what if we want something in between?
Can an LED be half on?
Can we create smooth fading effects?
Can we control motor speed, fan speed, or even generate analog-like voltages using only digital GPIO pins?
The answer is PWM.
What Is PWM?
PWM stands for Pulse Width Modulation.
Despite the intimidating name, the idea is surprisingly simple:
Instead of changing the voltage, we rapidly switch the GPIO pin between ON and OFF.
If we do this fast enough, the LED appears dimmer or brighter depending on how long it stays ON during each cycle.
The GPIO Problem
A GPIO pin is digital.
It only knows two states:
LOW = 0V
HIGH = 3.3V
There is no built-in way to output:
1.65V
2.1V
0.8V
At least not directly.
So how do we create different brightness levels?
The PWM Trick
Imagine a GPIO pin switching on and off repeatedly.
100% Duty Cycle
████████████████
The signal is always ON.
The LED is fully bright.
50% Duty Cycle
████_████_
The signal spends half of its time ON and half OFF.
The LED appears approximately half as bright.
25% Duty Cycle
██___██___
The LED appears much dimmer.
Duty Cycle
The most important PWM concept is the duty cycle.
It represents the percentage of time the signal remains ON during a complete cycle.
Examples:
Mathematically:
Duty Cycle = ON Time / Total Period × 100%
Frequency Matters
PWM is defined by two parameters:
- Duty Cycle
- Frequency
Frequency tells us how many complete cycles occur every second.
For example:
100 Hz
means:
100 cycles per second
If the frequency is too low, visible flickering may occur.
If the frequency is high enough, the LED appears continuously illuminated.
A typical LED application often uses frequencies ranging from a few hundred Hertz to several kilohertz.
Frequency vs Resolution
There is often a trade-off between PWM frequency and PWM resolution.
In many systems:
Higher Frequency
↓
Lower Resolution
or
Higher Resolution
↓
Lower Maximum Frequency
This happens because the hardware has a limited amount of time to represent each PWM cycle.
As frequency increases, fewer timing steps are available.
Understanding this trade-off becomes important in more advanced projects.
Hardware PWM vs Software PWM
Not all PWM signals are generated the same way.
Software PWM
CPU
↓
GPIO
The CPU continuously generates the pulses.
Advantages
- Works on many GPIO pins
- Easy to configure
- Flexible for simple projects
Disadvantages
- Consumes CPU resources
- Less precise timing
- Accuracy may decrease at higher frequencies or under heavy system load
Hardware PWM
PWM Controller
↓
GPIO
Dedicated hardware generates the signal.
Advantages
- Extremely accurate timing
- Minimal CPU usage
- Stable frequencies and duty cycles
- Ideal for LEDs, motors, audio generation, and other timing-sensitive applications
Disadvantages
- Available only on specific GPIO pins
- Limited number of hardware PWM channels
Key Difference
The biggest difference is where the PWM signal is generated:

Table 02: Software VS Hardware PWM
For most LED projects, both approaches work well. However, when accuracy, efficiency, or higher frequencies matter, Hardware PWM is usually the preferred choice.
Building the Circuit
The circuit is identical to the one used in the Blink article.
GPIO18
|
Resistor
|
LED
|
GND
GPIO18 is a popular choice because it supports hardware PWM on Raspberry Pi.
PWM with Pi4J
Software PWM
Software PWM works on virtually any GPIO configured as an output.
import com.pi4j.Pi4J;
import com.pi4j.context.Context;
import com.pi4j.io.pwm.Pwm;
import com.pi4j.io.pwm.PwmType;
public class SoftwarePwmExample {
public static void main(String[] args) throws Exception {
Context pi4j = Pi4J.newAutoContext();
var pwmConfig = Pwm.newConfigBuilder(pi4j)
.id("soft-pwm")
.name("Software PWM LED")
.address(18) // BCM GPIO18
.pwmType(PwmType.SOFTWARE)
.provider("pigpio-pwm")
.initial(0)
.shutdown(0)
.build();
Pwm pwm = pi4j.create(pwmConfig);
for (int duty = 0; duty <= 100; duty++) {
pwm.on(duty, 1000);
Thread.sleep(20);
}
pwm.off();
pi4j.shutdown();
}
}
What Happens Behind the Scenes?
Java Application
↓
CPU generates PWM pulses
↓
GPIO18
In Software PWM, the CPU is responsible for continuously toggling the GPIO pin on and off according to the configured duty cycle and frequency.
Hardware PWM
Hardware PWM uses the Raspberry Pi's dedicated PWM controller.
import com.pi4j.Pi4J;
import com.pi4j.context.Context;
import com.pi4j.io.pwm.Pwm;
import com.pi4j.io.pwm.PwmType;
public class HardwarePwmExample {
public static void main(String[] args) throws Exception {
Context pi4j = Pi4J.newAutoContext();
var pwmConfig = Pwm.newConfigBuilder(pi4j)
.id("hw-pwm")
.name("Hardware PWM LED")
.address(18)
.pwmType(PwmType.HARDWARE)
.provider("pigpio-pwm")
.initial(0)
.shutdown(0)
.build();
Pwm pwm = pi4j.create(pwmConfig);
pwm.on(50, 1000);
Thread.sleep(5000);
pwm.off();
pi4j.shutdown();
}
}
What Happens Behind the Scenes?
Java Application
↓
PWM Hardware Controller
↓
GPIO18
In Hardware PWM, the Raspberry Pi's PWM peripheral generates the signal independently of the CPU.
Once configured, the hardware continues producing precise PWM pulses even if the CPU is busy performing other tasks.
First PWM Example
Let's set the LED to 50% brightness.
pwm.on(50, 1000);
This means:
50% Duty Cycle
1000 Hz Frequency
The LED now appears partially illuminated.
Creating a Fade Effect
One of the most satisfying PWM demonstrations is a smooth fade.
for (int duty = 0; duty <= 100; duty++) {
pwm.on(duty, 1000);
Thread.sleep(20);
}
for (int duty = 100; duty >= 0; duty--) {
pwm.on(duty, 1000);
Thread.sleep(20);
}
The LED gradually brightens and then fades away.
This effect is often called a breathing LED.
PWM Doesn't Control Brightness. It Controls Energy.
This is a subtle but important distinction.
PWM doesn't directly tell an LED to become dimmer.
Instead, it controls how much energy is delivered to the load over time.
For an LED, that energy becomes perceived brightness.
For a DC motor, it becomes speed.
For a heating element, it becomes temperature.
The same PWM technique is used in all three cases.
Only the result changes.
Every Dimmed LED Is Actually Blinking
This surprises many beginners.
A dim LED is not receiving "half voltage."
It is actually turning completely ON and completely OFF hundreds or thousands of times per second.
When you see a LED at 50% brightness, what is really happening is:
ON
OFF
ON
OFF
ON
OFF
Very fast.
So a more accurate statement would be:
Every dimmed LED is actually blinking. It's just blinking too fast for us to notice.
The LED Is Never Half On
This is one of the most common misconceptions.
The apparent brightness comes from how our eyes perceive the rapid switching.
Persistence of Vision
Human vision is not instantaneous.
Our eyes and brains effectively average fast changes in light.
If the LED switches thousands of times per second, we no longer perceive individual flashes.
Instead, we see a stable brightness level.
This phenomenon is called persistence of vision, and it is one of the reasons PWM works so well for LEDs.
PWM Is a Useful Lie
One of the most interesting ways to think about PWM is as a "useful lie."
The Raspberry Pi never outputs intermediate voltages through PWM.
It only outputs:
0V
or
3.3V
Yet both humans and electronic circuits often behave as if intermediate voltages existed.
PWM tricks our eyes.
PWM tricks filters.
PWM tricks motors.
And that makes it incredibly useful.
Understanding Raspberry Pi GPIO Limitations
When working with PWM on a Raspberry Pi, it's important to remember that GPIO pins were never designed to be power outputs.
They are designed to be control signals.
This distinction explains many of the limitations developers encounter when starting with electronics.
GPIO Pins Are Not Power Supplies
A Raspberry Pi GPIO pin can output:
LOW = 0V
HIGH = 3.3V
However, that does not mean you can power arbitrary devices directly from a GPIO pin.
GPIO pins are intended to provide signals, not significant amounts of current.
Typical use cases include:
- LEDs (with resistors)
- Logic inputs
- Sensors
- Transistors
- Driver circuits
Not:
- Motors
- High-power LEDs
- Relays
- Speakers
- Servos
These devices usually require external power and dedicated drivers.
GPIO Pins Operate at 3.3V
Unlike some microcontroller boards, Raspberry Pi GPIOs are not 5V tolerant.
Applying 5V directly to a GPIO pin can permanently damage the processor.
Always verify:
Input Voltage ≤ 3.3V
when connecting external electronics.
Current Limitations
A GPIO pin can source or sink only a limited amount of current.
A common rule of thumb is:
≈16mA per GPIO
≈50mA total across all GPIOs
Always check the latest Raspberry Pi documentation for the exact specifications of your model.
This is why LEDs should always use a resistor.
For example:
GPIO
|
220Ω
|
LED
|
GND
The resistor protects both the LED and the GPIO pin.
PWM Limitations on Raspberry Pi
PWM is powerful, but it has limitations.
Understanding them helps explain why some projects work perfectly while others require additional hardware.
PWM Is Still Digital
This is the most important limitation.
PWM is not true analog output.
Even at 50% duty cycle, the signal remains:
0V
or
3.3V
only.
PWM merely changes the timing.
Some devices are perfectly happy with this.
Others require actual analog voltages.
Not Every GPIO Supports Hardware PWM
One of the most common surprises for beginners.
Many GPIO pins can be used for:
Digital Input
Digital Output
but only a subset support hardware PWM.
For example, GPIO18 is commonly used because it supports hardware PWM functionality.
This means:
GPIO18 ✓
Random GPIO ? Maybe not
Depending on the PWM provider and Raspberry Pi model, available PWM pins may vary.
Limited Hardware PWM Channels
The Raspberry Pi contains a small number of hardware PWM generators.
Conceptually:
PWM Generator #1
PWM Generator #2
Multiple GPIOs may share these generators.
As a result, changing one PWM configuration can sometimes affect another pin using the same channel.
This limitation becomes important in projects involving:
- Multiple motors
- RGB LED strips
- Audio generation
- Robotics
PWM Cannot Source Significant Power
A common misconception:
PWM = More Power
Not really.
PWM changes average power delivered to a load.
The GPIO pin itself still has the same current limitations.
For example:
GPIO PWM
|
Motor
is generally a bad idea.
Instead:
GPIO PWM
|
Transistor / MOSFET
|
Motor Power Supply
This allows PWM to control the motor while external circuitry handles the actual power.
Why PWM Is So Efficient
Before PWM became popular, many systems controlled power by wasting energy as heat.
Imagine trying to dim an LED using a variable resistor.
Power
↓
Resistor
↓
Heat
A portion of the energy is simply lost.
PWM works differently.
Power
↓
Load
The signal is either fully ON or fully OFF.
Very little energy is wasted.
This is one of the reasons PWM is found in:
- Electric vehicles
- Drones
- Industrial automation
- LED lighting systems
- Computer cooling fans
- Switching power supplies
Efficiency is one of PWM's greatest strengths.
PWM Is Everywhere
PWM is far more important than controlling LEDs.
The same technique is used in:
- Computer cooling fans
- DC motor speed control
- Servo motors
- LED lighting systems
- Audio synthesis
- Switching power supplies
- Industrial automation equipment
Once you understand PWM, you begin seeing it everywhere.
Experiment Time
Try connecting a multimeter to the filtered output.
Set the duty cycle to:
0%
25%
50%
75%
100%
Measure the voltage at each step.
You should observe values close to:
0.0V
0.8V
1.65V
2.5V
3.3V
This simple experiment demonstrates one of the most powerful ideas in electronics:
A digital signal can behave like an analog one.
The Smartphone Camera Experiment
Want to prove that the LED is actually blinking?
Point your smartphone camera at it.
Depending on the PWM frequency, you may see:
- Flickering
- Rolling bands
- Horizontal stripes
The camera sensor often captures the rapid switching that our eyes cannot detect.
It's a simple experiment, but it makes PWM feel much more real.
A Fun Experiment
Try running exactly the same fade effect using both Software PWM and Hardware PWM.
for (int duty = 0; duty <= 100; duty++) {
pwm.on(duty, 1000);
Thread.sleep(20);
}
First, configure the PWM instance as:
.pwmType(PwmType.SOFTWARE)
Then run the same code again using:
.pwmType(PwmType.HARDWARE)
At first glance, the LED will appear to behave exactly the same. Both versions will smoothly fade from dark to bright.
So what's the difference?
The answer is hidden behind the scenes.
The magic happens behind the scenes. In Software PWM, Java and the CPU generate every pulse. In Hardware PWM, a dedicated Raspberry Pi peripheral takes over the job and continues generating the signal even while your application is busy doing something else.
This distinction may not be visible when fading a single LED, but it becomes important in more demanding applications such as motor control, audio generation, robotics, or any project that requires precise timing.
A useful way to think about it is:
Software PWM
Java
↓
CPU
↓
GPIO
versus
Hardware PWM
Java
↓
PWM Controller
↓
GPIO
In the first case, the CPU is actively creating every pulse. In the second, the CPU simply configures the PWM controller and lets the hardware handle the rest.
For a simple LED project, both approaches work great. But understanding this difference is an important step toward understanding how embedded systems manage timing, performance, and hardware resources.
Final Thoughts
Blink taught us how to control a GPIO pin.
PWM teaches us how to control perception.
By rapidly switching a digital signal on and off, we can create smooth brightness transitions, control motors, regulate power, and even generate analog-like voltages.
What started as a blinking LED turns out to be one of the most important techniques in modern electronics.
And this is only the beginning.
Links
https://osteele.github.io/pwm-explorer/
https://en.wikipedia.org/wiki/Pulse-width_modulation





Top comments (0)