開發(fā)環(huán)境:
MDK:Keil 5.30
開發(fā)板:GD32F207I-EVAL
MCU:GD32F207IK
1 PWM輸出的工作原理
脈沖寬度調(diào)制(PWM) ,是英文“Pulse Width Modulation” 的縮寫,簡稱脈寬調(diào)制,是利用微處理器的數(shù)字輸出來對模擬電路進行控制的一種非常有效的技術(shù)。簡單一點,就是對脈沖寬度的控制。
GD32 的定時器除了 TIMER5 和 6(基本定時器)。其他的定時器都可以用來產(chǎn)生 PWM 輸出。
每個定時器有四個通道,每一個通道都有一個捕獲比較寄存器,,將寄存器值和計數(shù)器值比較,通過比較結(jié)果輸出高低電平,便可以實現(xiàn)脈沖寬度調(diào)制模式(PWM信號)。
在上一節(jié),講解了定時器的相關(guān)寄存器即基本原理,本節(jié)將不再贅述。下面談?wù)勅绾问褂枚〞r器的寄存器進行PWM輸出的。若配置脈沖計數(shù)器TIMERx_CNT為向上計數(shù),而重載寄存器TIMERx_CAR配置為N,即TIMERx_CNT的當前計數(shù)值數(shù)值X在CK_TIMER時鐘源的驅(qū)動下不斷累加,當TIMERx_CNT的數(shù)值X大于N時,會重置TIMERx_CNT數(shù)值為0重新計數(shù)。而在TIMERx_CNT計數(shù)的同時,TIMERx_CNT的計數(shù)值X會與比較寄存器TIMERx_CHxCV預(yù)先存儲了的數(shù)值A(chǔ)進行比較,當脈沖計數(shù)器TIMERx_CNT的數(shù)值X小于比較寄存器TIMERx_CHxCV的值A(chǔ)時,輸出高電平(或低電平),相反地,當脈沖計數(shù)器的數(shù)值X大于或等于比較寄存器的值A(chǔ)時,輸出低電平(或高電平)。如此循環(huán),得到的輸出脈沖周期就為重載寄存器TIMERx_CAR存儲的數(shù)值(N+1)乘以觸發(fā)脈沖的時鐘周期,其脈沖寬度則為比較寄存器TIMERx_CHxCV的值A(chǔ)乘以觸發(fā)脈沖的時鐘周期,即輸出PWM的占空比為A/(N+1)。
估計很多初學(xué)者看了上面的一段話都很蒙圈,沒關(guān)系,下面以向上計數(shù)模式為例進行講解。
在PWM輸出模式下,除了CNT(計數(shù)器當前值)、CAR(自動重裝載值)之外,還多了一個值CHxCV(捕獲/比較寄存器值)。當CNT小于CHxCV時,CHxCV通道輸出低電平;當CNT等于或大于CHxCV時,CHxCV通道輸出高電平。因此得到PWM的一個周期如下:
1.定時器從0開始向上計數(shù);
2.當0-t1段,定時器計數(shù)器CNT值小于CHxCV值,輸出低電平;
3.t1-t2段,定時器計數(shù)器CNT值大于CHxCV值,輸出高電平;
4.當CNT值達到CAR時,定時器溢出,重新向上計數(shù)...循環(huán)此過程。
至此一個PWM周期完成。針對PWM重點關(guān)注兩個寄存器, CAR寄存器確定PWM頻率,CHxCV寄存器確定占空比 。
上文提到了PWM的輸出模式,下面講解PWM的工作模式:
- PWM模式1(向上計數(shù)) :計數(shù)器從0計數(shù)加到自動重裝載值(CAR),然后重新從0開始計數(shù),并且產(chǎn)生一個計數(shù)器溢出事。
- PWM模式2(向下計數(shù)) :計數(shù)器從自動重裝載值(CAR)減到0,然后重新從重裝載值(CAR)開始遞減,并且產(chǎn)生一個計數(shù)器溢出事件。
這里我們僅利用 TIMER2產(chǎn)生多路 PWM 輸出。如果要產(chǎn)生多路輸出,大家可以根據(jù)我們的代碼稍作修改即可。具體不同定時器對應(yīng)引腳在對應(yīng)芯片數(shù)據(jù)手冊的引腳說明(pin description) 中查看。
[ps] 本文以F2系列為例進行講解,GD不同系列其定時器個數(shù)不同
2 PWM輸出的寄存器描述
同樣,我們首先通過對 PWM 相關(guān)的寄存器進行講解,大家了解了定時器 TIMER2的 PWM原理之后,我們再講解怎么使用庫函數(shù)產(chǎn)生 PWM 輸出。
要使 GD32 的通用定時器 TIMERx 產(chǎn)生 PWM 輸出,除了上一章介紹的寄存器外,我們還會用到 3 個寄存器,來控制 PWM 的。這三個寄存器分別是:通道控制寄存器(TIMERx_CHCTL0/1)、通道控制寄存器(TIMERx_CHCTL2)、捕獲/比較寄存器(TIMERx_CHxCV)。接下來我們簡單介紹一下這三個寄存器。
首先是通道控制寄存器(TIMERx_CHCTL0/1),該寄存器總共有2個,TIMERx_CHCTL0和TIMERx_CHCTL1。TIMERx_CHCTL0控制 CH1 和 2,而TIMERx_CHCTL1 控制 CH3 和 4。該寄存器的各位描述如下圖。
該寄存器的有些位在不同模式下,功能不一樣,所以在上圖中,我們把寄存器分了2層,上面一層對應(yīng)輸出而下面的則對應(yīng)輸入。關(guān)于該寄存器的詳細說明,請參考《GD32F20x User Manual》。這里我們需要說明的是模式設(shè)置位CH0COMCTL,此部分由 3 位組成。總共可以配置成 7 種模式,我們使用的是 PWM 模式,所以這 3 位必須設(shè)置為 110/111。這兩種PWM 模式的區(qū)別就是輸出電平的極性相反。
接下來,我們介紹通道控制寄存器(TIMERx_CHCTL2),該寄存器控制著各個輸入輸出通道的開關(guān)。該寄存器的各位描述如下圖。
該寄存器比較簡單, 我們這里只用到了CHxEN位,該位是輸入/捕獲 2 輸出使能位,要想PWM 從 IO 口輸出,這個位必須設(shè)置為 1,所以我們需要設(shè)置該位為 1。
最后,我們介紹一下捕獲/比較寄存器(TIMERx_CHxCV),該寄存器總共有 4 個,對應(yīng) 4 個輸通道 CH0~3。因為這 4 個寄存器都差不多,我們僅以TIMERx_CH0CV為例介紹,該寄存器的各位描述如下圖。
在輸出模式下,該寄存器的值與 CNT 的值比較,根據(jù)比較結(jié)果產(chǎn)生相應(yīng)動作。利用這點,我們通過修改這個寄存器的值,就可以控制 PWM 的輸出脈寬了。
假如我們要利用 TIMER2的 CH1 輸出 PWM 來控制 DS0 的亮度,但是 TIMER2_CH1默認是接在 PA7上面的,這就可以通過重映射功能,把 TIMER2_CH1映射到 PB5 上。
GD32 的重映射控制是由復(fù)用重映射和調(diào)試 IO 配置寄存器控制的,該寄存器的各位描述如上圖。我們這里用到的是 TIMER2的重映射,從上圖可以看出,TIMER2_REMAP 是由[11:10]這 2 個位控制的。TIMER2_REMAP[1:0]重映射控制表如下表。
默認條件下,TIMER2_REMAP[1:0]為 00,是沒有重映射的,所以 TIMER2_CH0~TIMER2_CH3 分別是接在 PA6、 PA7、 PB0 和 PB1 上的,而我們想讓 TIMER2_CH1 映射到 PB5 上, 則需要設(shè)置TIMER2_REMAP[1:0]=10,即部分重映射,這里需要注意,此時TIMER2_CH0 也被映射到 PB4 上了。
TIMER定時器的四路通道CHx_O輸出PWM。
3 PWM輸出實現(xiàn)
3.1 PWM代碼分析
本章要實現(xiàn)通過TIMER2實現(xiàn)四路方波的輸出,以TIMER2_CH0 輸出 PWM 為例進行講解。下面我們介紹通過庫函數(shù)來配置該功能的步驟。
首先要提到的是,PWM 相關(guān)的函數(shù)設(shè)置在庫函數(shù)文件gd32f20x_timer.h和gd32f20x_timer.c文件中。
1) 開啟 TIMER2 時鐘以及GPIO的時鐘,配置 PA6為復(fù)用輸出。
要使用 TIMER2,我們必須先開啟 TIMER2的時鐘,這點相信大家看了這么多代碼,應(yīng)該明白了。庫函數(shù)使能 TIMER2及PA6時鐘的方法是:
/* enable the GPIOA clock */
rcu_periph_clock_enable(RCU_GPIOA);
//Enable TIMER2 clock
rcu_periph_clock_enable(RCU_TIMER2);
庫函數(shù)設(shè)置 AFIO 時鐘的方法是:
/* 開啟復(fù)用功能時鐘 */
rcu_periph_clock_enable(RCU_AF);
2) 初始化 TIMER2,設(shè)置 TIMER2的 CAR 和 PSC。
在開啟了 TIMER2 的時鐘之后,我們要設(shè)置 CAR 和 PSC 兩個寄存器的值來控制輸出 PWM 的周期。這在庫函數(shù)是通過timer_init()函數(shù)實現(xiàn)的,在上一節(jié)定時器中斷章節(jié)我們已經(jīng)有講解,這里就不詳細講解,調(diào)用的格式為:
/* TIMER2 configuration */
timer_init_struct.prescaler = 0;
timer_init_struct.alignedmode = TIMER_COUNTER_EDGE;
timer_init_struct.counterdirection = TIMER_COUNTER_UP;
timer_init_struct.period = 999;
timer_init_struct.clockdivision = TIMER_CKDIV_DIV1;
timer_init_struct.repetitioncounter = 0;
timer_init(TIMER2, &timer_init_struct);
3) 設(shè)置 TIMER2_CH0的 PWM 模式,使能 TIMER2的 CH0輸出。
接下來,我們要設(shè)置 TIMER2_CH0為 PWM 模式(默認是凍結(jié)的),在庫函數(shù)中,PWM通道設(shè)置是通過函數(shù) timer_channel_output_config()來設(shè)置的,我們直接來看看結(jié)構(gòu)體 timer_oc_parameter_struct的定義:
/* channel output parameter structure definitions */
typedef struct {
uint16_t outputstate; /*!< channel output state */
uint16_t outputnstate; /*!< channel complementary output state */
uint16_t ocpolarity; /*!< channel output polarity */
uint16_t ocnpolarity; /*!< channel complementary output polarity */
uint16_t ocidlestate; /*!< idle state of channel output */
uint16_t ocnidlestate; /*!< idle state of channel complementary output */
} timer_oc_parameter_struct;
該結(jié)構(gòu)體主要配置通道的狀態(tài),極性等,還需要設(shè)置占空比等配置,不同的通道需要分別設(shè)置。
/* PWM Mode configuration: Channel0 */
timer_channel_output_config(TIMER2, TIMER_CH_0, &timer_oc_init_struct);
/* 通道2占空比設(shè)置 */
timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, CH0CV_Val);
/* PWM模式0 */
timer_channel_output_mode_config(TIMER2,TIMER_CH_0,TIMER_OC_MODE_PWM0);
/* 不使用輸出比較影子寄存器 */
timer_channel_output_shadow_config(TIMER2,TIMER_CH_0,TIMER_OC_SHADOW_DISABLE);
4) 使能 TIM3。
我們需要使能 TIMER2。使能 TIMER2的方法前面已經(jīng)講解過:
timer_enable(TIMER2);
最后看下主函數(shù)代碼:
/*
brief main function
param[in] none
param[out] none
retval none
*/
int main(void)
{
//systick init
sysTick_init();
/* configure the TIMER peripheral */
pwm_init ();
while(1)
{
}
}
是不是很簡單,這里進行了PWM初始化,最核心的就是timer2_init()函數(shù),其代碼如下:
/*
brief configure the TIMER peripheral
param[in] none
param[out] none
retval none
*/
void pwm_init(void)
{
/* TIMER2 configuration: generate PWM signals with different duty cycles*/
/* 定義一個定時器初始化結(jié)構(gòu)體 */
timer_parameter_struct timer_init_struct;
/* 定義一個定時器輸出比較參數(shù)結(jié)構(gòu)體*/
timer_oc_parameter_struct timer_oc_init_struct;
/* PWM信號電平跳變值 */
uint16_t CH0CV_Val = 500;
uint16_t CH1CV_Val = 375;
uint16_t CH2CV_Val = 250;
uint16_t CH3CV_Val = 125;
/* -----------------------------------------------------------------------
TIMER2 Channel0 duty cycle = (TIMER2_CH0CV/ TIMER2_CAR+1)* 100% = 50%
TIMER2 Channel1 duty cycle = (TIMER2_CH1CV/ TIMER2_CAR+1)* 100% = 37.5%
TIMER2 Channel2 duty cycle = (TIMER2_CH2CV/ TIMER2_CAR+1)* 100% = 25%
TIMER2 Channel3 duty cycle = (TIMER2_CH3CV/ TIMER2_CAR+1)* 100% = 12.5%
----------------------------------------------------------------------- */
// gpio init
timer_gpio_init();
//Enable TIMER2 clock
rcu_periph_clock_enable(RCU_TIMER2);
/* 開啟復(fù)用功能時鐘 */
rcu_periph_clock_enable(RCU_AF);
timer_deinit(TIMER2);
/* TIMER2 configuration */
timer_init_struct.prescaler = 0;
timer_init_struct.alignedmode = TIMER_COUNTER_EDGE;
timer_init_struct.counterdirection = TIMER_COUNTER_UP;
timer_init_struct.period = 999;
timer_init_struct.clockdivision = TIMER_CKDIV_DIV1;
timer_init_struct.repetitioncounter = 0;
timer_init(TIMER2, &timer_init_struct);
/* PWM初始化 */
timer_oc_init_struct.outputstate = TIMER_CCX_ENABLE; /* 通道使能 */
timer_oc_init_struct.outputnstate = TIMER_CCXN_DISABLE; /* 通道互補輸出使能(定時器2無效) */
timer_oc_init_struct.ocpolarity = TIMER_OC_POLARITY_HIGH; /* 通道極性 */
timer_oc_init_struct.ocnpolarity = TIMER_OCN_POLARITY_HIGH;/* 互補通道極性(定時器2無效)*/
timer_oc_init_struct.ocidlestate = TIMER_OC_IDLE_STATE_LOW;/* 通道空閑狀態(tài)輸出(定時器2無效)*/
timer_oc_init_struct.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;/*互補通道空閑狀態(tài)輸出(定時器2無效) */
/* PWM Mode configuration: Channel0 */
timer_channel_output_config(TIMER2, TIMER_CH_0, &timer_oc_init_struct);
/* 通道2占空比設(shè)置 */
timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, CH0CV_Val);
/* PWM模式0 */
timer_channel_output_mode_config(TIMER2,TIMER_CH_0,TIMER_OC_MODE_PWM0);
/* 不使用輸出比較影子寄存器 */
timer_channel_output_shadow_config(TIMER2,TIMER_CH_0,TIMER_OC_SHADOW_DISABLE);
/* PWM Mode configuration: Channel1 */
timer_channel_output_config(TIMER2, TIMER_CH_1, &timer_oc_init_struct);
/* 通道2占空比設(shè)置 */
timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_1, CH1CV_Val);
/* PWM模式0 */
timer_channel_output_mode_config(TIMER2,TIMER_CH_1,TIMER_OC_MODE_PWM0);
/* 不使用輸出比較影子寄存器 */
timer_channel_output_shadow_config(TIMER2,TIMER_CH_1,TIMER_OC_SHADOW_DISABLE);
/* PWM Mode configuration: Channel2 */
timer_channel_output_config(TIMER2, TIMER_CH_2, &timer_oc_init_struct);
/* 通道2占空比設(shè)置 */
timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_2, CH2CV_Val);
/* PWM模式0 */
timer_channel_output_mode_config(TIMER2,TIMER_CH_2,TIMER_OC_MODE_PWM0);
/* 不使用輸出比較影子寄存器 */
timer_channel_output_shadow_config(TIMER2,TIMER_CH_2,TIMER_OC_SHADOW_DISABLE);
/* PWM Mode configuration: Channel3 */
timer_channel_output_config(TIMER2, TIMER_CH_3, &timer_oc_init_struct);
/* 通道2占空比設(shè)置 */
timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_3, CH3CV_Val);
/* PWM模式0 */
timer_channel_output_mode_config(TIMER2,TIMER_CH_3,TIMER_OC_MODE_PWM0);
/* 不使用輸出比較影子寄存器 */
timer_channel_output_shadow_config(TIMER2,TIMER_CH_3,TIMER_OC_SHADOW_DISABLE);
/* 自動重裝載影子比較器使能 */
timer_auto_reload_shadow_enable(TIMER2);
/* TIMER2 enable */
timer_enable(TIMER2);
}
3.2 PWM周期、占空比分析
根據(jù)前面的參數(shù)配置,我們可以算出PWM的輸出周期:
PWM=1/(Tclk/(psc+1))*(arr+1)
這里我們 arr=999 psc=0 Tclk=120Mhz ,
PWM=1/(120Mhz/(1))*(999+1)=1/120ms
因此PWM的輸出頻率120KHz,周期是8.3us。
PWM的占空比為:
Dutycycle=(CHxCV/CAR+1)* 100%
PWM自動重裝值為999,四個通道的跳變值分別為500,375,250,125。因此,TIMER2的四個通道的占空比分別為50%,37.5%,25%,12.5%。
4 PWM輸出的實驗現(xiàn)象
在前面我們輸出了TIMER2的通道0(PA6)、1(PA7)、2(PB0)、3(PB1)不同占空比的 PWM 信號。接下來就看看PWM的輸出,PWM 信號可以通過示波器看到,下面筆者就是用邏輯分析儀查看波形。
首先筆者使用的邏輯分析儀是Kingst LA5016,當然啦,其他的也可以,關(guān)于邏輯分析儀的相關(guān)使用筆者這里就不介紹了,可以查看官方資料。
首先將通道 0(PA6)、1(PA7)、2(PB0)、3(PB1)分別接到邏輯分析儀的CH0 – CH3,然后下載程序到板子中,打開Kingst VIS,然后進行采樣。
我們就可以看到不同通道的實際周期,占空比等信息。
從上圖可以看到,實際測量的頻率和占空比和理論是相符的。
-
PWM
+關(guān)注
關(guān)注
114文章
5141瀏覽量
213390 -
定時器
+關(guān)注
關(guān)注
23文章
3237瀏覽量
114467 -
Cortex-M
+關(guān)注
關(guān)注
2文章
227瀏覽量
29726 -
GD32
+關(guān)注
關(guān)注
7文章
403瀏覽量
24219
發(fā)布評論請先 登錄
相關(guān)推薦
評論