ESP32 LED_PWM模塊應用
這幾天在等待服務器調試,所以打算把一些周邊的功能做一下。最無聊的當然是指示燈啦。在準備隨手開個軟件定時器的時候,居然發現ESP32有一個專門的LED PWM模塊,真是神奇。當然事情沒有這麼簡單,ESP32 LED PWM模塊應該是爲了專業的燈光調製特別做的,當然也可以用於產生其它特定需求的PWM波形。
1:ESP32 LEDPWM模塊架構
官方文檔《esp32_technical_reference_manual》中關於LED PWM模塊有比較仔細的描述,這裏理一下:
1.1 ESP32 LED_PWM 高速/低速通道
<1>ESP32LED PWM模塊由16路通道組成,高速/低速各8路通道,對應h/l_ch0~8。
<2>高、低速通道由定時器(其實是分頻器)驅動,高、低速通道各有4個定時器,對應h/l_timer0~4。
<3>高、低速通道的區別僅在於:
A時鐘源:高速通道使用系統的APB_CLK或者REF_CLK;低速通道使用系統的REF_CLK或者SLOW_CLK。
B當軟件修改了高速通道計數器的最大值或分頻係數的話,輸出信號的更新將會在下一次溢出中斷之後生效。而低速通道在置位 LEDC_LSTIMERx_PARA_UP 之後,立刻更新計數器的計數範圍參數和分頻器的分頻係數。
1.2 ESP32 LED_PWM高速/低速通道結構
ESP32 LED_PWM模塊由16個高低速通道構成,通道包括通道分頻器(h/l_timer)與比較輸出通道(h/l_chn),結構如下:
通道分頻器(h/l_timerx)包括:時鐘源選擇器,時鐘源分頻器,時鐘計數器;通道分頻器決定輸出PWM波的頻率。
A:時鐘源選擇器:
時鐘源輸入到通道定時器。高速通道使用系統的ABP_CLK或者REF_CLK作爲時鐘源;低速通道使用系統REF_CLK或者SLOW_CLK作爲時鐘源。
B:時鐘源分頻器:
輸入到LED_PWM通道的時鐘首先需要分頻:分頻係數LEDC_CLK_DIV_NUM_H/LSTIMERx固定爲18位,分頻係數公式:
公式中A是LEDC_CLK_DIV_NUM_H/LSTIMER高10位,B是其低8位;
若B不爲0,則256個輸出週期中有B個以(A+1)分頻,(256-B)個以A分頻;
B個以(A+1)分頻的週期會均勻分佈在256個週期內。
C:時鐘計數器:
時鐘源分頻器輸出時鐘輸入時鐘計數器,時鐘計數器計數範圍由LEDC_HSTIMERx_DUTY_RES配置,當到達最大值也就是2^ LEDC_HSTIMERx_DUTY_RES – 1時,計數器產生溢出中斷(LEDC_H/LS_TIMERn_OVF_INT)。時鐘計數器可以被軟件暫停,復位,以及讀取。計數器最大可被設置爲20位。
D:高速通道最終輸出的PWM信號頻率計算公式:
低速通道的輸出頻率類似,換算時鐘和對應寄存器數值就可以獲知。
比較輸出通道(h/l_chx)接收來自通道分頻器輸出的脈衝並計數,根據計數值與高位/低位比較器預設值進行比較,變更PWM波引腳電平。比較輸出通道決定PWM波佔空比。
A:高位比較器:當比較輸出通道接收來自通道分頻器的脈衝數達到高位比較器設定值時,該通道連接的GPIO輸出高電平,高位比較器取值(hpoint)只由LEDC_HPOINT_H/LSCHn確定
B:低位比較器:當比較輸出通道脈衝計數值達到低位比較器設定值時,GPIO輸出低電平。但低位比較器設定值(lpoint)由多個寄存器決定,這些寄存器共同決定PWM最終輸出固定佔空比或是可變佔空比的波形:
決定低位比較器設定值的寄存器包括:
LEDC_DUTY_INC_H/LSCHn:佔空比變化趨勢,增加或減少
LEDC_DUTY_START_H/LSCHn:啓動LEDC_DUTY_H/LSCHn設定的佔空比
LEDC_DUTY_CYCLE_H/LSCHn:每計數CYCLE個脈衝,波形漸變一次
LEDC_DUTY_SCALE_H/LSCHn:每計數CYCLE個脈衝,LEDC_DUTY_H/LSCHn的值遞增/遞減SCALE
LEDC_DUTY_NUM_H/LSCHn:佔空比變化的總次數,漸變完成時,會產生漸變完成中斷(LEDC_DUTY_CHNG_END_LSCH/Ln_INT)
LEDC_DUTY_H/LSCHn:該寄存器高20位即是低位比較器的值(lpoint);低4位用於“抖動”lpoint的值:當低4位非0時,輸出信號的脈衝寬度有LEDC_DUTY_HSCHn[3:0]/16 的概率多一個計數週期。低4位小數有利於提高輸出信號佔空比的精度。
綜上所述,ESP32 LED_PWM模塊由高/低速通道構成,每個通道由通道分頻器與比較輸出通道構成;每個通道都可以產生通道分頻器溢出中斷與佔空比漸變完成中斷。
下面簡析ESP32 LED_PWM低速通道的通道分頻器、比較輸出通道配置以及如何設置輸出固定佔空比與可變佔空比波形。
ESP32SDK關於LED_PWM模塊的驅動位於:
\esp-idf-v3.0-rc1\components\driver\ledc.c
\esp-idf-v3.0-rc1\components\driver\include\ledc.h
2:通道分頻器配置與PWM頻率
ESP32LED_PWM通道分頻器的配置接口是:
esp_err_tledc_timer_config(const ledc_timer_config_t* timer_conf)
參數類型ledc_timer_config_t:
對照通道分頻器的結構,參數非常明顯:
○speed_mode:選用高速/低速通道;意味着時鐘源不同。
○duty_resolution:時鐘計數器計數閾值設置,若duty_resolution = 10,則時鐘計數器計數範圍(0 - 2^10-1)
○timer_num:高低速通道均有4個定時器,該參數決定使用哪個
○freq_hz:通道分頻器輸出頻率,也即是最終輸出到GPIO的PWM波形的頻率。
ledc_timer_config接口內會根據輸入的參數計算並配置相應的寄存器。最終在:
ledc_timer_set(speed_mode, timer_num,div_param, duty_resolution, timer_clk_src)設置相關寄存器。
3:比較輸出通道配置與PWM佔空比
ESP32LED_PWM比較輸出通道的配置接口是:
esp_err_tledc_channel_config(const ledc_channel_config_t* ledc_conf)
參數類型:ledc_channel_config_t:
相關參數也非常好理解,這裏只關注intr_type與duty。
duty:
ledc_channel_config最終會通過ledc_set_duty將duty參數傳入到ledc_duty_config:
參見1.2節對於波形輸出通道配置的描述,可知函數ledc_duty_config是通過配置高位比較器與低位比較器來控制佔空比,其中:
高位比較器取值相關參數:hpoint= 0;
低位比較器取值相關參數:
duty_increase = 1:佔空比遞增
duty_num = 1:佔空比變化的次數,僅1次
duty_cycle = 1:每計數1個脈衝,佔空比發生1次漸變
duty_value = duty << 4:duty_value即是lpoint的值,左移4位是因爲低4位是小數部分
duty_scale = 0:計數1個(duty_cycle=1)脈衝,duty_value遞增(duty_increase=1) duty_scale,而此處duty_scale = 0意味着波形佔空比不變化
也就是說,通過ledc_channel_config配置比較輸出通道,會得到一個佔空比固定的波形。其佔空比固定爲duty/2^duty_resolution。
intr_type:該參數用於使能/禁止“fade”中斷,也就是PWM波形佔空比漸變完成時產生的中斷。這個中斷需要調用ledc_fade_func_install註冊中斷處理到ledc_fade_isr。
該功能用於控制PWM波佔空比連續漸變,後面再提到。
4:LED頻率閃爍實現
對於簡單的LED閃爍提示功能,只需要設定閃爍頻率,固定閃爍亮度(固定波形佔空比)即可。下面是一個例子:
5:LED亮度漸變實現
LED若需要實現亮度漸變功能,則需要(連續)變化PWM佔空比。ESP32 LED_PWM模塊提供了相關的功能。
驅動內相關的API包括:
ledc_fade_func_install:註冊LED_PWM“佔空比漸變完成”中斷處理函數ledc_fade_isr
ledc_set_fade:設置LED_PWM佔空比漸變參數(不要直接用這個接口,一堆BUG)
ledc_set_fade_with_time/ledc_set_fade_with_step:按照設定的漸變時間或者步長設置LED_PWM佔空比漸變參數
ledc_fade_start:啓動漸變佔空比的波形輸出
輸出佔空比漸變的PWM歸根結底還是要依賴設置ledc_duty_config的函數,也就是比較輸出通道的高位點與低位點,以下是一個例子:
可以在示波器看到波形連續變化,LED越來越亮:
6:問題
ledc驅動下代碼肯定有問題(手動笑臉),不過暫時夠用,遇到複雜情況再行討論。