9. PWM
9.1 PWM Introduction
PWM, Pulse Width Modulation, is a very effective technique that uses the digital output of a microprocessor to control analog circuits. It is a method for digitally encoding analog signal levels. It refers to the proportion of time that a waveform stays at high level, or state 1, within a certain period. By using a high-resolution counter, the duty cycle of a square wave is modulated to encode the level of an analog signal. A PWM signal is still digital, because at any given moment the full-scale DC supply is either completely on or completely off. For example, if our voltage output is 5 V, changing the PWM duty cycle can create an average output effect of 3.3 V or 1.3 V over a certain period.
Example
Imagine you have an LED and a switch. If you repeatedly turn the switch on and off so quickly that the human eye cannot see it, the LED is on half the time and off half the time. If you do this quickly enough, the observer sees the LED as continuously lit at half brightness. This is the basic principle of PWM. If the switch stays off most of the time, the LED appears dimmer. Conversely, if the switch stays on most of the time, the LED appears brighter. This is how PWM controls brightness by adjusting the duty cycle.
9.2 Basic PWM Parameters
PWM is pulse width modulation and has two very important parameters: frequency and duty cycle.
- Frequency: the period length of a PWM signal, usually expressed in hertz, Hz. It indicates how many pulses occur per second. PWM frequency is the reciprocal of the entire period. It refers to the number of times within 1 second that the signal goes from high level to low level and back to high level, which is one cycle.
- Duty cycle: the percentage of one period during which the signal remains high. Duty cycle can be expressed as a percentage. For example, a 50% duty cycle means the high level lasts for half of the period, and the low level lasts for the other half.
- Resolution: the PWM signal resolution supported by ESP32 refers to the number of different duty cycle levels that the device can output. For example, 8-bit resolution means the device can output 2^8 different duty cycle levels, from 0%, 1/256, 2/256, and so on, up to 100%.
9.3 PWM on ESP32S3
ESP32-S3 has two hardware peripherals that can output PWM signals: the LED PWM controller, LEDC, and the Motor Control Pulse Width Modulator, MCPWM. They have different characteristics and uses:
- LED PWM controller, LEDC: this module is mainly designed to generate high-precision PWM waveforms for controlling LED brightness or generating sound. LEDC resolution can reach 16 bits and can generate accurate and smooth changes, making it suitable for LED brightness control and sound generation. LEDC supports up to 8 PWM output channels and any GPIO pin. Users can configure the frequency and duty cycle of each channel.
- Motor Control Pulse Width Modulator, MCPWM: this module is mainly used for motor control, including servo motors, stepper motors, and ordinary motors. MCPWM supports more complex control modes, such as forward/reverse motor drive, power-off braking, and closed-loop control, meeting more complex motor control requirements. MCPWM supports up to 3 independent PWM output channels and also supports dead-time control and external signal capture.
In general, LEDC and MCPWM handle PWM differently and are used in different scenarios. LEDC is more suitable for controlling linear devices such as lights and sound, while MCPWM includes more advanced functions suitable for motor control. In this chapter, the LED PWM controller is used as the example to output PWM. It is referred to as LEDC below.
9.4 PWM Control Methods
There are multiple ways to control PWM signals in Arduino. Two of them are described below:
9.4.1 Control Through Arduino Internal Function Calls
On the ESP32S3 development board, you can write programs in Arduino IDE and control PWM signal output frequency, duty cycle, resolution, and other parameters by calling the analogWrite() function. Use Arduino's built-in analogWrite(pin, value) function: The two parameters are:
pin: the GPIO pin to write to. Allowed data type: int. Any GPIO pin is supported.value: duty cycle, between 0, always off, and 255, always on. Allowed data type: int. Based on the example in the PWM Introduction section, write a code example to make the LED gradually brighten and dim:
// Use Arduino's built-in analogWrite(pin, value) function:
// Macro definition for GPIO output pin
#define LED_PIN 48
void setup()
{
// Configure GPIO output pin
pinMode(LED_PIN, OUTPUT);
}
void loop()
{
// Gradually brighten
for(int i=0;i<256;i++)
{
// Set the analog brightness value. The duty cycle keeps increasing.
analogWrite(LED_PIN, i);
// Delay for 10 ms
delay(10);
}
// Gradually dim
for(int i=255;i>=0;i--)
{
// Set the analog brightness value. The duty cycle keeps decreasing.
analogWrite(LED_PIN, i);
// Delay for 10 ms
delay(10);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
📌How does PWM control LED brightness?
LED brightness is determined by the current or voltage flowing through it. PWM controls the average brightness of an LED by rapidly switching its power supply on and off so quickly that the human eye cannot perceive flicker. Adjusting the duty cycle makes the LED stay ON longer during one period, making it brighter, or stay OFF longer, making it dimmer. This is a very effective way to modulate brightness because it changes only the ratio of on-time to off-time rather than changing the supply voltage or current. This means the LED always runs at its optimal brightness and efficiency when it is on.
9.4.2 Control Through the PWM Peripheral Library
ESP32S3 also supports calling LEDC peripheral functions through the PWM API to set PWM channel output parameters, such as frequency and duty cycle, thereby controlling device speed, brightness, and so on. In addition, related parameter setting and auxiliary functions can be used to further control the PWM signal.
Configuring LEDC to output PWM usually follows these steps:
- Use
ledcAttach()to create an LEDC channel. - Use
ledcWrite()to output the PWM signal.
Important Function Descriptions
ledcAttach()initializes the PWM signal output parameters of the channel.
bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution); Here, pin is the pin number, meaning which pin the LED controller outputs PWM to. freq is the PWM output frequency in hertz, Hz. bit_num is the PWM output precision, with a range from 1 to 14.
ledcAttachPin()associates the specified pin with a specific LEDC channel for PWM output.ledcWrite()is a function in the ESP32S3 Arduino library used to set the PWM output value of an LED controller channel.
bool ledcWrite(uint8_t pin, uint32_t duty); Here, pin is the pin where the LED controller outputs PWM. duty is the PWM duty cycle to set, with a range from 0 to (2^bit_num - 1), where bit_num is the output precision set by ledcAttach(). By calling ledcWrite(), the PWM output value on the specified channel can be set. The PWM duty cycle range starts from 0, completely off, to (2^bit_num - 1), completely on.
9.5 Hardware Connection and Preparation
This example uses the onboard LED for a breathing light test. In general, the human eye does not perceive flicker above about 80 Hz. When the frequency is high enough that flicker is not visible, a larger duty cycle makes the LED brighter, and a smaller duty cycle makes the LED dimmer. Therefore, when frequency is fixed, different duty cycles can be used to change LED brightness, creating a breathing light effect, gradually brightening and then gradually dimming repeatedly. The onboard LED is connected to GPIO48, so during initialization, the LEDC function needs to be bound to GPIO48.
9.6 PWM Breathing Light Verification
#define FREQ 2000 // Frequency
#define RESOLUTION 8 // Resolution
#define LED 48 // LED pin
void setup()
{
ledcAttach(LED, FREQ, RESOLUTION); // Set channel
}
void loop()
{
// Gradually brighten
for (int i=0;i<pow(2, RESOLUTION); i++)
{
ledcWrite(LED, i); // Output PWM
delay(5);
}
// Gradually dim
for (int i=pow(2, RESOLUTION)-1;i>=0;i--)
{
ledcWrite(LED, i); // Output PWM
delay(5);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
pow(2, RESOLUTION) is a mathematical expression that calls the C exponential function pow() and returns 2 raised to the power of RESOLUTION. In this example, RESOLUTION is 8, so the result of pow(2, RESOLUTION) is 2 to the 8th power, or 256.