????????本章將介紹使用 ESP32-S3 LED 控制器(LEDC)。 LEDC 主要用于控制 LED,也可產生PWM信號用于其他設備的控制。該控制器有 8 路通道,可以產生獨立的波形,驅動 RGB LED 等設備。 LED PWM 控制器可在無需 CPU 干預的情況下自動改變占空比,實現亮度漸變。 ESP32-S3 IDF 提供了兩種方式改變 PWM,一種是通過軟件改變 PWM 占空比,另一種是通過硬件改變PWM 占空比,這兩種方式都會學習。通過本章將學習到通過軟件改變PWM 占空比的運用。本章分為如下幾個小節:
17.1 PWM 簡介
17.2 硬件設計
17.3 程序設計
17.4 下載驗證
17.1 PWM 簡介
(1) PWM 原理解析
PWM(Pulse Width Modulation),簡稱脈寬調制,是一種將模擬信號變為脈沖信號的計數。PWM 可以控制 LED 亮度、直流電機的轉速等。
PWM 的主要參數如下:
① PWM 頻率:PWM 頻率是 PWM 信號在 1s 內從高電平到低電平再回到高電平的次數,
也就是說 1s 內有多少個 PWM 周期,單位為 Hz。
② PWM 周期:PWM 周期是 PWM 頻率的倒數,即 T=1/f, T 是 PWM 周期, f 是 PWM 頻率。如果 PWM 頻率為 50Hz,也就是說 PWM 周期為 20ms,即 1s 由 50 個 PWM 周期。
③ PWM 占空比:PWM 占空比是指在一個 PWM 周期內,高電平的時間與整個周期時間的比例,取值范圍為 0%~100%。例如,如果 PWM 周期是 10ms,而脈寬時間為 8ms,那么 PWM 占空比就是8/10=80%,此時的 PWM 信號就是占空比為 80%的 PWM 信號。 PWM 名為脈沖寬度調制,顧名思義,就是通過調節 PWM 占空比來調節 PWM 脈寬時間。PWM 占空比如下圖所示。
圖 17.1.1 PWM 占空比
????????在使用 PWM 控制 LED 時,亮 1s 后滅 1s,往復循環,就可以看到 LED 在閃爍。如果把這個周期縮小到 200ms,亮 100ms 后滅 100ms,往復循環,就可以看到 LED 燈在高頻閃爍。繼續
把這個周期持續縮小,總有一個臨界值使人眼分辨不出 LED在閃爍,此時 LED的亮度處于滅與
亮之間亮度的中間值,達到了 1/2 亮度。 PWM 占空比和亮度的關系如下圖所示。
圖 17.1.2 PWM 占空比和亮度的關系
(2)ESP32 的 LED PWM 控制器介紹
ESP32-S3 的 LED PWM 控制器,簡寫為 LEDC,用于生成控制 LED 的脈沖寬度調制信號。
LED PWM 控制器具有八個獨立的 PWM 生成器(即八個通道)。每個 PWM 生成器會從四個通用定時器中選擇一個,以該定時器的計數值作為基準生成 PWM 信號。 LED PWM 定時器如下圖所示。
圖 17.1.1.1 LED_PWM 的定時器
????????為了實現 PWM 輸出,先需要設置指定通道的 PWM 參數:頻率、分辨率、占空比,然后將該通道映射到指定引腳,該引腳輸出對應通道的 PWM 信號,通道和引腳的關系所下圖所示。
圖 17.1.1.2 LED PWM 輸出示意圖
????????另外, LED PWM 控制器可在沒有 CPU 干預的情況下自動改變占空比,實現亮度以及顏色
漸變。
17.2 硬件設計
17.2.1 例程功能
通過軟件改變 PWM 的形式使得 LED 由亮到暗,再由暗到亮,依次循環。
17.2.2 硬件資源
1. LED
LED-IO1
2. 定時器 1
通道 1 - IO1
17.2.3 原理圖
????????本章實驗使用的定時器 1 為 ESP32-S3 的片上資源,因此沒有對應的連接原理圖。
17.3 程序設計
17.3.1 程序流程圖
本實驗的程序流程圖:
圖 17.3.1.1 SW_PWM 實驗程序流程圖
17.3.2 SW_PWM 函數解析
ESP-IDF 提供了一套 API 來配置 PWM。要使用此功能,需要導入必要的頭文件:
#include "driver/ledc.h"
????????接下來,將介紹一些常用的 SW_PWM 函數,這些函數的描述及其作用如下:
(1)配置 LEDC 使用的定時器為定時器1
需要注意的一點是,在首次配置LEDC時,建議先配置定時器(調用函數ledc_timer_config()),再配置通道(調用函數ledc_channel_config())。這樣可以確保IO引腳上的PWM信號自輸出開始那一刻起,其頻率就是正確的。
要設置定時器,可調用函數 ledc_timer_config(),需要將一些參數的數據結構傳遞給該函數,該函數原型如下所示:
esp_err_t ledc_timer_config(const ledc_timer_config_t *timer_conf);
????????該函數的形參描述,如下表所示:
形參 | 描述 |
---|---|
timer_conf | 指向配置 LEDC 定時器的結構體指針 |
表 17.3.2.1 函數 ledc_timer_config ()形參描述
????????返回值: ESP_OK 表示配置成功,其他配置失敗。
該函數使用 ledc_timer_config_t 類型的結構體變量傳入,該結構體的定義如下所示:
結構體 | 成員變量 | 可選參數 |
---|---|---|
ledc_timer_config_t | speed_mode : 速度模式。需要注意的是,ESP32-S3 僅支持低速模式。 | LEDC_LOW_SPEED_MODE: 低速模式 |
LEDC_HIGH_SPEED_MODE: 高速模式 | ||
timer_num : 通道的定時器源, 定時器索引 ledc_timer_t | LEDC_TIMER_0 | |
LEDC_TIMER_1 | ||
LEDC_TIMER_2 | ||
LEDC_TIMER_3 | ||
LEDC_TIMER_MAX | ||
freq_hz : PWM信號頻率 , 表示LEDC模塊的定時器時鐘頻率設置,單位為 Hz | 無 | |
duty_resolution : PWM 占空比分辨率。 占空比分辨率通常用ledc_timer_bit_t 設置,范圍是 10 至 15 位。如需較低的占空比分辨率(上至10,下至1),可直接輸入相應數值。相關參數請參考ledc_timer_bit_t。 | LEDC_TIMER_1_BIT // 1位分辨率 (2級) LEDC_TIMER_2_BIT // 2位分辨率 (4級) LEDC_TIMER_3_BIT // 3位分辨率 (8級) LEDC_TIMER_4_BIT // 4位分辨率 (16級) LEDC_TIMER_5_BIT // 5位分辨率 (32級) LEDC_TIMER_6_BIT // 6位分辨率 (64級) LEDC_TIMER_7_BIT // 7位分辨率 (128級) LEDC_TIMER_8_BIT // 8位分辨率 (256級) LEDC_TIMER_9_BIT // 9位分辨率 (512級) LEDC_TIMER_10_BIT // 10位分辨率 (1024級) LEDC_TIMER_11_BIT // 11位分辨率 (2048級) LEDC_TIMER_12_BIT // 12位分辨率 (4096級) LEDC_TIMER_13_BIT // 13位分辨率 (8192級) LEDC_TIMER_14_BIT // 14位分辨率 (16384級) LEDC_TIMER_15_BIT // 15位分辨率 (32768級) LEDC_TIMER_16_BIT // 16位分辨率 (65536級) // ESP32-S2/S3/C3 支持到 20位 | |
clk_cfg: LEDPWM 的時鐘來源 | LEDC_AUTO_CLK: 啟動定時器時,將根據給定的分辨率和 占空率參數自動選擇 ledc 源時鐘; | |
LEDC_USE_APB_CLK: 選擇 APB 作為源時鐘; | ||
LEDC_USE_RC_FAST_CLK: 選擇“RC_FAST” 作為源時鐘; | ||
LEDC_USE_XTAL_CLK: 選擇 XTAL 作為源時鐘; | ||
LEDC_USE_RTC8M_CLK: ” LEDC_USE_RC_FAST_CLK” 的別名 |
表 17.3.2.2 ledc_timer_config_t 結構體參數值描述
特別再介紹下duty_resolution(占空比分辨率):
????????占空比分辨率是 PWM 控制中的核心概念,它決定了你能以多精細的級別控制輸出信號的強度。在 ESP32 的 LEDC (LED PWM Controller) 中,這是一個關鍵配置參數。
分辨率級別與精度:
分辨率位數?? | ??占空比級數?? | ??最小調節步進?? | ??適用場景?? |
---|---|---|---|
1 bit | 2 | 50% | 簡單開關控制 |
8 bits | 256 | 0.39% | 通用 LED 調光 |
10 bits | 1024 | 0.098% | 電機速度控制 |
12 bits | 4096 | 0.024% | 精密調光 |
13 bits | 8192 | 0.012% | 專業燈光控制 |
16 bits | 65536 | 0.0015% | 高精度儀器 |
????????表 17.3.2.3 分辨率級別與精度描述
分辨率與頻率的關系:
分辨率、時鐘頻率和 PWM 頻率之間存在嚴格的數學關系:
??計算公式??:
:PWM 輸出頻率;
:時鐘源頻率 (APB=80MHz, RTC8M=8.5MHz);
- ??resolution??:占空比分辨率位數;
- ??divider??:預分頻系數 (1-1024)。
實際限制??:
- 更高分辨率 → 更低最大 PWM 頻率;
- 更高PWM頻率 → 更低最大分辨率。
????????完成上述結構體參數配置之后,可以將結構傳遞給 ledc_timer_config () 函數,用以實例化SW_PWM 并返回 SW_PWM 句柄。 另外時鐘源同樣可以限制 PWM 頻率。選擇的時鐘源頻率越高,可以配置的 PWM 頻率上限就越高。
(2)通道配置函數
該函數原型如下所示:
esp_err_t ledc_channel_config(const ledc_channel_config_t *ledc_conf);
????????該函數的形參描述,如下表所示:
形參 | 描述 |
---|---|
ledc_conf | 指向配置 LEDC 通道的結構體指針 |
表 17.3.2.4?函數 ledc_channel_config ()形參描述
????????返回值: ESP_OK 表示配置成功,其他配置失敗。
該函數使用 ledc_channel_config_t 類型的結構體變量傳入,該結構體的定義如下所示:
結構體 | 成員變量 | 可選參數 |
---|---|---|
ledc_channel_config_t | gpio_num : 配置輸出引腳 | 本例程使用的引腳是 GPIO_NUM_1 |
speed_mode : 速度模式 | LEDC_HIGH_SPEED_MODE: 高速模式 | |
LEDC_LOW_SPEED_MODE: 低速模式 | ||
channel : LEDC 的輸出通道(PWM 的輸出通道) | 0~7,本例程配置為通道 1 | |
intr_type: 配置中斷 | 使能中斷: LEDC_INTR_FADE_END 失能中斷: LEDC_INTR_DISABLE | |
timer_sel: 選擇通道的定時器源。 定時器索引 ledc_timer_t | LEDC_TIMER_0 | |
LEDC_TIMER_1 | ||
LEDC_TIMER_2 | ||
LEDC_TIMER_3 | ||
LEDC_TIMER_MAX | ||
duty : LEDC 通道的占空比設置 | 占空比設定范圍為0~ | |
hpoint : led 通道 hpoint 值。 表示 占空比對應的時鐘計數值 | 無 | |
output_invert: 啟 用(1)或禁 用(0)gpio 輸 出反相 | 無 |
表 17.3.2.5 ledc_channel_config_t 結構體參數值描述
????????完成上述結構體參數配置之后,可以將結構傳遞給 ledc_channel_config() 函數,用以實例化
PWM 通道。
(3)改變 PWM 占空比
調用函數 ledc_set_duty()可以設置新的占空比。之后,調用函數 ledc_update_duty()使新配置生效。要查看當前設置的占空比,可使用 get 函數 ledc_get_duty(),該函數原型如下所示:
esp_err_t ledc_set_duty(ledc_mode_t speed_mode,ledc_channel_t channel,uint32_t duty);
????????該函數的形參描述,如下表所示:
形參 | 描述 |
---|---|
speed_mode | 速度模式選擇: LEDC_HIGH_SPEED_MODE LEDC_LOW_SPEED_MODE |
channel | LEDC 通道: (0 - LEDC_CHANNEL_MAX-1),從 ledc_channel_t 中選擇 |
duty | 設置 led 的負載,負載設置范圍為: 0~[( |
表 17.3.2.6 函數 ledc_set_dut ()形參描述
????????返回值: ESP_OK 表示配置成功,其他配置失敗。
(5)改變 PWM 占空比
在上一步調用 ledc_set_duty()設置新的占空比后,調用函數 ledc_update_duty()使新配置生效,該函數原型如下所示:
esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel);
????????該函數的形參描述,如下表所示:
形參 | 描述 |
---|---|
speed_mode | 速度模式選擇: LEDC_HIGH_SPEED_MODE LEDC_LOW_SPEED_MODE |
channel | LEDC 通道: (0 - LEDC_CHANNEL_MAX-1),從 ledc_channel_t 中選擇 |
表 17.3.2.7 函數 ledc_update_duty()形參描述
????????返回值: ESP_OK 表示配置成功,其他配置失敗。
17.3.3 SW_PWM 驅動解析
在 IDF 版的 08-1_sw_pwm 例程中,作者在 08-1_sw_pwm \components\BSP 路徑下新增了一個 PWM 文件夾,用于存放 pwm.c 和 pwm.h 這兩個文件。其中, pwm.h 文件負責聲明SW_PWM相關的函數和變量,而 pwm.c 文件則實現了 SW_PWM 的驅動代碼。下面,我們將詳細解析這兩個文件的實現內容。
(1)pwm.h 文件
/* 引腳以及重要參數定義 */
#define LEDC_PWM_TIMER LEDC_TIMER_1 /* 使用定時器0 */
#define LEDC_PWM_CH0_GPIO GPIO_NUM_1 /* LED控制器通道對應GPIO */
#define LEDC_PWM_CH0_CHANNEL LEDC_CHANNEL_1 /* LED控制器通道號 *//* 函數聲明 */
void pwm_init(uint8_t resolution, uint16_t freq); /* 初始化PWM */
void pwm_set_duty(uint16_t duty); /* PWM占空比設置 */
(2)pwm.c 文件
/*** @brief 初始化PWM* @param resolution: PWM占空比分辨率* freq: PWM信號頻率* @retval 無*/
void pwm_init(uint8_t resolution, uint16_t freq)
{ledc_timer_config_t ledc_timer = {0}; /* LEDC定時器句柄 */ledc_channel_config_t ledc_channel = {0}; /* LEDC通道配置句柄 *//* 配置LEDC定時器 */ledc_timer.duty_resolution = resolution; /* PWM占空比分辨率 */ledc_timer.freq_hz = freq; /* PWM信號頻率 */ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE; /* 定時器模式 */ledc_timer.timer_num = LEDC_PWM_TIMER; /* 定時器序號 */ledc_timer.clk_cfg = LEDC_AUTO_CLK; /* LEDC時鐘源 */ledc_timer_config(&ledc_timer); /* 配置定時器 *//* 配置LEDC通道 */ledc_channel.gpio_num = LEDC_PWM_CH0_GPIO; /* LED控制器通道對應引腳 */ledc_channel.speed_mode = LEDC_LOW_SPEED_MODE; /* LEDC模式 */ledc_channel.channel = LEDC_PWM_CH0_CHANNEL; /* LEDC控制器通道號 */ledc_channel.intr_type = LEDC_INTR_DISABLE; /* LEDC失能中斷 */ledc_channel.timer_sel = LEDC_PWM_TIMER; /* 定時器序號 */ledc_channel.duty = 0; /* 占空比值 */ledc_channel_config(&ledc_channel); /* 配置LEDC通道 */
}/*** @brief PWM占空比設置* @param duty:PWM占空比* @retval 無*/
void pwm_set_duty(uint16_t duty)
{ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_PWM_CH0_CHANNEL, duty); /* 設置占空比 */ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_PWM_CH0_CHANNEL); /* 更新占空比 */
}
????????LEDC 定時器設置好后,請配置所需的通道(ledc_channel_t)。配置通道需調用函數ledc_channel_config()。通道的配置與定時器設置類似,需向通道配置函數傳遞相應的結構體ledc_channel_config_t。此時,通道會按照 ledc_channel_config_t 的配置開始運作,并在選定的
GPIO 上生成由定時器指定的頻率和占空比的 PWM 信號。
調用函數 ledc_set_duty()可以設置新的占空比。之后,調用函數 ledc_update_duty()使新配置生效。為了方便使用,筆者將這兩個函數進行了“封裝”,通過傳參的形式來配置 PWM 占空比。
關于配置過程中所涉及到的結構體成員變量的含義以及用法,請讀者們回顧本章節前面的內容。
17.3.4 CMakeLists.txt 文件
打開本實驗 BSP 下的 CMakeLists.txt 文件,其內容如下所示:
set(src_dirsPWM)set(include_dirsPWM)set(requiresdriver)idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
17.3.5 實驗應用代碼
打開 main/main.c 文件,該文件定義了工程入口函數,名為 app_main。該函數代碼如下。
/*** @brief 程序入口* @param 無* @retval 無*/
void app_main(void)
{esp_err_t ret;uint8_t dir = 1;uint16_t ledpwmval = 0;ret = nvs_flash_init(); /* 初始化NVS */if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND){ESP_ERROR_CHECK(nvs_flash_erase());ret = nvs_flash_init();}pwm_init(LEDC_TIMER_10_BIT, 1000); /* 初始化PWM */while(1) {vTaskDelay(10);if (dir == 1){ledpwmval += 5; /* dir==1 ledpwmval遞增 */}else{ledpwmval -= 5; /* dir==0 ledpwmval遞減 */}if (ledpwmval > 1005){dir = 0; /* ledpwmval到達1005后,方向為遞減 */}if (ledpwmval < 5){dir = 1; /* ledpwmval遞減到5后,方向改為遞增 */}/* 設置占空比 */pwm_set_duty(ledpwmval);}
}
????????從上面的代碼中可以看到,在初始化 LEDC 定時器,并輸出 PWM 后,就不斷地改變定時器 0 的值,以達到改變 PWM 占功比的目的。又因為 PWM 由 IO1 引腳輸出, IO1 引腳連接至LED,所以 LED 的亮度也會隨之發生變化,從而實現呼吸燈的效果。
17.4 下載驗證
? ? ? ? 在完成編譯和燒錄后,可以看到板子上的 LED 先由暗再逐漸變亮,以此循環,實現了呼吸燈的效果。