- Requirements For This Tutorial
- Pulse Width Modulation (PWM)
- ESP32 Hardware PWM
- ESP32 PWM Outputs Control (in Arduino)
- Components For This Tutorial’s LABs
- ESP32 PWM LED Brightness Control – LAB
In this tutorial, you’ll learn about ESP32 PWM and how to control PWM channels in Arduino Core. But first of all, you’ll get an introduction to what’s PWM and how it works in most microcontrollers on a hardware level. Then, we’ll investigate the ESP32 PWM Hardware peripheral and check the features it does have.
Then, we’ll move to the Arduino Core libraries that implement drivers for the ESP32 LED PWM peripheral and how to use its API functions, like ledcWrite() . It’s something similar to the commonly known Arduino analogWrite() function, but with a little bit more functionality and customizations as we’ll see. Without further ado, let’s get right into it!
Requirements For This Tutorial
You can either get the complete course kit for this series of tutorials using the link down below. Or just refer to the table for the exact components to be used in practical LABs for only this specific tutorial.
Pulse Width Modulation (PWM)
The pulse width modulation (PWM) is a technique to create a controllable waveform digital signal to be used in various applications. There are different variations of designs to implement hardware PWM in different microcontrollers devices. But they are essentially the same in terms of the final output and usage.
PWM Internal Hardware & How PWM Works?
This is a generic hardware diagram for a typical PWM peripheral.
As you can see in the diagram above, the main component in a PWM signal generator is the Timer module. The timer is being clocked by a clock signal that’s derived from the main system’s clock. And it starts counting from 0 each clock cycle it increments by one.
As the timer is counting up, its value is being compared by two comparators. If it reached the Duty Cycle register value, a match signal is generated which Resets the pin state so it becomes LOW. The timer will continue counting until it reaches the Period register’s value, then the other comparator will generate a match signal which Sets the PWM pin to the HIGH state. The timer rolls over back to 0, the process is repeated, And so on!
PWM Signal Properties
A typical PWM signal has the following properties that we can control by programming the microcontroller’s PWM peripheral’s registers. Such as PWM Frequency, PWM Resolution, and PWM Duty Cycle. By changing the PWM’s duty cycle parameter, the width of the pulse does also change. Therefore, the average voltage of the waveform does also change and this creates some sort of controllable analog output (not exactly).
The PWM frequency is just 1/T where T is the period of each cycle. You can set the frequency to any value you want depending on what you’re trying to control. We’ll dig deeper into this in future tutorials, but for now, we’d like to dim an LED. So a PWM frequency of 1kHz will be good enough for this application.
The last PWM parameter, the Resolution is a measure for how many discrete levels of duty cycle that we can control. Look at the GIF image above, you’ll notice that the PWM duty cycle is increasing by 10% at each level. Therefore, the total discrete levels of control for the duty cycle are 10 levels. The PWM resolution = log2(Num_of_Levels) = log2(10) = 3.3 Bits.
Setting the resolution at 8 bits will give us total Duty Cycle levels of = 2 n where n is the resolution (in bits). So the duty cycle entire range has 2 8 = 256 levels. The duty cycle values range is, therefore [ 0 – 255 ]. The higher the resolution is, the finer it gets to control the duty cycle.
ESP32 Hardware PWM
In this section, I’ll give you an introduction to the hardware capabilities for the ESP32 LED PWM peripheral, how it works, and what kind of features it has. So you can use it in an efficient way depending on the specific application requirements you have.
ESP32 PWM Pins
The ESP32 PWM hardware has 16 different channels, not pins. You can assign any of these channels to any GPIO pin that you want. But it has to have an output driver or in other words, it should be capable of operating as an output pin.
In our ESP32 dev boards, all the GPIO pins can be configured to operate in output mode except for 4 pins. Those pins are input only, so they don’t support PWM functionality. You can avoid those pins and you’re free to use any of the others.
Refer to this ESP32 devkit board pinout.
(if it’s not clear, right-click and open it in a new tab for a larger view)
ESP32 PWM Channels
The ESP32 PWM controller is primarily designed to control the intensity of LEDs, although it can be used to generate PWM signals for other purposes as well. It has 16 channels that can generate independent PWM waveforms.
The ESP32 PWM controller has 8 high-speed channels and 8 low-speed channels, which gives us a total of 16 channels. They’re divided into two groups depending on the speed. For each group, there are 4 timers / 8 channels. This means every two channels share the same timer. Therefore, we can’t independently control the PWM frequency of each couple of channels.
This is the ESP32 PWM hardware diagram from the datasheet.
So this means we’ve got 16 channels that we can control their PWM duty cycle independently. But the frequency has to be shared between each couple of channels routed to the same timer. If you need more than 8 different PWM signals with different frequencies, you should consider looking for an external solution (e.g. I2C PWM Controller IC).
ESP32 PWM Outputs Control (in Arduino)
You can use the ESP32 PWM pins to control many devices ranging from small LEDs and up to high-power motors using MOSFET drivers and things like that. In this section, I’ll give you a step-by-step approach for what to do in order to configure and control a PWM output pin.
Step1 – Decide on the PWM channel that you’re going to use [ 0 – 15 ].
Step2 – Decide on the GPIO pin to route this PWM Ch signal to.
Step3 – Assign that PWM Ch to the selected GPIO pin using this function.
Step4 – Decide on the required PWM Resolution for the selected channel [ 1Bit – 16Bits ]. Setting the resolution to 8Bits, gives you a duty cycle range [0 – 255]. While setting it to 10Bits, gives you a range of [ 0 – 1023 ]. And so on!
Step5 – Decide on the required PWM Frequency for the selected channel. It can be anything, but for our LED dimming example let’s set it to 1kHz or (1000Hz).
Step6 – Configure this PWM Channel with the selected frequency & resolution using this function.
Step7 – Now you can control this PWM pin by changing the duty cycle using this function down below.
Components For This Tutorial’s LABs
*Affiliate Disclosure: When you click on links in this section and make a purchase, this can result in this site earning a commission. Affiliate programs and affiliations include, but are not limited to, the eBay Partner Network (EPN) and Amazon.com, Banggood.com. This may be one of the ways to support this free platform while getting your regular electronic parts orders as usual at no extra cost to you.
ESP32 PWM LED Brightness Control – LAB
- Define & Attach The PWM GPIO pin
- Configure The PWM Channel (frequency & resolution)
- Gradually Increase the PWM’s duty cycle to max value, and gradually decrease it to the minimum value, and repeat!
ESP32 PWM LED Control – Code Example
The code example down below does the following: We start with defining & Attaching The PWM GPIO pin. The pin I’ll be using a PWM pin is GPIO5 in this example.
Then, we’ll be configuring the PWM Channel’s frequency & resolution.
And in the main loop() function, I’ll be gradually increasing the PWM’s duty cycle to max value, and gradually decreasing it to the minimum value, and repeat!