Activity 4.2.3 — Pulse Width Modulation (PWM)¶
Learning Objectives¶
By the end of this lesson, students will be able to:
- Explain what PWM is and how it controls average power
- Calculate duty cycle and understand its relationship to output power
- Use analogWrite() to control LED brightness
- Control DC motor speed using PWM
- Position servo motors using the Servo library
Vocabulary¶
Vocabulary (click to expand)
| Term | Definition |
|---|---|
| PWM | Pulse Width Modulation - rapidly switching a signal on and off |
| Duty Cycle | Percentage of time a PWM signal is HIGH |
| Frequency | How often the PWM cycle repeats (Hz) |
| analogWrite() | Arduino function to output PWM signal |
| Servo Motor | A motor with precise angular position control |
Part 1: What is PWM?¶
Definition¶
Pulse Width Modulation (PWM) is a technique that rapidly turns a signal on and off to control the average amount of power delivered to a device.
How It Works¶
Instead of varying the voltage, PWM switches between fully ON and fully OFF very quickly:
100% Duty Cycle (Always ON):
────────────────────────
│ │
│ │
──────────┘ ┘
50% Duty Cycle (Half ON, Half OFF):
┌─────────┐ ┌─────────┐
│ │ │ │
│ │ │ │
──────────┘ └─────────────┘ └────
25% Duty Cycle (Mostly OFF):
┌──────┐ ┌──────┐
│ │ │ │
│ │ │ │
──────────┘ └─────────────────────┘ └────
0% Duty Cycle (Always OFF):
────────────────────────────────
│ │
│ │
────────────────────────────────────────
Key Terms¶
| Term | Definition |
|---|---|
| Period | Total time for one PWM cycle (frequency = 1/period) |
| Pulse Width | Time the signal is HIGH during each cycle |
| Duty Cycle | (Pulse Width / Period) × 100% |
Calculating Duty Cycle¶
If a PWM signal is HIGH for 2ms and LOW for 2ms (period = 4ms):
Duty Cycle = (2ms / 4ms) × 100% = 50%
Why PWM Works¶
Our eyes cannot see flickering at high frequencies. The eye averages the light, seeing: - 0% duty cycle = OFF (0V average) - 50% duty cycle = DIM (2.5V average) - 100% duty cycle = FULL (5V average)
Part 2: PWM on Arduino¶
PWM Pins¶
Not all Arduino pins support PWM. Only pins marked with ~ can output PWM:
- Arduino Uno: Pins 3, 5, 6, 9, 10, 11
- These pins are controlled by timer registers
analogWrite() Function¶
Arduino provides analogWrite() to output PWM:
- pin: PWM-capable pin number
- value: 0 to 255 (0 = always off, 255 = always on)
| Value | Duty Cycle | Average Voltage |
|---|---|---|
| 0 | 0% | 0V |
| 64 | 25% | 1.25V |
| 128 | 50% | 2.5V |
| 191 | 75% | 3.75V |
| 255 | 100% | 5V |
Default PWM Frequency¶
Arduino PWM runs at approximately 490 Hz or 980 Hz (pins 5 and 6).
This is fast enough that: - LEDs appear to dim smoothly - Motors run smoothly - Humans don't hear the switching
Part 3: Project - LED Brightness Control¶
Circuit¶
Pin 9 is a PWM pin (marked with ~).
Code - Dimming an LED¶
int ledPin = 9; // PWM-capable pin
int brightness = 0; // Initial brightness (0-255)
int fadeAmount = 5; // How much to change each step
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
// Set LED brightness
analogWrite(ledPin, brightness);
// Change brightness for next iteration
brightness = brightness + fadeAmount;
// Reverse direction at ends
if (brightness <= 0 || brightness >= 255) {
fadeAmount = -fadeAmount;
}
delay(30); // Wait 30ms before changing again
}
How It Works¶
- Start with brightness = 0 (LED off)
- Gradually increase brightness
- When reaches 255, reverse direction
- Gradually decrease back to 0
- Repeat forever
The result: LED pulses smoothly from dim to bright and back.
Practice: Different Fade Speeds¶
Try changing these values:
- fadeAmount = 1 - Very slow fade
- fadeAmount = 20 - Fast pulse
- delay(10) - Faster overall cycle
- delay(100) - Slower overall cycle
Part 4: Project - DC Motor Speed Control¶
Circuit¶
Important: Motors need more current than Arduino can provide. Use a motor driver!
Code¶
int motorPin = 5; // PWM pin connected to motor driver
int speed = 0; // Motor speed (0-255)
void setup() {
pinMode(motorPin, OUTPUT);
}
void loop() {
// Accelerate from stop to full speed
for (speed = 0; speed <= 255; speed += 5) {
analogWrite(motorPin, speed);
delay(50); // Gradual acceleration
}
// Hold at full speed
delay(1000);
// Decelerate to stop
for (speed = 255; speed >= 0; speed -= 5) {
analogWrite(motorPin, speed);
delay(50); // Gradual deceleration
}
delay(1000); // Hold at stop
}
Controlling Direction¶
To control direction (forward/reverse), use an H-bridge like the L293D:
int enablePin = 5; // Speed control (PWM)
int in1Pin = 4; // Direction control
int in2Pin = 3; // Direction control
void setup() {
pinMode(enablePin, OUTPUT);
pinMode(in1Pin, OUTPUT);
pinMode(in2Pin, OUTPUT);
}
void setMotor(int speed, boolean reverse) {
analogWrite(enablePin, speed);
digitalWrite(in1Pin, !reverse);
digitalWrite(in2Pin, reverse);
}
void loop() {
setMotor(200, false); // Forward at 78% speed
delay(2000);
setMotor(0, false); // Stop
delay(1000);
setMotor(200, true); // Reverse at 78% speed
delay(2000);
setMotor(0, false); // Stop
delay(1000);
}
Part 5: Servo Motors¶
What is a Servo?¶
A servo motor rotates to a specific angle (typically 0° to 180°) and holds that position.
Servo Control¶
Servos use a special PWM signal: - Pulse width determines angle - 20ms period (50 Hz refresh rate)
| Pulse Width | Angle |
|---|---|
| 1 ms | 0° |
| 1.5 ms | 90° |
| 2 ms | 180° |
Arduino Servo Library¶
Arduino includes a built-in Servo library:
#include <Servo.h>
Servo myServo; // Create servo object
int pos = 0; // Current position (angle)
void setup() {
myServo.attach(9); // Attach to pin 9
}
void loop() {
// Sweep from 0 to 180 degrees
for (pos = 0; pos <= 180; pos += 1) {
myServo.write(pos); // Move to position
delay(15); // Wait for servo to reach position
}
// Sweep back from 180 to 0 degrees
for (pos = 180; pos >= 0; pos -= 1) {
myServo.write(pos);
delay(15);
}
}
Key Servo Functions¶
| Function | Description |
|---|---|
Servo.attach(pin) |
Connect servo to a pin |
Servo.write(angle) |
Move to angle (0-180) |
Servo.writeMicroseconds(us) |
Set pulse width in microseconds |
Servo.read() |
Read current angle |
Part 6: Practice Problems¶
Problem 1: LED Breathing¶
Write a program that creates a "breathing" LED effect - starts at 0%, gradually increases to 100%, then back to 0%, repeating.
Show Solution
int ledPin = 9;
int brightness = 0;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
// Fade in
for (int brightness = 0; brightness <= 255; brightness += 1) {
analogWrite(ledPin, brightness);
delay(10);
}
// Fade out
for (int brightness = 255; brightness >= 0; brightness -= 1) {
analogWrite(ledPin, brightness);
delay(10);
}
delay(500); // Pause at each end
}
Problem 2: Variable Speed Motor¶
Modify the motor speed control to use a potentiometer for speed control.
Show Solution
int motorPin = 5;
int potPin = A0; // Potentiometer on analog pin 0
int speed = 0;
void setup() {
pinMode(motorPin, OUTPUT);
pinMode(potPin, INPUT);
}
void loop() {
// Read potentiometer (0-1023)
int potValue = analogRead(potPin);
// Map to motor speed (0-255)
speed = map(potValue, 0, 1023, 0, 255);
// Control motor
analogWrite(motorPin, speed);
delay(10); // Small delay for stability
}
Problem 3: Servo Position Control¶
Write a program that moves a servo to 0°, 45°, 90°, 135°, 180° with a button press.
Show Solution
#include <Servo.h>
Servo myServo;
int servoPin = 9;
int buttonPin = 2;
int angles[] = {0, 45, 90, 135, 180};
int currentIndex = 0;
void setup() {
myServo.attach(servoPin);
pinMode(buttonPin, INPUT_PULLUP);
myServo.write(angles[0]); // Start at 0 degrees
}
void loop() {
if (digitalRead(buttonPin) == LOW) {
delay(50); // Debounce
currentIndex = (currentIndex + 1) % 5; // Next angle
myServo.write(angles[currentIndex]);
// Wait for button release
while (digitalRead(buttonPin) == LOW) {
delay(10);
}
delay(50); // Additional debounce
}
}
Summary¶
- PWM rapidly switches between ON and OFF to control average power
- Duty cycle = percentage of time signal is HIGH
- Arduino PWM:
analogWrite(pin, value)where value = 0-255 - LED dimming uses PWM (value controls brightness)
- Motor speed uses PWM (value controls speed)
- Servo motors use angle-based control via Servo library
Key Reminders¶
- Only PWM pins (marked ~) support analogWrite()
- Motors need motor drivers (L293D) - never connect directly to Arduino
- Higher duty cycle = more power/brightness/speed
- Servo library handles the special PWM timing automatically
- 0-255 maps to 0-100% duty cycle
Custom activity — adapted from PLTW Digital Electronics