Welcome to ESP32 Core Board repository
9. PWM Breathing LED
PWM Breathing LED
PWM Breathing LED1. PWM Overview1.1 What is PWM1.2 Three Core Parameters of PWM1.3 What This Experiment Does2. Core Concepts2.1 Duty Cycle and Resolution2.2 Common-Anode LED PWM Control2.3 Why VISIBLE_DUTY = 200 Instead of 02.4 LEDC Peripheral Architecture3. Project Architecture and Flow4. Hardware Dependencies5. Source Code Analysis5.1 Macro Definitions5.2 led_pwm_init() — LEDC Initialization5.3 breath_led_task() — Breathing Light Algorithm (Core)5.4 app_main() — Main Entry6. Operation Flow6.1 Build and Flash6.2 Expected Phenomenon6.3 Parameter Adjustment Guide7. Common Issues
1. PWM Overview
1.1 What is PWM
PWM (Pulse Width Modulation) is simply a technique that "uses digital switches to simulate analog voltage".
In digital circuits, GPIO pins can only output two states: high (3.3V) or low (0V), no intermediate values. But PWM cleverly bypasses this limitation through "rapid switching"—the faster the switching, and with different ratios of on-time, the resulting "average effect" on the load differs.
Think of it this way: you can't make a water tap produce "half-thick" water column, but you can rapidly turn it on and off, 50% on / 50% off, and the bucket receives only half the water of a fully open tap. PWM is this concept.
1.2 Three Core Parameters of PWM
① Period: Time for one complete "on→off" cycle, in seconds. Period = 1 / frequency.
② Frequency: How many "on→off" cycles per second, in Hz. How high should frequency be?
Too low (<50Hz): Human eye can detect LED flickering, motor vibrates
Moderate (~1kHz): LED appears stable, ordinary motors run smoothly—this experiment is 1kHz
Too high (>20kHz): Beyond human hearing range, suitable for quiet motor drive scenarios, but switching losses increase
③ Duty Cycle: In one cycle, the ratio of "high level time" to "entire cycle time".
xDuty cycle = (high level time / period) × 100%Example frequency 1kHz (period = 1ms):25% duty cycle = high 0.25ms + low 0.75ms → average voltage ≈ 0.825V50% duty cycle = high 0.50ms + low 0.50ms → average voltage = 1.65V80% duty cycle = high 0.80ms + low 0.20ms → average voltage = 2.64V
PWM's magic is: You only change the "on/off" time ratio (duty cycle), but the load feels like voltage is continuously changing. This is the "area equivalence principle"—the average value of high-frequency square wave equals a constant analog voltage value.
1.3 What This Experiment Does
This experiment uses ESP32-S3's LEDC (LED PWM Controller) peripheral to generate PWM signals, driving an LED to display "breathing light" effect by dynamically changing duty cycle: gradually bright → gradually dim → off stay → repeat.
Application Scenarios:
| Scenario | Description |
|---|---|
| LED brightness control | Breathing light, keyboard backlight, ambiance light |
| Motor speed control | Servo angle, DC motor speed |
| Audio output | Generate analog audio with low-pass filter |
| Power management | DC-DC converter control signal |
2. Core Concepts
2.1 Duty Cycle and Resolution
xxxxxxxxxxPWM signal (1kHz frequency, period = 1ms):0% duty cycle: ────────────────────────────── average voltage = 0V(entire low level)25% duty cycle: ┌──┐┌───────────────────────── average voltage = 0.825V└──┘└─────────────────────────50% duty cycle: ┌─────┐┌────────────────────── average voltage = 1.65V└─────┘└──────────────────────100% duty cycle: ┌────────────────────────────── average voltage = 3.3V└──────────────────────────────
Duty cycle resolution: LEDC_TIMER_10_BIT means duty cycle divided into 0~1023 (1024 levels total). With 10-bit resolution:
| Duty Value | Meaning (common-anode this experiment) |
|---|---|
| 1023 (MAX_DUTY) | High level 100% = LED completely OFF |
| 511 | High level ~50% = LED half bright |
| 200 (VISIBLE_DUTY) | High level ~20% = LED relatively bright (low level 80%) |
| 0 | High level 0% = LED brightest (all low level) |
2.2 Common-Anode LED PWM Control
xxxxxxxxxxCommon-anode connection:VDD (3.3V) ──→ LED anode ──→ LED cathode ──→ Current-limiting resistor ──→ GPIO44GPIO output HIGH (1023): LED both ends at same potential, no current → OFFGPIO output LOW (0): Current VDD→LED→GPIO→GND → ON
This experiment's "reverse brightness" relationship:
| duty value | Low level ratio | LED brightness | State description |
|---|---|---|---|
| 1023 | 0% | OFF | OFF phase stay |
| 1023→200 | 0%→80% | Gradually bright | fade-in |
| 200→1023 | 80%→0% | Gradually dim | fade-out |
In common-anode connection, duty decreases → low level ratio increases → LED gets brighter. This is opposite to the conventional understanding "duty increases = brighter", so code uses
MAX_DUTY(1023) for OFF,VISIBLE_DUTY(200) for visible brightness.
2.3 Why VISIBLE_DUTY = 200 Instead of 0
If duty drops to 0 (100% low level = brightest), LED will instantly jump from "fully bright" to "fully off" (phase switch), visually discontinuous. 200 provides a "brightest but not fully bright" visible threshold, forming brightness contrast with MAX_DUTY, making breathing effect smooth.
2.4 LEDC Peripheral Architecture
xxxxxxxxxx┌──────────┐ ┌──────────┐ ┌──────────┐│ Timer 0 │───→│ Channel 0│───→│ GPIO44 ││ Timer 0 │ │ Channel 0│ │ (LED) ││ 1kHz │ │ duty=... │ │ ││ 10-bit │ │ │ │ │└──────────┘ └──────────┘ └──────────┘One timer can be shared by multiple channels (same frequency, different duty cycles),one channel can be bound to different GPIO pins.
LEDC Resources:
| Resource Type | Available Count | Description |
|---|---|---|
| Timer | 4 | Determines PWM frequency and resolution |
| Channel | 8 | Bind GPIO and duty cycle, share timer |
| Speed mode | Low Speed / High Speed | Low speed suitable for LED, high speed for precise edge requirements |
3. Project Architecture and Flow
xxxxxxxxxx