第32章 TIM—高級定時器
全套200集視頻教程和1000頁PDF教程請到秉火論壇下載:www.firebbs.cn
野火視頻教程優酷觀看網址:http://i.youku.com/firege
本章參考資料:《STM32F4xx 中文參考手冊》、《STM32F4xx規格書》、庫幫助文檔《stm32f4xx_dsp_stdperiph_lib_um.chm》。
學習本章時,配合《STM32F4xx 中文參考手冊》高級定時器章節一起閱讀,效果會更佳,特別是涉及到寄存器說明的部分。
特別說明,本書內容是以STM32F42x系列控制器資源講解。
上一章我們講解了基本定時器功能,基本定時器功能簡單,理解起來也容易。高級控制定時器包含了通用定時器的功能,再加上已經有了基本定時器基礎的基礎,如果再把通用定時器單獨拿出來講那內容有很多重復,實際效果不是很好,所以通用定時器不作為獨立章節講解,可以在理解了高級定時器后參考《STM32F4xx中文參考手冊》通用定時器章節內容理解即可。
32.1 高級控制定時器
高級控制定時器(TIM1和TIM8)和通用定時器在基本定時器的基礎上引入了外部引腳,可以輸入捕獲和輸出比較功能。高級控制定時器比通用定時器增加了可編程死區互補輸出、重復計數器、帶剎車(斷路)功能,這些功能都是針對工業電機控制方面。這幾個功能在本書不做詳細的介紹,主要介紹常用的輸入捕獲和輸出比較功能。
高級控制定時器時基單元包含一個16位自動重載計數器ARR,一個16位的計數器CNT,可向上/下計數,一個16位可編程預分頻器PSC,預分頻器時鍾源有多種可選,有內部的時鍾、外部時鍾。還有一個8位的重復計數器RCR,這樣最高可實現40位的可編程定時。
STM32F429IGT6的高級/通用定時器的IO分配具體見表 01。配套開發板因為IO資源緊缺,定時器的IO很多已經復用它途,故下表中的IO只有部分可用於定時器的實驗。
表 01 高級控制和通用定時器通道引腳分布
|
高級控制 |
通用定時器 |
||||||||||
TIM1 |
TIM8 |
TIM2 |
TIM5 |
TIM3 |
TIM4 |
TIM9 |
TIM10 |
TIM11 |
TIM12 |
TIM13 |
TIM14 |
|
CH1 |
PA8/PE9/PC9 |
PC6/PI5 |
PA0/PA5/PA15 |
PA0/PH10 |
PA6/PC6/PB4 |
PD12/PB6 |
PE5/PA2 |
PF6/PB8 |
PF7/PB9 |
PH6/PB14 |
PF8/PA6 |
PF9/PA7 |
CH1N |
PA7/PE8/PB13 |
PA5/PA7/PH13 |
|
|
|
|
|
|
|
|
|
|
CH2 |
PE11/PA9 |
PC7/PI6 |
PA1/PB3 |
PA1/PH11 |
PA7/PC7/PB5 |
PD13/PB7 |
PE6/PA3 |
|
|
PH9/PB15 |
|
|
CH2N |
PB0/PE10/PB14 |
PB0/PB14/PH14 |
|
|
|
|
|
|
|
|
|
|
CH3 |
PE13/PA10 |
PC8/PI7 |
PA2/PB10 |
PA2/PH12 |
PB0/PC8 |
PD14/PB8 |
|
|
|
|
|
|
CH3N |
PB1/PE12/PB15 |
PB1/PB15/PH15 |
|
|
|
|
|
|
|
|
|
|
CH4 |
PE14/PA11 |
PC9/PI2 |
PA3/PB11 |
PA3/PI0 |
PB1/PC9 |
PD15/PB9 |
|
|
|
|
|
|
ETR |
PE7/PA12 |
PA0/PI3 |
PA0/PA5/PA15 |
|
PD2 |
PE0 |
|
|
|
|
|
|
BKIN |
PA6/PE15/PB12 |
PA6/PI4 |
|
|
|
|
|
|
|
|
|
|
32.2 高級控制定時器功能框圖
高級控制定時器功能框圖包含了高級控制定時器最核心內容,掌握了功能框圖,對高級控制定時器就有一個整體的把握,在編程時思路就非常清晰,見圖 01。
關於圖中帶陰影的寄存器,即帶有影子寄存器,指向左下角的事件更新圖標以及指向右上角的中斷和DMA輸出標志在上一章已經做了解釋,這里就不再介紹。
圖 01 高級控制定時器功能框圖
1. ①時鍾源
高級控制定時器有四個時鍾源可選:
內部時鍾源CK_INT
外部時鍾模式1:外部輸入引腳TIx(x=1,2,3,4)
外部時鍾模式2:外部觸發輸入ETR
內部觸發輸入
內部時鍾源(CK_INT)
內部時鍾CK_INT即來自於芯片內部,等於180M,一般情況下,我們都是使用內部時鍾。當從模式控制寄存器TIMx_SMCR的SMS位等於000時,則使用內部時鍾。
外部時鍾模式1
圖 02 外部時鍾模式1框圖
①:時鍾信號輸入引腳
當使用外部時鍾模式1的時候,時鍾信號來自於定時器的輸入通道,總共有4個,分別為TI1/2/3/4,即TIMx_CH1/2/3/4。具體使用哪一路信號,由TIM_CCMx的位CCxS[1:0]配置,其中CCM1控制TI1/2,CCM2控制TI3/4。
②:濾波器
如果來自外部的時鍾信號的頻率過高或者混雜有高頻干擾信號的話,我們就需要使用濾波器對ETRP信號重新采樣,來達到降頻或者去除高頻干擾的目的,具體的由TIMx_CCMx的位ICxF[3:0]配置。
③:邊沿檢測
邊沿檢測的信號來自於濾波器的輸出,在成為觸發信號之前,需要進行邊沿檢測,決定是上升沿有效還是下降沿有效,具體的由TIMx_CCER的位CCxP和CCxNP配置。
④:觸發選擇
當使用外部時鍾模式1時,觸發源有兩個,一個是濾波后的定時器輸入1(TI1FP1)和濾波后的定時器輸入2(TI2FP2),具體的由TIMxSMCR的位TS[2:0]配置。
⑤:從模式選擇
選定了觸發源信號后,最后我們需把信號連接到TRGI引腳,讓觸發信號成為外部時鍾模式1的輸入,最終等於CK_PSC,然后驅動計數器CNT計數。具體的配置TIMx_SMCR的位SMS[2:0]為000即可選擇外部時鍾模式1。
⑥:使能計數器
經過上面的5個步驟之后,最后我們只需使能計數器開始計數,外部時鍾模式1的配置就算完成。使能計數器由TIMx_CR1的位CEN配置。
外部時鍾模式2
圖 03 外部時鍾模式2框圖
①:時鍾信號輸入引腳
當使用外部時鍾模式2的時候,時鍾信號來自於定時器的特定輸入通道TIMx_ETR,只有1個。
②:外部觸發極性
來自ETR引腳輸入的信號可以選擇為上升沿或者下降沿有效,具體的由TIMx_SMCR的位ETP配置。
③:外部觸發預分頻器
由於ETRP的信號的頻率不能超過TIMx_CLK(180M)的1/4,當觸發信號的頻率很高的情況下,就必須使用分頻器來降頻,具體的由 TIMx_SMCR的位ETPS[1:0]配置。
④:濾波器
如果ETRP的信號的頻率過高或者混雜有高頻干擾信號的話,我們就需要使用濾波器對ETRP信號重新采樣,來達到降頻或者去除高頻干擾的目的。具體的由TIMx_SMCR的位ETF[3:0]配置,其中的fDTS是由內部時鍾CK_INT分頻得到,具體的由TIMx_CR1的位CKD[1:0]配置。
⑤:從模式選擇
經過濾波器濾波的信號連接到ETRF引腳后,觸發信號成為外部時鍾模式2的輸入,最終等於CK_PSC,然后驅動計數器CNT計數。具體的配置TIMx_SMCR的位ECE為1即可選擇外部時鍾模式2。
⑥:使能計數器
經過上面的5個步驟之后,最后我們只需使能計數器開始計數,外部時鍾模式2的配置就算完成。使能計數器由TIMx_CR1的位CEN配置。
內部觸發輸入
內部觸發輸入是使用一個定時器作為另一個定時器的預分頻器。硬件上高級控制定時器和通用定時器在內部連接在一起,可以實現定時器同步或級聯。主模式的定時器可以對從模式定時器執行復位、啟動、停止或提供時鍾。高級控制定時器和部分通用定時器(TIM2至TIM5)可以設置為主模式或從模式,TIM9和TIM10可設置為從模式。
圖 04為主模式定時器(TIM1)為從模式定時器(TIM2)提供時鍾,即TIM1用作TIM2的預分頻器。
圖 04 TIM1用作TIM2的預分頻器
2. ②控制器
高級控制定時器控制器部分包括觸發控制器、從模式控制器以及編碼器接口。觸發控制器用來針對片內外設輸出觸發信號,比如為其它定時器提供時鍾和觸發DAC/ADC轉換。
編碼器接口專門針對編碼器計數而設計。從模式控制器可以控制計數器復位、啟動、遞增/遞減、計數。
3. ③時基單元
圖 05 高級定時器時基單元
高級控制定時器時基單元包括四個寄存器,分別是計數器寄存器(CNT)、預分頻器寄存器(PSC)、自動重載寄存器(ARR)和重復計數器寄存器(RCR)。其中重復計數器RCR是高級定時器獨有,通用和基本定時器沒有。前面三個寄存器都是16位有效,TIMx_RCR寄存器是8位有效。
預分頻器PSC
預分頻器PSC,有一個輸入時鍾CK_PSC和一個輸出時鍾CK_CNT。輸入時鍾CK_PSC就是上面時鍾源的輸出,輸出CK_CNT則用來驅動計數器CNT計數。通過設置預分頻器PSC的值可以得到不同的CK_CNT,實際計算為:fCK_CNT等於fCK_PSC/(PSC[15:0]+1),可以實現1至65536分頻。
計數器CNT
高級控制定時器的計數器有三種計數模式,分別為遞增計數模式、遞減計數模式和遞增/遞減(中心對齊)計數模式。
(1) 遞增計數模式下,計數器從0開始計數,每來一個CK_CNT脈沖計數器就增加1,直到計數器的值與自動重載寄存器ARR值相等,然后計數器又從0開始計數並生成計數器上溢事件,計數器總是如此循環計數。如果禁用重復計數器,在計數器生成上溢事件就馬上生成更新事件(UEV);如果使能重復計數器,每生成一次上溢事件重復計數器內容就減1,直到重復計數器內容為0時才會生成更新事件。
(2) 遞減計數模式下,計數器從自動重載寄存器ARR值開始計數,每來一個CK_CNT脈沖計數器就減1,直到計數器值為0,然后計數器又從自動重載寄存器ARR值開始遞減計數並生成計數器下溢事件,計數器總是如此循環計數。如果禁用重復計數器,在計數器生成下溢事件就馬上生成更新事件;如果使能重復計數器,每生成一次下溢事件重復計數器內容就減1,直到重復計數器內容為0時才會生成更新事件。
(3) 中心對齊模式下,計數器從0開始遞增計數,直到計數值等於(ARR-1)值生成計數器上溢事件,然后從ARR值開始遞減計數直到1生成計數器下溢事件。然后又從0開始計數,如此循環。每次發生計數器上溢和下溢事件都會生成更新事件。
自動重載寄存器ARR
自動重載寄存器ARR用來存放與計數器CNT比較的值,如果兩個值相等就遞減重復計數器。可以通過TIMx_CR1寄存器的ARPE位控制自動重載影子寄存器功能,如果ARPE位置1,自動重載影子寄存器有效,只有在事件更新時才把TIMx_ARR值賦給影子寄存器。如果ARPE位為0,則修改TIMx_ARR值馬上有效。
重復計數器RCR
在基本/通用定時器發生上/下溢事件時直接就生成更新事件,但對於高級控制定時器卻不是這樣,高級控制定時器在硬件結構上多出了重復計數器,在定時器發生上溢或下溢事件是遞減重復計數器的值,只有當重復計數器為0時才會生成更新事件。在發生N+1個上溢或下溢事件(N為RCR的值)時產生更新事件。
4. ④輸入捕獲
圖 06 輸入捕獲功能框圖
輸入捕獲可以對輸入的信號的上升沿,下降沿或者雙邊沿進行捕獲,常用的有測量輸入信號的脈寬和測量PWM輸入信號的頻率和占空比這兩種。
輸入捕獲的大概的原理就是,當捕獲到信號的跳變沿的時候,把計數器CNT的值鎖存到捕獲寄存器CCR中,把前后兩次捕獲到的CCR寄存器中的值相減,就可以算出脈寬或者頻率。如果捕獲的脈寬的時間長度超過你的捕獲定時器的周期,就會發生溢出,這個我們需要做額外的處理。
①輸入通道
需要被測量的信號從定時器的外部引腳TIMx_CH1/2/3/4進入,通常叫TI1/2/3/4,在后面的捕獲講解中對於要被測量的信號我們都以TIx為標准叫法。
②輸入濾波器和邊沿檢測器
當輸入的信號存在高頻干擾的時候,我們需要對輸入信號進行濾波,即進行重新采樣,根據采樣定律,采樣的頻率必須大於等於兩倍的輸入信號。比如輸入的信號為1M,又存在高頻的信號干擾,那么此時就很有必要進行濾波,我們可以設置采樣頻率為2M,這樣可以在保證采樣到有效信號的基礎上把高於2M的高頻干擾信號過濾掉。
濾波器的配置由CR1寄存器的位CKD[1:0]和CCMR1/2的位ICxF[3:0]控制。從ICxF位的描述可知,采樣頻率fSAMPLE可以由fCK_INT和fDTS分頻后的時鍾提供,其中是fCK_INT內部時鍾,fDTS是fCK_INT經過分頻后得到的頻率,分頻因子由CKD[1:0]決定,可以是不分頻,2分頻或者是4分頻。
邊沿檢測器用來設置信號在捕獲的時候是什么邊沿有效,可以是上升沿,下降沿,或者是雙邊沿,具體的由CCER寄存器的位CCxP和CCxNP決定。
③捕獲通道
捕獲通道就是圖中的IC1/2/3/4,每個捕獲通道都有相對應的捕獲寄存器CCR1/2/3/4,當發生捕獲的時候,計數器CNT的值就會被鎖存到捕獲寄存器中。
這里我們要搞清楚輸入通道和捕獲通道的區別,輸入通道是用來輸入信號的,捕獲通道是用來捕獲輸入信號的通道,一個輸入通道的信號可以同時輸入給兩個捕獲通道。比如輸入通道TI1的信號經過濾波邊沿檢測器之后的TI1FP1和TI1FP2可以進入到捕獲通道IC1和IC2,其實這就是我們后面要講的PWM輸入捕獲,只有一路輸入信號(TI1)卻占用了兩個捕獲通道(IC1和IC2)。當只需要測量輸入信號的脈寬時候,用一個捕獲通道即可。輸入通道和捕獲通道的映射關系具體由寄存器CCMRx的位CCxS[1:0]配置。
④的預分頻器
ICx的輸出信號會經過一個預分頻器,用於決定發生多少個事件時進行一次捕獲。具體的由寄存器CCMRx的位ICxPSC配置,如果希望捕獲信號的每一個邊沿,則不分頻。
⑤捕獲寄存器
經過預分頻器的信號ICxPS是最終被捕獲的信號,當發生捕獲時(第一次),計數器CNT的值會被鎖存到捕獲寄存器CCR中,還會產生CCxI中斷,相應的中斷位CCxIF(在SR寄存器中)會被置位,通過軟件或者讀取CCR中的值可以將CCxIF清0。如果發生第二次捕獲(即重復捕獲:CCR 寄存器中已捕獲到計數器值且 CCxIF 標志已置 1),則捕獲溢出標志位CCxOF(在SR寄存器中)會被置位,CCxOF只能通過軟件清零。
5. ⑤輸出比較
圖 07 輸出比較功能框圖
輸出比較就是通過定時器的外部引腳對外輸出控制信號,有凍結、將通道X(x=1,2,3,4)設置為匹配時輸出有效電平、將通道X設置為匹配時輸出無效電平、翻轉、強制變為無效電平、強制變為有效電平、PWM1和PWM2這八種模式,具體使用哪種模式由寄存器CCMRx的位OCxM[2:0]配置。其中PWM模式是輸出比較中的特例,使用的也最多。
①比較寄存器
當計數器CNT的值跟比較寄存器CCR的值相等的時候,輸出參考信號OCxREF的信號的極性就會改變,其中OCxREF=1(高電平)稱之為有效電平,OCxREF=0(低電平)稱之為無效電平,並且會產生比較中斷CCxI,相應的標志位CCxIF(SR寄存器中)會置位。然后OCxREF再經過一系列的控制之后就成為真正的輸出信號OCx/OCxN。
②死區發生器
在生成的參考波形OCxREF的基礎上,可以插入死區時間,用於生成兩路互補的輸出信號OCx和OCxN,死區時間的大小具體由BDTR寄存器的位DTG[7:0]配置。死區時間的大小必須根據與輸出信號相連接的器件及其特性來調整。下面我們簡單舉例說明下帶死區的PWM信號的應用,我們以一個板橋驅動電路為例。
圖 08 半橋驅動電路
在這個半橋驅動電路中,Q1導通,Q2截止,此時我想讓Q1截止Q2導通,肯定是要先讓Q1截止一段時間之后,再等一段時間才讓Q2導通,那么這段等待的時間就稱為死區時間,因為Q1關閉需要時間(由MOS管的工藝決定)。如果Q1關閉之后,馬上打開Q2,那么此時一段時間內相當於Q1和Q2都導通了,這樣電路會短路。
圖 09是針對上面的半橋驅動電路而畫的帶死區插入的PWM信號,圖中的死區時間要根據MOS管的工藝來調節。
圖 09 帶死區插入的互補輸出
③輸出控制
圖 010 輸出比較(通道1~3)的輸出控制框圖
在輸出比較的輸出控制中,參考信號OCxREF在經過死區發生器之后會產生兩路帶死區的互補信號OCx_DT和OCxN_DT(通道1~3才有互補信號,通道4沒有,其余跟通道1~3一樣),這兩路帶死區的互補信號然后就進入輸出控制電路,如果沒有加入死區控制,那么進入輸出控制電路的信號就直接是OCxREF。
進入輸出控制電路的信號會被分成兩路,一路是原始信號,一路是被反向的信號,具體的由寄存器CCER的位CCxP和CCxNP控制。經過極性選擇的信號是否由OCx引腳輸出到外部引腳CHx/CHxN則由寄存器CCER的位CxE/CxNE配置。
如果加入了斷路(剎車)功能,則斷路和死區寄存器BDTR的MOE、OSSI和OSSR這三個位會共同影響輸出的信號。
④輸出引腳
輸出比較的輸出信號最終是通過定時器的外部IO來輸出的,分別為CH1/2/3/4,其中前面三個通道還有互補的輸出通道CH1/2/3N。更加詳細的IO說明還請查閱相關的數據手冊。
6. ⑥斷路功能
斷路功能就是電機控制的剎車功能,使能斷路功能時,根據相關控制位狀態修改輸出信號電平。在任何情況下,OCx和OCxN輸出都不能同時為有效電平,這關系到電機控制常用的H橋電路結構原因。
斷路源可以是時鍾故障事件,由內部復位時鍾控制器中的時鍾安全系統(CSS)生成,也可以是外部斷路輸入IO,兩者是或運算關系。
系統復位啟動都默認關閉斷路功能,將斷路和死區寄存器(TIMx_BDTR)的BKE為置1,使能斷路功能。可通過TIMx_BDTR 寄存器的BKP位設置設置斷路輸入引腳的有效電平,設置為1時輸入BRK為高電平有效,否則低電平有效。
發送斷路時,將產生以下效果:
TIMx_BDTR 寄存器中主輸出模式使能(MOE)位被清零,輸出處於無效、空閑或復位狀態;
根據相關控制位狀態控制輸出通道引腳電平;當使能通道互補輸出時,會根據情況自動控制輸出通道電平;
將TIMx_SR 寄存器中的 BIF位置 1,並可產生中斷和DMA傳輸請求。
如果 TIMx_BDTR 寄存器中的 自動輸出使能(AOE)位置 1,則MOE位會在發生下一個UEV事件時自動再次置 1。
1.1 輸入捕獲應用
輸入捕獲一般應用在兩個方面,一個方面是脈沖跳變沿時間測量,另一方面是PWM輸入測量。
1.1.1 測量脈寬或者頻率
圖 011 脈寬/頻率測量示意圖
1. 測量頻率
當捕獲通道TIx上出現上升沿時,發生第一次捕獲,計數器CNT的值會被鎖存到捕獲寄存器CCR中,而且還會進入捕獲中斷,在中斷服務程序中記錄一次捕獲(可以用一個標志變量來記錄),並把捕獲寄存器中的值讀取到value1中。當出現第二次上升沿時,發生第二次捕獲,計數器CNT的值會再次被鎖存到捕獲寄存器CCR中,並再次進入捕獲中斷,在捕獲中斷中,把捕獲寄存器的值讀取到value3中,並清除捕獲記錄標志。利用value3和value1的差值我們就可以算出信號的周期(頻率)。
2. 測量脈寬
當捕獲通道TIx上出現上升沿時,發生第一次捕獲,計數器CNT的值會被鎖存到捕獲寄存器CCR中,而且還會進入捕獲中斷,在中斷服務程序中記錄一次捕獲(可以用一個標志變量來記錄),並把捕獲寄存器中的值讀取到value1中。然后把捕獲邊沿改變為下降沿捕獲,目的是捕獲后面的下降沿。當下降沿到來的時候,發生第二次捕獲,計數器CNT的值會再次被鎖存到捕獲寄存器CCR中,並再次進入捕獲中斷,在捕獲中斷中,把捕獲寄存器的值讀取到value3中,並清除捕獲記錄標志。然后把捕獲邊沿設置為上升沿捕獲。
在測量脈寬過程中需要來回的切換捕獲邊沿的極性,如果測量的脈寬時間比較長,定時器就會發生溢出,溢出的時候會產生更新中斷,我們可以在中斷里面對溢出進行記錄處理。
1.1.2 PWM輸入模式
測量脈寬和頻率還有一個更簡便的方法就是使用PWM輸入模式。與上面那種只使用一個捕獲寄存器測量脈寬和頻率的方法相比,PWM輸入模式需要占用兩個捕獲寄存器。
圖 012 輸入通道和捕獲通道的關系映射圖
當使用PWM輸入模式的時候,因為一個輸入通道(TIx)會占用兩個捕獲通道(ICx),所以一個定時器在使用PWM輸入的時候最多只能使用兩個輸入通道(TIx)。
我們以輸入通道TI1工作在PWM輸入模式為例來講解下具體的工作原理,其他通道以此類推即可。
PWM信號由輸入通道TI1進入,因為是PWM輸入模式的緣故,信號會被分為兩路,一路是TI1FP1,另外一路是TI2FP2。其中一路是周期,另一路是占空比,具體哪一路信號對應周期還是占空比,得從程序上設置哪一路信號作為觸發輸入,作為觸發輸入的哪一路信號對應的就是周期,另一路就是對應占空比。作為觸發輸入的那一路信號還需要設置極性,是上升沿還是下降沿捕獲,一旦設置好觸發輸入的極性,另外一路硬件就會自動配置為相反的極性捕獲,無需軟件配置。一句話概括就是:選定輸入通道,確定觸發信號,然后設置觸發信號的極性即可,因為是PWM輸入的緣故,另一路信號則由硬件配置,無需軟件配置。
當使用PWM輸入模式的時候必須將從模式控制器配置為復位模式(配置寄存器SMCR的位SMS[2:0]來實現),即當我們啟動觸發信號開始進行捕獲的時候,同時把計數器CNT復位清零。
下面我們以一個更加具體的時序圖來分析下PWM輸入模式。
圖 013 PWM輸入模式時序
PWM信號由輸入通道TI1進入,配置TI1FP1為觸發信號,上升沿捕獲。當上升沿的時候IC1和IC2同時捕獲,計數器CNT清零,到了下降沿的時候,IC2捕獲,此時計數器CNT的值被鎖存到捕獲寄存器CCR2中,到了下一個上升沿的時候,IC1捕獲,計數器CNT的值被鎖存到捕獲寄存器CCR1中。其中CCR2測量的是脈寬,CCR1測量的是周期。
從軟件上來說,用PWM輸入模式測量脈寬和周期更容易,付出的代價是需要占用兩個捕獲寄存器。
1.2 輸出比較應用
輸出比較模式總共有8種,具體的由寄存器CCMRx的位OCxM[2:0]配置。我們這里只講解最常用的PWM模式,其他幾種模式具體的看數據手冊即可。
1.2.1 PWM輸出模式
PWM輸出就是對外輸出脈寬(即占空比)可調的方波信號,信號頻率由自動重裝寄存器ARR的值決定,占空比由比較寄存器CCR的值決定。
PWM模式分為兩種,PWM1和PWM2,總得來說是差不多,就看你怎么用而已,具體的區別見表格 01。
表格 01 PWM1與PWM2模式的區別
模式 |
計數器CNT計算方式 |
說明 |
PWM1 |
遞增 |
CNT<CCR,通道CH為有效,否則為無效 |
遞減 |
CNT>CCR,通道CH為無效,否則為有效 |
|
PWM2 |
遞增 |
CNT<CCR,通道CH為無效,否則為有效 |
遞減 |
CNT>CCR,通道CH為有效,否則為無效 |
下面我們以PWM1模式來講解,以計數器CNT計數的方向不同還分為邊沿對齊模式和中心對齊模式。PWM信號主要都是用來控制電機,一般的電機控制用的都是邊沿對齊模式,FOC電機一般用中心對齊模式。我們這里只分析這兩種模式在信號感官上(即信號波形)的區別,具體在電機控制中的區別不做討論,到了你真正需要使用的時候就會知道了。
1. PWM邊沿對齊模式
在遞增計數模式下,計數器從 0 計數到自動重載值( TIMx_ARR 寄存器的內容),然后重新
從 0 開始計數並生成計數器上溢事件
圖 014 PWM1模式的邊沿對齊波形
在邊沿對齊模式下,計數器CNT只工作在一種模式,遞增或者遞減模式。這里我們以CNT工作在遞增模式為例,在中,ARR=8,CCR=4,CNT從0開始計數,當CNT<CCR的值時,OCxREF為有效的高電平,於此同時,比較中斷寄存器CCxIF置位。當CCR=<CNT<=ARR時,OCxREF為無效的低電平。然后CNT又從0開始計數並生成計數器上溢事件,以此循環往復。
2. PWM中心對齊模式
圖 015 PWM1模式的中心對齊波形
在中心對齊模式下,計數器CNT是工作做遞增/遞減模式下。開始的時候,計數器CNT從 0 開始計數到自動重載值減1(ARR-1),生成計數器上溢事件;然后從自動重載值開始向下計數到 1 並生成計數器下溢事件。之后從0 開始重新計數。
圖 015是PWM1模式的中心對齊波形,ARR=8,CCR=4。第一階段計數器CNT工作在遞增模式下,從0開始計數,當CNT<CCR的值時,OCxREF為有效的高電平,當CCR=<CNT<<ARR時,OCxREF為無效的低電平。第二階段計數器CNT工作在遞減模式,從ARR的值開始遞減,當CNT>CCR時,OCxREF為無效的低電平,當CCR=>CNT>=1時,OCxREF為有效的高電平。
在波形圖上我們把波形分為兩個階段,第一個階段是計數器CNT工作在遞增模式的波形,這個階段我們又分為①和②兩個階段,第二個階段是計數器CNT工作在遞減模式的波形,這個階段我們又分為③和④兩個階段。要說中心對齊模式下的波形有什么特征的話,那就是①和③階段的時間相等,②和④階段的時間相等。
中心對齊模式又分為中心對齊模式1/2/3 三種,具體由寄存器CR1位CMS[1:0]配置。具體的區別就是比較中斷中斷標志位CCxIF在何時置1:中心模式1在CNT遞減計數的時候置1,中心對齊模式2在CNT遞增計數時置1,中心模式3在CNT遞增和遞減計數時都置1。
1.3 定時器初始化結構體詳解
標准庫函數對定時器外設建立了四個初始化結構體,分別為時基初始化結構體TIM_TimeBaseInitTypeDef、輸出比較初始化結構體TIM_OCInitTypeDef、輸入捕獲初始化結構體TIM_ICInitTypeDef和斷路和死區初始化結構體TIM_BDTRInitTypeDef,高級控制定時器可以用到所有初始化結構體,通用定時器不能使用TIM_BDTRInitTypeDef結構體,基本定時器只能使用時基結構體。初始化結構體成員用於設置定時器工作環境參數,並由定時器相應初始化配置函數調用,最終這些參數將會寫入到定時器相應的寄存器中。
初始化結構體和初始化庫函數配合使用是標准庫精髓所在,理解了初始化結構體每個成員意義基本上就可以對該外設運用自如。初始化結構體定義在stm32f4xx_tim.h文件中,初始化庫函數定義在stm32f4xx_tim.c文件中,編程時我們可以結合這兩個文件內注釋使用。
1. TIM_TimeBaseInitTypeDef
時基結構體TIM_TimeBaseInitTypeDef用於定時器基礎參數設置,與TIM_TimeBaseInit函數配合使用完成配置。
代碼清單 01 定時器基本初始化結構體
1 typedef struct {
2 uint16_t TIM_Prescaler; // 預分頻器
3 uint16_t TIM_CounterMode; // 計數模式
4 uint32_t TIM_Period; // 定時器周期
5 uint16_t TIM_ClockDivision; // 時鍾分頻
6 uint8_t TIM_RepetitionCounter; // 重復計算器
7 } TIM_TimeBaseInitTypeDef;
(5) TIM_Prescaler:定時器預分頻器設置,時鍾源經該預分頻器才是定時器計數時鍾CK_CNT,它設定PSC寄存器的值。計算公式為:計數器時鍾頻率 (fCK_CNT) 等於 fCK_PSC / (PSC[15:0] + 1),可實現1至65536分頻。
(6) TIM_CounterMode:定時器計數方式,可設置為向上計數、向下計數以及中心對齊。高級控制定時器允許選擇任意一種。
(7) TIM_Period:定時器周期,實際就是設定自動重載寄存器ARR的值,ARR 為要裝載到實際自動重載寄存器(即影子寄存器)的值,可設置范圍為0至65535。
(8) TIM_ClockDivision:時鍾分頻,設置定時器時鍾CK_INT頻率與死區發生器以及數字濾波器采樣時鍾頻率分頻比。可以選擇1、2、4分頻。
(9) TIM_RepetitionCounter:重復計數器,只有8位,只存在於高級定時器。
2. TIM_OCInitTypeDef
輸出比較結構體TIM_OCInitTypeDef用於輸出比較模式,與TIM_OCxInit函數配合使用完成指定定時器輸出通道初始化配置。高級控制定時器有四個定時器通道,使用時都必須單獨設置。
代碼清單 02 定時器比較輸出初始化結構體
1 typedef struct {
2 uint16_t TIM_OCMode; // 比較輸出模式
3 uint16_t TIM_OutputState; // 比較輸出使能
4 uint16_t TIM_OutputNState; // 比較互補輸出使能
5 uint32_t TIM_Pulse; // 脈沖寬度
6 uint16_t TIM_OCPolarity; // 輸出極性
7 uint16_t TIM_OCNPolarity; // 互補輸出極性
8 uint16_t TIM_OCIdleState; // 空閑狀態下比較輸出狀態
9 uint16_t TIM_OCNIdleState; // 空閑狀態下比較互補輸出狀態
10 } TIM_OCInitTypeDef;
(1) TIM_OCMode:比較輸出模式選擇,總共有八種,常用的為PWM1/PWM2。它設定CCMRx寄存器OCxM[2:0]位的值。
(2) TIM_OutputState:比較輸出使能,決定最終的輸出比較信號OCx是否通過外部引腳輸出。它設定TIMx_CCER寄存器CCxE/CCxNE位的值。
(3) TIM_OutputNState:比較互補輸出使能,決定OCx的互補信號OCxN是否通過外部引腳輸出。它設定CCER寄存器CCxNE位的值。
(4) TIM_Pulse:比較輸出脈沖寬度,實際設定比較寄存器CCR的值,決定脈沖寬度。可設置范圍為0至65535。
(5) TIM_OCPolarity:比較輸出極性,可選OCx為高電平有效或低電平有效。它決定着定時器通道有效電平。它設定CCER寄存器的CCxP位的值。
(6) TIM_OCNPolarity:比較互補輸出極性,可選OCxN為高電平有效或低電平有效。它設定TIMx_CCER寄存器的CCxNP位的值。
(7) TIM_OCIdleState:空閑狀態時通道輸出電平設置,可選輸出1或輸出0,即在空閑狀態(BDTR_MOE位為0)時,經過死區時間后定時器通道輸出高電平或低電平。它設定CR2寄存器的OISx位的值。
(8) TIM_OCNIdleState:空閑狀態時互補通道輸出電平設置,可選輸出1或輸出0,即在空閑狀態(BDTR_MOE位為0)時,經過死區時間后定時器互補通道輸出高電平或低電平,設定值必須與TIM_OCIdleState相反。它設定是CR2寄存器的OISxN位的值。
3. TIM_ICInitTypeDef
輸入捕獲結構體TIM_ICInitTypeDef用於輸入捕獲模式,與TIM_ICInit函數配合使用完成定時器輸入通道初始化配置。如果使用PWM輸入模式需要與TIM_PWMIConfig函數配合使用完成定時器輸入通道初始化配置。
代碼清單 03 定時器輸入捕獲初始化結構體
1 typedef struct {
2 uint16_t TIM_Channel; // 輸入通道選擇
3 uint16_t TIM_ICPolarity; // 輸入捕獲觸發選擇
4 uint16_t TIM_ICSelection; // 輸入捕獲選擇
5 uint16_t TIM_ICPrescaler; // 輸入捕獲預分頻器
6 uint16_t TIM_ICFilter; // 輸入捕獲濾波器
7 } TIM_ICInitTypeDef;
(1) TIM_Channel:捕獲通道ICx選擇,可選TIM_Channel_1、TIM_Channel_2、TIM_Channel_3或TIM_Channel_4四個通道。它設定CCMRx寄存器CCxS位 的值。
(2) TIM_ICPolarity:輸入捕獲邊沿觸發選擇,可選上升沿觸發、下降沿觸發或邊沿跳變觸發。它設定CCER寄存器CCxP位和CCxNP位的值。
(3) TIM_ICSelection:輸入通道選擇,捕獲通道ICx的信號可來自三個輸入通道,分別為TIM_ICSelection_DirectTI、TIM_ICSelection_IndirectTI或TIM_ICSelection_TRC,具體的區別見圖 016。它設定CCRMx寄存器的CCxS[1:0]位的值。
圖 016輸入通道與捕獲通道IC的映射圖
(4) TIM_ICPrescaler:輸入捕獲通道預分頻器,可設置1、2、4、8分頻,它設定CCMRx寄存器的ICxPSC[1:0]位的值。如果需要捕獲輸入信號的每個有效邊沿,則設置1分頻即可。
(5) TIM_ICFilter:輸入捕獲濾波器設置,可選設置0x0至0x0F。它設定CCMRx寄存器ICxF[3:0]位的值。一般我們不使用濾波器,即設置為0。
4. TIM_BDTRInitTypeDef
斷路和死區結構體TIM_BDTRInitTypeDef用於斷路和死區參數的設置,屬於高級定時器專用,用於配置斷路時通道輸出狀態,以及死區時間。它與TIM_BDTRConfig函數配置使用完成參數配置。這個結構體的成員只對應BDTR這個寄存器,有關成員的具體使用配置請參考手冊BDTR寄存器的詳細描述。
代碼清單 04 斷路和死區初始化結構體
1 typedef struct {
2 uint16_t TIM_OSSRState; // 運行模式下的關閉狀態選擇
3 uint16_t TIM_OSSIState; // 空閑模式下的關閉狀態選擇
4 uint16_t TIM_LOCKLevel; // 鎖定配置
5 uint16_t TIM_DeadTime; // 死區時間
6 uint16_t TIM_Break; // 斷路輸入使能控制
7 uint16_t TIM_BreakPolarity; // 斷路輸入極性
8 uint16_t TIM_AutomaticOutput; // 自動輸出使能
9 } TIM_BDTRInitTypeDef;
(1) TIM_OSSRState:運行模式下的關閉狀態選擇,它設定BDTR寄存器OSSR位的值。
(2) TIM_OSSIState:空閑模式下的關閉狀態選擇,它設定BDTR寄存器OSSI位的值。
(3) TIM_LOCKLevel:鎖定級別配置, BDTR寄存器LOCK[1:0]位的值。
(4) TIM_DeadTime:配置死區發生器,定義死區持續時間,可選設置范圍為0x0至0xFF。它設定BDTR寄存器DTG[7:0]位的值。
(5) TIM_Break:斷路輸入功能選擇,可選使能或禁止。它設定BDTR寄存器BKE位的值。
(6) TIM_BreakPolarity:斷路輸入通道BRK極性選擇,可選高電平有效或低電平有效。它設定BDTR寄存器BKP位的值。
(7) TIM_AutomaticOutput:自動輸出使能,可選使能或禁止,它設定BDTR寄存器AOE位的值。
1.4 PWM互補輸出實驗
輸出比較模式比較多,這里我們以PWM輸出為例講解,並通過示波器來觀察波形。實驗中不僅在主輸出通道輸出波形,還在互補通道輸出與主通道互補的的波形,並且添加了斷路和死區功能。
1.4.1 硬件設計
根據開發板引腳使用情況,並且參考表 01中定時器引腳信息,使用TIM8的通道1及其互補通道作為本實驗的波形輸出通道,對應選擇PC6和PA5引腳。將示波器的兩個輸入通道分別與PC6和PA5引腳短接,用於觀察波形,還有注意共地。
為增加斷路功能,需要用到TIM8_BKIN引腳,這里選擇PA6引腳。程序我們設置該引腳為低電平有效,所以先使用杜邦線將該引腳與開發板上3.3V短接。
另外,實驗用到兩個按鍵用於調節PWM的占空比大小,直接使用開發板上獨立按鍵即可,電路參考獨立按鍵相關章節。
1.4.2 軟件設計
這里只講解核心的部分代碼,有些變量的設置,頭文件的包含等並沒有涉及到,完整的代碼請參考本章配套的工程。我們創建了兩個文件:bsp_advance_tim.c和bsp_advance_tim.h文件用來存定時器驅動程序及相關宏定義。
1. 編程要點
(1) 定時器 IO 配置
(2) 定時器時基結構體TIM_TimeBaseInitTypeDef配置
(3) 定時器輸出比較結構體TIM_OCInitTypeDef配置
(4) 定時器斷路和死區結構體TIM_BDTRInitTypeDef配置
2. 軟件分析
宏定義
代碼清單 05 宏定義
1 /* 定時器 */
2 #define ADVANCE_TIM TIM8
3 #define ADVANCE_TIM_CLK RCC_APB2Periph_TIM8
4
5 /* TIM8通道1輸出引腳 */
6 #define ADVANCE_OCPWM_PIN GPIO_Pin_6
7 #define ADVANCE_OCPWM_GPIO_PORT GPIOC
8 #define ADVANCE_OCPWM_GPIO_CLK RCC_AHB1Periph_GPIOC
9 #define ADVANCE_OCPWM_PINSOURCE GPIO_PinSource6
10 #define ADVANCE_OCPWM_AF GPIO_AF_TIM8
11
12 /* TIM8通道1互補輸出引腳 */
13 #define ADVANCE_OCNPWM_PIN GPIO_Pin_5
14 #define ADVANCE_OCNPWM_GPIO_PORT GPIOA
15 #define ADVANCE_OCNPWM_GPIO_CLK RCC_AHB1Periph_GPIOA
16 #define ADVANCE_OCNPWM_PINSOURCE GPIO_PinSource5
17 #define ADVANCE_OCNPWM_AF GPIO_AF_TIM8
18
19 /* TIM8斷路輸入引腳 */
20 #define ADVANCE_BKIN_PIN GPIO_Pin_6
21 #define ADVANCE_BKIN_GPIO_PORT GPIOA
22 #define ADVANCE_BKIN_GPIO_CLK RCC_AHB1Periph_GPIOA
23 #define ADVANCE_BKIN_PINSOURCE GPIO_PinSource6
24 #define ADVANCE_BKIN_AF GPIO_AF_TIM8
使用宏定義非常方便程序升級、移植。如果使用不同的定時器IO,修改這些宏即可。
定時器復用功能引腳初始化
代碼清單 06 定時器復用功能引腳初始化
1 static void TIMx_GPIO_Config(void)
2 {
3 /*定義一個GPIO_InitTypeDef類型的結構體*/
4 GPIO_InitTypeDef GPIO_InitStructure;
5
6 /*開啟定時器相關的GPIO外設時鍾*/
7 RCC_AHB1PeriphClockCmd (ADVANCE_OCPWM_GPIO_CLK |
8 ADVANCE_OCNPWM_GPIO_CLK |
9 ADVANCE_BKIN_GPIO_CLK,
10 ENABLE);
11 /* 指定引腳復用功能 */
12 GPIO_PinAFConfig(ADVANCE_OCPWM_GPIO_PORT,
13 ADVANCE_OCPWM_PINSOURCE,
14 ADVANCE_OCPWM_AF);
15 GPIO_PinAFConfig(ADVANCE_OCNPWM_GPIO_PORT,
16 ADVANCE_OCNPWM_PINSOURCE,
17 ADVANCE_OCNPWM_AF);
18 GPIO_PinAFConfig(ADVANCE_BKIN_GPIO_PORT,
19 ADVANCE_BKIN_PINSOURCE,
20 ADVANCE_BKIN_AF);
21
22 /* 定時器功能引腳初始化 */
23 GPIO_InitStructure.GPIO_Pin = ADVANCE_OCPWM_PIN;
24 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
25 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
26 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
27 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
28 GPIO_Init(ADVANCE_OCPWM_GPIO_PORT, &GPIO_InitStructure);
29
30 GPIO_InitStructure.GPIO_Pin = ADVANCE_OCNPWM_PIN;
31 GPIO_Init(ADVANCE_OCNPWM_GPIO_PORT, &GPIO_InitStructure);
32
33 GPIO_InitStructure.GPIO_Pin = ADVANCE_BKIN_PIN;
34 GPIO_Init(ADVANCE_BKIN_GPIO_PORT, &GPIO_InitStructure);
35 }
定時器通道引腳使用之前必須設定相關參數,這選擇復用功能,並指定到對應的定時器。使用GPIO之前都必須開啟相應端口時鍾。
定時器模式配置
代碼清單 07 定時器模式配置
1 static void TIM_Mode_Config(void)
2 {
3 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
4 TIM_OCInitTypeDef TIM_OCInitStructure;
5 TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
6
7 //開啟TIMx_CLK,x[1,8]時鍾
8 RCC_APB2PeriphClockCmd(ADVANCE_TIM_CLK, ENABLE);
9
10 //累計 TIM_Period個后產生一個更新或者中斷
11 //當定時器從0計數到1023,即為1024次,為一個定時周期
12 TIM_TimeBaseStructure.TIM_Period = 1024-1;
13 //高級控制定時器時鍾源TIMxCLK = HCLK=180MHz
14 //設定定時器計數器頻率為=TIMxCLK/(TIM_Prescaler+1)=100KHz
15 TIM_TimeBaseStructure.TIM_Prescaler = 1800-1;
16 //采樣時鍾分頻,這里不需要用到
17 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
18 //計數方式
19 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
20 //重復計數器,這里不需要用到
21 TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
22 //初始化定時器TIMx, x[1,8]
23 TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);
24
25 //PWM模式配置
26 //配置為PWM模式1
27 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
28 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
29 TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
30 TIM_OCInitStructure.TIM_Pulse = ChannelPulse;
31 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
32 TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
33 TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
34 TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
35 //初始化輸出比較通道
36 TIM_OC1Init(ADVANCE_TIM, &TIM_OCInitStructure);
37
38 //使能ARR寄存器預裝載
39 TIM_OC1PreloadConfig(ADVANCE_TIM, TIM_OCPreload_Enable);
40
41 //自動輸出使能,斷路、死區時間和鎖定配置
42 TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
43 TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
44 TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
45 TIM_BDTRInitStructure.TIM_DeadTime = 11;
46 TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
47 TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low;
48 TIM_BDTRInitStructure.TIM_AutomaticOutput=TIM_AutomaticOutput_Enable;
49 TIM_BDTRConfig(ADVANCE_TIM, &TIM_BDTRInitStructure);
50
51 //使能定時器,計數器開始計數
52 TIM_Cmd(ADVANCE_TIM, ENABLE);
53
54 //主動輸出使能
55 TIM_CtrlPWMOutputs(ADVANCE_TIM, ENABLE);
56 }
首先定義三個定時器初始化結構體,定時器模式配置函數主要就是對這三個結構體的成員進行初始化,然后通過相應的初始化函數把這些參數寫人定時器的寄存器中。有關結構體的成員介紹請參考定時器初始化結構體詳解小節。
不同的定時器可能對應不同的APB總線,在使能定時器時鍾是必須特別注意。高級控制定時器屬於APB2,定時器內部時鍾是180MHz。
在時基結構體中我們設置定時器周期參數為1024,頻率為100KHz,使用向上計數方式。因為我們使用的是內部時鍾,所以外部時鍾采樣分頻成員不需要設置,重復計數器我們沒用到,也不需要設置。
在輸出比較結構體中,設置輸出模式為PWM1模式,主通道和互補通道輸出均使能,且高電平有效,設置脈寬為ChannelPulse,ChannelPulse是我們定義的一個無符號16位整形的全局變量,用來指定占空比大小,實際上脈寬就是設定比較寄存器CCR的值,用於跟計數器CNT的值比較。
斷路和死區結構體中,使能斷路功能,設定斷路信號的有效極性,設定死區時間。
最后使能定時器讓計數器開始計數和通道主輸出。
主函數
代碼清單 08 main函數
1 int main(void)
2 {
3 /* 初始化按鍵GPIO */
4 Key_GPIO_Config();
5
6 /* 初始化高級控制定時器,設置PWM模式,使能通道1互補輸出 */
7 TIMx_Configuration();
8
9 while (1) {
10 /* 掃描KEY1 */
11 if ( Key_Scan(KEY1_GPIO_PORT,KEY1_PIN) == KEY_ON ) {
12 /* 增大占空比 */
13 if (ChannelPulse<960)
14 ChannelPulse+=64;
15 else
16 ChannelPulse=1024;
17 TIM_SetCompare1(ADVANCE_TIM,ChannelPulse);
18 }
19 /* 掃描KEY2 */
20 if ( Key_Scan(KEY2_GPIO_PORT,KEY2_PIN) == KEY_ON ) {
21 /* 減小占空比 */
22 if (ChannelPulse>=64)
23 ChannelPulse-=64;
24 else
25 ChannelPulse=0;
26 TIM_SetCompare1(ADVANCE_TIM,ChannelPulse);
27 }
28 }
29 }
首先,調用Key_GPIO_Config函數完成按鍵引腳初始化配置,該函數定義在bsp_key.c文件中。
接下來,調用TIMx_Configuration函數完成定時器參數配置,包括定時器復用引腳配置和定時器模式配置,該函數定義在bsp_advance_tim.c文件中它實際上只是簡單的調用TIMx_GPIO_Config函數和TIM_Mode_Config函數。運行完該函數后通道引腳就已經有PWM波形輸出,通過示波器可直觀觀察到。
最后,在無限循環函數中檢測按鍵狀態,如果是KEY1被按下,就增加ChannelPulse變量值,並調用TIM_SetCompare1函數完成增加占空比設置;如果是KEY2被按下,就減小ChannelPulse變量值,並調用TIM_SetCompare1函數完成減少占空比設置。TIM_SetCompare1函數實際是設定TIMx_CCR1寄存器值。
1.4.3 下載驗證
根據實驗的硬件設計內容接好示波器輸入通道和開發板引腳連接,並把斷路輸入引腳拉高。編譯實驗程序並下載到開發板上,調整示波器到合適參數,在示波器顯示屏和看到一路互補的PWM波形,參考圖 017。此時,按下開發板上KEY1或KEY2可改變波形的占空比。
圖 017 PWM互補波形輸出示波器圖
1.5 PWM輸入捕獲實驗
實驗中,我們用通用定時器產生已知頻率和占空比的PWM信號,然后用高級定時器的PWM輸入模式來測量這個已知的PWM信號的頻率和占空比,通過兩者的對比即可知道測量是否准確。
1.5.1 硬件設計
實驗中用到兩個引腳,一個是通用定時器通道用於波形輸出,另一個是高級控制定時器通道用於輸入捕獲,實驗中直接使用一根杜邦線短接即可。
1.5.2 軟件設計
這里只講解核心的部分代碼,有些變量的設置,頭文件的包含等並沒有涉及到,完整的代碼請參考本章配套的工程。我們創建了兩個文件:bsp_advance_tim.c和bsp_advance_tim.h文件用來存定時器驅動程序及相關宏定義。
1. 編程要點
(1) 通用定時器產生PWM配置
(2) 高級定時器PWM輸入配置
(3) 計算測量的頻率和占空比,並打印出來比較
2. 軟件分析
宏定義
代碼清單 09 宏定義
1 /* 通用定時器PWM輸出 */
2 /* PWM輸出引腳 */
3 #define GENERAL_OCPWM_PIN GPIO_Pin_5
4 #define GENERAL_OCPWM_GPIO_PORT GPIOA
5 #define GENERAL_OCPWM_GPIO_CLK RCC_AHB1Periph_GPIOA
6 #define GENERAL_OCPWM_PINSOURCE GPIO_PinSource5
7 #define GENERAL_OCPWM_AF GPIO_AF_TIM2
8
9 /* 通用定時器 */
10 #define GENERAL_TIM TIM2
11 #define GENERAL_TIM_CLK RCC_APB1Periph_TIM2
12
13 /* 高級控制定時器PWM輸入捕獲 */
14 /* PWM輸入捕獲引腳 */
15 #define ADVANCE_ICPWM_PIN GPIO_Pin_6
16 #define ADVANCE_ICPWM_GPIO_PORT GPIOC
17 #define ADVANCE_ICPWM_GPIO_CLK RCC_AHB1Periph_GPIOC
18 #define ADVANCE_ICPWM_PINSOURCE GPIO_PinSource6
19 #define ADVANCE_ICPWM_AF GPIO_AF_TIM8
20 #define ADVANCE_IC1PWM_CHANNEL TIM_Channel_1
21 #define ADVANCE_IC2PWM_CHANNEL TIM_Channel_2
22
23 /* 高級控制定時器 */
24 #define ADVANCE_TIM TIM8
25 #define ADVANCE_TIM_CLK RCC_APB2Periph_TIM8
26
27 /* 捕獲/比較中斷 */
28 #define ADVANCE_TIM_IRQn TIM8_CC_IRQn
29 #define ADVANCE_TIM_IRQHandler TIM8_CC_IRQHandler
使用宏定義非常方便程序升級、移植。如果使用不同的定時器IO,修改這些宏即可。
定時器復用功能引腳初始化
代碼清單 010 定時器復用功能引腳初始化
1 static void TIMx_GPIO_Config(void)
2 {
3 /*定義一個GPIO_InitTypeDef類型的結構體*/
4 GPIO_InitTypeDef GPIO_InitStructure;
5
6 /*開啟LED相關的GPIO外設時鍾*/
7 RCC_AHB1PeriphClockCmd (GENERAL_OCPWM_GPIO_CLK, ENABLE);
8 RCC_AHB1PeriphClockCmd (ADVANCE_ICPWM_GPIO_CLK, ENABLE);
9
10 /* 定時器復用引腳 */
11 GPIO_PinAFConfig(GENERAL_OCPWM_GPIO_PORT,
12 GENERAL_OCPWM_PINSOURCE,
13 GENERAL_OCPWM_AF);
14 GPIO_PinAFConfig(ADVANCE_ICPWM_GPIO_PORT,
15 ADVANCE_ICPWM_PINSOURCE,
16 ADVANCE_ICPWM_AF);
17
18 /* 通用定時器PWM輸出引腳 */
19 GPIO_InitStructure.GPIO_Pin = GENERAL_OCPWM_PIN;
20 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
21 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
22 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
23 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
24 GPIO_Init(GENERAL_OCPWM_GPIO_PORT, &GPIO_InitStructure);
25
26 /* 高級控制定時器PWM輸入捕獲引腳 */
27 GPIO_InitStructure.GPIO_Pin = ADVANCE_ICPWM_PIN;
28 GPIO_Init(ADVANCE_ICPWM_GPIO_PORT, &GPIO_InitStructure);
29 }
定時器通道引腳使用之前必須設定相關參數,這選擇復用功能,並指定到對應的定時器。使用GPIO之前都必須開啟相應端口時鍾。
嵌套向量中斷控制器組配置
代碼清單 011 NVIC配置
1 static void TIMx_NVIC_Configuration(void)
2 {
3 NVIC_InitTypeDef NVIC_InitStructure;
4 // 設置中斷組為0
5 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
6 // 設置中斷來源
7 NVIC_InitStructure.NVIC_IRQChannel = ADVANCE_TIM_IRQn;
8 // 設置搶占優先級
9 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
10 // 設置子優先級
11 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
12 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
13 NVIC_Init(&NVIC_InitStructure);
14 }
實驗用到高級控制定時器捕獲/比較中斷,需要配置中斷優先級,因為實驗只用到一個中斷,所以這里對優先級配置沒具體要求,只要符合中斷組參數要求即可。
通用定時器PWM輸出
代碼清單 012 通用定時器PWM輸出
1 static void TIM_PWMOUTPUT_Config(void)
2 {
3 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
4 TIM_OCInitTypeDef TIM_OCInitStructure;
5
6 // 開啟TIMx_CLK,x[2,3,4,5,12,13,14]
7 RCC_APB1PeriphClockCmd(GENERAL_TIM_CLK, ENABLE);
8
9 //累計 TIM_Period個后產生一個更新或者中斷
10 //當定時器從0計數到8999,即為9000次,為一個定時周期
11 TIM_TimeBaseStructure.TIM_Period = 10000-1;
12
13 // 通用定時器2時鍾源TIMxCLK = HCLK/2=90MHz
14 // 設定定時器頻率為=TIMxCLK/(TIM_Prescaler+1)=100KHz
15 TIM_TimeBaseStructure.TIM_Prescaler = 900-1;
16 // 采樣時鍾分頻,這里不需要用到
17 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
18 // 計數方式
19 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
20
21 // 初始化定時器TIMx, x[1,8]
22 TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);
23
24 //PWM輸出模式配置
25 //配置為PWM模式1
26 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
27 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
28 //PWM脈沖寬度
29 TIM_OCInitStructure.TIM_Pulse = 3000-1;
30 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
31 // 輸出比較通道初始化
32 TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);
33 // 使能ARR預裝載
34 TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
35 // 使能定時器,計數器開始計數
36 TIM_Cmd(GENERAL_TIM, ENABLE);
37 }
定時器PWM輸出模式配置函數很簡單,看代碼注釋即可。這里我們設置了PWM的頻率為100KHZ,即周期為10ms,占空比為:(Pulse+1)/(Period+1) = 30%。
高級控制定時PWM輸入模式
代碼清單 013 PWM輸入模式配置
1 static void TIM_PWMINPUT_Config(void)
2 {
3 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
4 TIM_ICInitTypeDef TIM_ICInitStructure;
5
6 // 開啟TIMx_CLK,x[1,8]
7 RCC_APB2PeriphClockCmd(ADVANCE_TIM_CLK, ENABLE);
8
9 TIM_TimeBaseStructure.TIM_Period = 0xFFFF-1;
10 // 高級控制定時器時鍾源TIMxCLK = HCLK=180MHz
11 // 設定定時器頻率為=TIMxCLK/(TIM_Prescaler+1)=100KHz
12 TIM_TimeBaseStructure.TIM_Prescaler = 1800-1;
13 // 計數方式
14 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
15 // 初始化定時器TIMx, x[1,8]
16 TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);
17
18 // 捕獲通道IC1配置
19 // 選擇捕獲通道
20 TIM_ICInitStructure.TIM_Channel = ADVANCE_IC1PWM_CHANNEL;
21 // 設置捕獲的邊沿
22 TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
23 // 設置捕獲通道的信號來自於哪個輸入通道
24 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
25 // 1分頻,即捕獲信號的每個有效邊沿都捕獲
26 TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
27 // 不濾波
28 TIM_ICInitStructure.TIM_ICFilter = 0x0;
29 // 初始化PWM輸入模式
30 TIM_PWMIConfig(ADVANCE_TIM, &TIM_ICInitStructure);
31
32 // 當工作做PWM輸入模式時,只需要設置觸發信號的那一路即可(用於測量周期)
33 // 另外一路(用於測量占空比)會由硬件自帶設置
34 // 捕獲通道IC2配置
35 // TIM_ICInitStructure.TIM_Channel = ADVANCE_IC2PWM_CHANNEL;
36 // TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
37 // TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;
38 // TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
39 // TIM_ICInitStructure.TIM_ICFilter = 0x0;
40 // TIM_PWMIConfig(ADVANCE_TIM, &TIM_ICInitStructure);
41
42 //選擇輸入捕獲的觸發信號
43 TIM_SelectInputTrigger(ADVANCE_TIM, TIM_TS_TI1FP1);
44
45 //選擇從模式: 復位模式
46 //PWM輸入模式時,從模式必須工作在復位模式,當捕獲開始時,計數器CNT會被復位
47 TIM_SelectSlaveMode(ADVANCE_TIM, TIM_SlaveMode_Reset);
48 TIM_SelectMasterSlaveMode(ADVANCE_TIM,TIM_MasterSlaveMode_Enable);
49
50 //使能高級控制定時器,計數器開始計數
51 TIM_Cmd(ADVANCE_TIM, ENABLE);
52
53 //使能捕獲中斷,這個中斷針對的是主捕獲通道(測量周期那個)
54 TIM_ITConfig(ADVANCE_TIM, TIM_IT_CC1, ENABLE);
55 }
輸入捕獲配置中,主要初始化兩個結構體,時基結構體部分很簡單,看注釋理解即可。關鍵部分是輸入捕獲結構體的初始化。
首先,我們要選定捕獲通道,這里我們用IC1,然后設置捕獲信號的極性,這里我們配置為上升沿,我們需要對捕獲信號的每個有效邊沿(即我們設置的上升沿)都捕獲,所以我們不分頻,濾波器我們也不需要用。那么捕獲通道的信號來源於哪里呢?IC1的信號可以是TI1輸入的TI1FP1,也可以是從TI2輸入的TI2FP1,我們這里選擇直連(DirectTI),即IC1映射到TI1FP1,即PWM信號從TI1輸入。
我們知道,PWM輸入模式,需要使用兩個捕獲通道,占用兩個捕獲寄存器。由輸入通道TI1輸入的信號會分成TI1FP1和TI1FP2,具體選擇哪一路信號作為捕獲觸發信號決定着哪個捕獲通道測量的是周期。這里我們選擇TI1FP1作為捕獲的觸發信號,那PWM信號的周期則存儲在CCR1寄存器中,剩下的另外一路信號TI1FP2則進入IC2,CCR2寄存器存儲的是脈沖寬度。
PWM輸入模式雖然占用了兩個通道,但是我們只需要配置觸發信號那一路即可,剩下的另外一個通道會由硬件自動配置,軟件無需配置。
。
高級控制定時器中斷服務函數
代碼清單 014 高級控制定時器中斷服務函數
1 void ADVANCE_TIM_IRQHandler (void)
2 {
3 /* 清除定時器捕獲/比較1中斷 */
4 TIM_ClearITPendingBit(ADVANCE_TIM, TIM_IT_CC1);
5
6 /* 獲取輸入捕獲值 */
7 IC1Value = TIM_GetCapture1(ADVANCE_TIM);
8 IC2Value = TIM_GetCapture2(ADVANCE_TIM);
9 printf("IC1Value = %d IC2Value = %d ",IC1Value,IC2Value);
10
11 if (IC1Value != 0) {
12 /* 占空比計算 */
13 DutyCycle = (float)(IC2Value * 100) / IC1Value;
14
15 /* 頻率計算 */
16 Frequency = 180000000/1800/(float)IC1Value;
17 printf("占空比:%0.2f%% 頻率:%0.2fHz\n",DutyCycle,Frequency);
18 } else {
19 DutyCycle = 0;
20 Frequency = 0;
21 }
22 }
中斷復位函數中,我們獲取CCR1和CCR2寄存器中的值,當CCR1的值不為0時,說明有效捕獲到了一個周期,然后計算出頻率和占空比。
如果是第一個上升沿中斷,計數器會被復位,鎖存到CCR1寄存器的值是0,CCR2寄存器的值也是0,無法計算頻率和占空比。當第二次上升沿到來的時候,CCR1和CCR2捕獲到的才是有效的值。
主函數
代碼清單 015 main函數
1 int main(void)
2 {
3 Debug_USART_Config();
4
5 /* 初始化高級控制定時器輸入捕獲以及通用定時器輸出PWM */
6 TIMx_Configuration();
7
8 while (1) {
9 }
10 }
主函數內容非常簡單,首先調用Debug_USART_Config函數完成串口初始化配置,該函定義在bsp_debug_usart.c文件內。
接下來就是調用TIMx_Configuration函數完成定時器配置,該函數定義在bsp_advance_tim.c文件內,它只是簡單的分別調用TIMx_GPIO_Config()、TIMx_NVIC_Configuration()、TIM_PWMOUTPUT_Config()和TIM_PWMINPUT_Config()四個函數,完成定時器引腳初始化配置,NVIC配置,通用定時器輸出PWM以及高級控制定時器PWM輸入模式配置。
主函數的無限循環不需要執行任何程序,任務執行在定時器中斷服務函數完成。
1.5.3 下載驗證
根據硬件設計內容結合軟件設計的引腳宏定義參數,用杜邦線連接通用定時器PWM輸出引腳和高級控制定時器的輸入捕獲引腳。使用USB線連接開發板上的"USB TO UART"接口到電腦,電腦端配置好串口調試助手參數。編譯實驗程序並下載到開發板上,程序運行后在串口調試助手可接收到開發板發過來有關測量波形的參數信息。
1.6 每課一問
13. 計算高級控制定時器一次最長定時時間。
14. PWM輸入捕獲實驗中,根據程序參數,求通用定時器輸出的PWM波形頻率。