DEV Community

EffessDev
EffessDev

Posted on

Pulse Width Modulation in ESP32

Note: This post is not completed yet.

Introduction

ESP32s have a peripheral module called LEDC, which stands for "LED Control" or "LED PWM Controller". It is used to dim external LEDs using a technique called Pulse Width Modulation (PWM), which works by switching the LED on and off too rapidly for human eyes to notice.

Virtually all ESP32s only have one LEDC. Inside the LEDC of the original ESP32, there are 4 timers and 16 channels.

Controlling the LEDC

The LEDC is controlled by writing to the registers in it. These registers are known as peripheral registers since they belong exclusively to the LEDC peripheral (outside the CPU core). They should not be confused with the internal CPU registers, which are inside the CPU and we generally don't work with directly.

A register is 32 bits wide. We can toggle each bit on and off (1 or 0), which does certain things to the peripheral. There might be multiple registers inside the same peripheral (when it requires more than 32 bits). For example, if we want to pause the timer inside the LEDC peripheral, we turn on the PAUSE bit of the LEDC_TIMER1_CONF_REG register.

Dimming an LED

Dimming an LED using PWM involves mainly 3 steps:

Step 1: Configure the Timer (The Clock)

You can use any one of the 4 LEDC timers(e.g., Timer 0).

  • Set the frequency of the timer, which should be fast enough to prevent visible flickering (typically 1 kHz to 5 kHz).

  • Set the bit resolution (e.g., 8-bit for values 0–255, or 10-bit for 0–1023).

  • Start the timer by clearing the RST (reset) and PAUSE bits in the timer register.

Since the frequency and bit resolution are numbers instead of simple "on" or "off" configuration, multiple bits of the 32-bit-wide LEDC_TIMERx_CONF_REG register are allocated to each of them.

Step 2: Configure the Channel (The Comparator)

Select an LEDC channel (e.g., Channel 0) and set its clock in the LEDC_TIMER_SEL field in the LEDC_CHx_CONF0_REG register. LEDC_TIMER_SEL is a 2-bit field inside a Channel Configuration Register (LEDC_CHx_CONF0_REG). The value in those 2 bits selects which of the 4 hardware timers (Timer 0, 1, 2, or 3) that specific channel will use as its clock source.

After that, write your desired brightness value into the duty cycle register (e.g., 128 out of 255 for 50% brightness). Latch the parameters by setting the duty update bit to transfer your written value into active hardware logic.

Step 3: Route to the Output (The Hardware Switchboard)

Use the GPIO Matrix to connect your configured LEDC channel directly to the physical GPIO pin where your LED is wired.

Step 4: Adjusting Brightness During Runtime (Optional)

Once steps 1–3 are done, the LED lights up at your set brightness, running automatically in hardware with 0% CPU usage. To change the brightness later:

  • Write a new duty cycle value to the channel register and trigger the update bit.
  • Or, tell the hardware to automatically perform a smooth fade to a new value over a set duration.

Top comments (0)