Pulse Width Modulation (PWM) is a technique to control the average voltage delivered to a load (like an LED) by rapidly switching power on and off. By varying the duty cycle (percentage of "on" time), you can smoothly adjust brightness. Here’s how to implement it on common platforms:

**1. PWM Basics**
Duty Cycle: Percentage of time the signal is "high" (e.g., 50% = half brightness).
Frequency: How fast the signal switches (e.g., 500Hz–1kHz for LEDs).
* Too low (<100Hz): Flickering visible.
* Too high (>10kHz): LED driver limitations.
**2. Hardware Setup**
**Components Needed**
* LED (+ current-limiting [resistor](https://www.onzuu.com/category/resistors), e.g., 220Ω for 3.3V/5V).
* [Microcontroller](https://www.ampheo.com/c/microcontrollers) (e.g., [Arduino](https://www.ampheo.com/c/development-board-arduino), [Raspberry Pi](https://www.ampheo.com/c/raspberry-pi/raspberry-pi-boards), [STM32](https://www.ampheo.com/search/STM32)).
**Wiring**
```
text
Microcontroller GPIO → Resistor → LED → GND
```
(PWM-capable pin required—check your MCU’s datasheet.)
**3. Software Implementation**
**A. Arduino (C++)**
```
cpp
const int LED_PIN = 9; // PWM-capable pin (~ symbol on Arduino)
void setup() {
pinMode(LED_PIN, OUTPUT);
}
void loop() {
// Fade in/out
for (int brightness = 0; brightness <= 255; brightness++) {
analogWrite(LED_PIN, brightness); // 8-bit PWM (0–255)
delay(10);
}
for (int brightness = 255; brightness >= 0; brightness--) {
analogWrite(LED_PIN, brightness);
delay(10);
}
}
```
**Key Notes:**
* analogWrite() uses hardware PWM (on pins 3, 5, 6, 9, 10, 11 for Arduino Uno).
* For non-PWM pins, use software PWM (less precise).
**B. Raspberry Pi (Python)**
```
python
import RPi.GPIO as GPIO
import time
LED_PIN = 18 # Broadcom GPIO18 (PWM-capable)
GPIO.setmode(GPIO.BCM)
GPIO.setup(LED_PIN, GPIO.OUT)
pwm = GPIO.PWM(LED_PIN, 1000) # 1kHz frequency
pwm.start(0) # Start with 0% duty cycle
try:
while True:
for duty_cycle in range(0, 101, 5): # 0% to 100%
pwm.ChangeDutyCycle(duty_cycle)
time.sleep(0.1)
for duty_cycle in range(100, -1, -5): # 100% to 0%
pwm.ChangeDutyCycle(duty_cycle)
time.sleep(0.1)
except KeyboardInterrupt:
pwm.stop()
GPIO.cleanup()
```
**Key Notes:**
* Only GPIO12, 13, 18, 19 support hardware PWM on Raspberry Pi.
* ChangeDutyCycle() accepts 0–100%.
**C. STM32 (HAL Library, C)**
```
c
#include "stm32f4xx_hal.h"
TIM_HandleTypeDef htim;
TIM_OC_InitTypeDef sConfigOC;
void PWM_Init() {
htim.Instance = TIM1;
htim.Init.Prescaler = 84 - 1; // 84MHz/84 = 1MHz
htim.Init.CounterMode = TIM_COUNTERMODE_UP;
htim.Init.Period = 1000 - 1; // 1MHz/1000 = 1kHz PWM
HAL_TIM_PWM_Init(&htim);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 500; // 50% duty (500/1000)
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim, TIM_CHANNEL_1);
}
int main() {
HAL_Init();
PWM_Init();
while (1) {
// Adjust duty cycle dynamically
__HAL_TIM_SET_COMPARE(&htim, TIM_CHANNEL_1, 700); // 70%
HAL_Delay(1000);
}
}
```
**Key Notes:**
* Configure timer (TIMx) and channel (TIM_CHANNEL_x).
* Pulse = duty cycle value (0–Period).
**4. Advanced Techniques**
**A. Linear vs. Perceived Brightness**
Human eyes perceive brightness logarithmically. For smooth fading, use gamma correction:
```
cpp
// Arduino example: Convert linear to gamma-corrected PWM
uint8_t gamma_correct(uint8_t input) {
return (uint8_t)(pow(input / 255.0, 2.8) * 255);
}
```
**B. High-Frequency PWM**
* For flicker-free applications (e.g., cameras), use >5kHz.
* Example (RPi at 10kHz):
```
python
pwm = GPIO.PWM(LED_PIN, 10000) # 10kHz
```
**C. Multiple LEDs (LED Strips)**
* Use WS2812B (NeoPixel) libraries for addressable LEDs.
* Example (Arduino):
```
cpp
#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel strip(60, LED_PIN, NEO_GRB + NEO_KHZ800);
strip.setPixelColor(0, strip.Color(255, 0, 0)); // Red at 100%
strip.show();
```
**5. Troubleshooting**

**6. Key Takeaways**
1. Duty Cycle controls brightness (0% = off, 100% = full on).
2. Frequency must be high enough to avoid flicker (>100Hz).
3. [Microcontroller](https://www.onzuu.com/category/microcontrollers) Limits:
* [Arduino](https://www.ampheoelec.de/c/development-board-arduino): Hardware PWM on specific pins.
* [Raspberry Pi](https://www.ampheoelec.de/c/raspberry-pi/raspberry-pi-boards): Limited hardware PWM pins (use software PWM if needed).
* [STM32](https://www.ampheoelec.de/search/STM32): Flexible timer configurations.
By mastering PWM, you can control not just LEDs but also motors, servos, and more!