Raspi PWM (Pulse Width Modulation) is a technique that rapidly switches a 3.3V digital GPIO pin on and off to simulate a variable analog voltage, allowing you to control motor speed, LED brightness, or servo angles. In a real circuit, PWM changes a binary logic output (strictly 0V or 3.3V) into a time-averaged effective voltage that analog components can react to, effectively bridging the gap between digital microcontrollers and analog actuators.
The most common point of failure for beginners is confusing Hardware PWM (handled by a dedicated silicon timer, perfectly stable) with Software PWM (handled by the Linux OS scheduler, prone to microsecond jitter). If you have ever wired a servo to a Raspberry Pi and watched it twitch violently instead of holding a steady angle, you have experienced the difference between the two.
The Core Concept: Digital Pins Faking Analog Voltage
A Raspberry Pi GPIO pin cannot output 1.5V or 2.2V directly; it is a digital pin that snaps between 0V (LOW) and 3.3V (HIGH). To dim an LED or slow a motor, we use PWM to trick the component. Think of it like flicking a light switch on and off so fast that the human eye perceives a dimmer room rather than a strobe light. By adjusting the ratio of ON time to OFF time (the duty cycle), we control the average power delivered to the load.
Example: A 3.3V pin at a 50% duty cycle delivers an average of 1.65V.
However, the frequency (how many times per second the pin cycles) matters just as much as the duty cycle. An LED needs a frequency above 60Hz to avoid visible flicker. A standard RC servo requires exactly 50Hz to interpret the pulse width as a physical angle. This is where the Pi's operating system gets in the way.
The Big Trap: Hardware PWM vs. Software PWM
The Raspberry Pi runs Linux, a multitasking operating system. When you use a basic library like the legacy RPi.GPIO to create a PWM signal, the library asks the Linux kernel to toggle the pin via software timers. If the CPU suddenly pauses your Python script to handle a background network interrupt or a USB polling event, the PWM pulse stretches or shrinks. This timing error is called jitter.
Hardware PWM bypasses the OS entirely. The Pi's silicon (the BCM2711 on Pi 4, or the RP1 southbridge on Pi 5) has dedicated clock circuits that toggle specific pins autonomously. Once your code tells the hardware timer to run at 50Hz with a 7.5% duty cycle, the silicon handles it perfectly, even if your Python script crashes or the CPU hits 100% load.
On both Raspberry Pi 4 and Raspberry Pi 5, true hardware PWM is available on GPIO 12, 13, 18, and 19. If you are driving a servo or a precision DAC circuit, you must use one of these four pins.
Worked Example: Driving a 50Hz Servo Without Jitter
Let's look at the exact math for controlling a standard hobby servo (like the SG90 or MG996R) using raspi PWM. Servos do not use duty cycle to determine speed; they use the absolute width of the ON pulse within a fixed 20ms window (50Hz frequency).
- Frequency: 50Hz (Period = 1 / 50 = 0.02 seconds, or 20ms)
- 0° Position: 1.0ms pulse width
- 90° Position (Center): 1.5ms pulse width
- 180° Position: 2.0ms pulse width
To find the required duty cycle for the center position, we divide the pulse width by the total period:
Therefore, a 7.5% duty cycle at 50Hz commands the servo to 90 degrees.
Note: Many cheap servos drift. You may need to tune this to 7.2% or 7.8% in code to achieve a perfect mechanical center.
If you attempt this 7.5% duty cycle using software PWM on GPIO 17, Linux scheduling jitter might stretch that 1.5ms pulse to 1.7ms randomly. The servo interprets 1.7ms as a command to move to 126 degrees, resulting in the violent twitching you see on the bench. Moving the wire to GPIO 18 (Hardware PWM) eliminates the jitter instantly.
Where You Meet Raspi PWM in Practice
You will reach for PWM in almost every physical computing project that interacts with the real world. Here is where it shows up on the workbench:
- DC Motor Speed Control: Feeding a PWM signal into the EN (Enable) pin of an H-bridge motor driver like the TB6612FNG or L298N to control robot wheel speed.
- High-Power LED Dimming: Switching a logic-level MOSFET (like the IRLZ44N) at 1kHz to dim 12V LED strips without the color shift that occurs when using analog voltage reduction.
- Servo Gimbals and Robotics: Positioning camera mounts or robotic arms using the 50Hz pulse-width standard.
- Fake Analog Outputs: Passing a high-frequency PWM signal through a simple RC low-pass filter (resistor and capacitor) to create a true DC analog voltage for driving vintage analog synthesizers or acting as a crude DAC.
Decision Tree: Which Pin and Library to Pick
Do not guess which setup to use. Follow this decision path to select the correct pin, library, and hardware interface for your specific 2026 Raspberry Pi build.
| Application | PWM Type Required | Recommended GPIO Pins | Best Python Library |
|---|---|---|---|
| Dimming LEDs (Visual) | Software PWM (Jitter is invisible to human eye) | Any GPIO (e.g., 17, 27, 22) | gpiozero (Default) |
| DC Motor Speed (H-Bridge) | Software or Hardware (Motors have high inertia, absorb jitter) | Any GPIO | gpiozero |
| RC Servos / Steppers | Hardware PWM Mandatory (Jitter causes mechanical failure) | 12, 13, 18, or 19 | gpiozero with pigpio pin factory |
| RC Low-Pass Filter (Fake DAC) | Hardware PWM Mandatory (Jitter creates analog noise) | 12, 13, 18, or 19 | pigpio (Direct C-library binding) |
The 3.3V vs 5V Logic Hazard
A critical hardware reality: The Raspberry Pi outputs 3.3V logic. Most standard hobby servos expect a 5V logic pulse. While many 5V servos will partially trigger at 3.3V, it is out of spec and can lead to missed pulses. Worse, if you power the servo from the Pi's 5V rail and the servo backfeeds voltage into the signal line, you can fry the Pi's RP1 or BCM chip.
The Fix: Always use a bidirectional logic level shifter (like the TXS0108E module, roughly $2 on Adafruit or Amazon) between the Pi's GPIO 18 and the servo signal wire. Power the servo from a dedicated 5V buck converter or USB power supply, sharing only the Ground (GND) wire with the Pi.
Concrete Implementation: Jitter-Free Servo Code
The legacy RPi.GPIO library is effectively deprecated for Raspberry Pi 5 due to the RP1 chip architecture changes. The modern, robust standard is gpiozero paired with the pigpio daemon to force hardware timing. For deeper timer mechanics, refer to the All About Circuits PWM guide.
from gpiozero import Servo
from gpiozero.pins.pigpio import PiGPIOFactory
# Force gpiozero to use the pigpio hardware-timed backend
factory = PiGPIOFactory()
# GPIO 18 is a dedicated Hardware PWM pin
servo = Servo(18, pin_factory=factory, min_pulse_width=0.001, max_pulse_width=0.002)
# Move to center (7.5% duty cycle equivalent)
servo.mid()
Before running this, ensure the pigpio daemon is active via terminal: sudo systemctl enable pigpiod && sudo systemctl start pigpiod. You can find more on modern Pi GPIO configurations in the official Raspberry Pi hardware documentation.
Final Verdict: The Default Pick
If you are wiring up a servo, a stepper driver, or an analog filter circuit, do not waste time testing software PWM on random pins. Wire your signal to GPIO 18, install the pigpio daemon, and use the PiGPIOFactory in your gpiozero script. This combination guarantees silicon-level timing precision, eliminates OS-induced jitter, and protects your mechanical components from erratic voltage spikes. For simple LED fading where the human eye cannot perceive microsecond timing errors, stick to standard software PWM on any pin using default gpiozero.






