定時器最基本的功能就是定時處理事情。比如定時發送USART數據、定時采集AD數據、定時檢測IO口電位、還可以通過IO口輸出波形等。可以實現非常豐富的功能。定時器是一個很強大的外設,不同行業使用的方式不同,知識面很廣。
01、定時器介紹
首先我們可以在STM32F207數據手冊找到定制器的資源,從下圖可以看到STM32F207一共10個通用定時器,2個高級定時器,2個基本定時器。
不同定時器的區別
在STM32F207的用戶參考手冊中可以看到定時器的基本框圖,下圖是定時器1&8的看圖。
由上圖看出,不同寄存器具有不同的參數,位數的區別,計數模式的區別,DMA請求的區別,通道得的區別,互補輸出的區別和其他。在具體項目中選擇哪個定時器,需要看具體的應用場景。下文主要講解定時器的基礎定時功能,選擇定時器3。其他定時器原理是相同的,理解定時器3的定時功能,其他定時器也就能理解了。對於STM32系列的單片機,外設基本都是一致的,並且其他家的MCU也是類似的,國內的有兆易創新、新唐科技、上海靈動微電子等等。
02、時鍾源
定時器基本定時功能框圖。
①CK_PSC是定時器時鍾TIMxCLK,經APB1預分頻器后分頻提供。
②定時器時鍾經過PSC 預分頻器之后,即CK_CNT,用來驅動計數器計數。
③計數器CNT 是一個16 位的計數器,向上,向下,向上/下計數模式,最大計數值為65535。當計數達到自動重裝載寄存器的時候產生更新事件,並清零從頭開始計數。
④自動重裝載寄存器ARR 是一個16位的寄存器,這里面裝着計數器能計數的最大數值。當計數到這個值的時候,如果使能了中斷的話,定時器就產生溢出中斷。
定時器說白了就是個計數器,就像我們用心跳粗略估算時間一樣,心臟跳動粗略可以認為是1s,那么我們計時60次心跳就過了60秒。其中CK_CNT時鍾就類似心跳,CNT計數器就類似心跳次數。舉一個極端簡單的例子,我們要實現60秒定時,CK_CNT是1s,我們設置CNT計數器向上計數開啟中斷,因為只有溢出時,也就是計數到65535時才會有中斷,那么我們設置CNT計數器為65535-60=65475,開始及時,那么60秒后就會產生中斷。我們設置自動重裝載寄存器ARR也為65475,當CNT計數器溢出時,自動重裝載寄存器ARR就會自動裝載到CNT計數器中,就能實現自動循環定時60秒。
經過上面分析,精確定時的關鍵在於CK_CNT的頻率,而CK_CNT是由定時器時鍾分頻而來的。那么我們就要知道timer3的定時器時鍾。我們就要看時鍾系統部分,具體看文章《STM32F207時鍾系統解析》,這篇文章主要講解了系統的120M時鍾如何從外部的25M的晶振得到的。其中說到APB外設時鍾的問題。
定時器在APB定時器時鍾下,具體在APB1還是在APB2時鍾下我們可以從STM32F207數據手冊上看到,圖片名字STM32F20xblock diagram。
從上文我們看timer3是在APB1下的。
那么我們來分析APB1的頻率
從上圖看出,APB1定時器的從系統120M時鍾(系統時鍾可配置的,我們使用默認的120M時鍾)經過AHB分頻,APB分頻得到的。
在這里說上圖紅框中的“錯誤”,萌新可能不太理解。首先手冊中少了個右括號。修改后應該為:
if(APBx presc == 1) X1 else X2
也就是說
APB分頻系數如果是1,頻率不變,APB輸出的頻率就是APB下面時鍾的頻率。
APB分頻系數不是1,頻率X2,APB輸出的頻率乘以2是APB下面時鍾的頻率。
下面我們分析APB1時鍾,從system_stm32f2xx.c中的SetSysClock函數中如下
/* HCLK = SYSCLK / 1*/ RCC->CFGR |= RCC_CFGR_HPRE_DIV1; /* PCLK2 = HCLK / 2*/ RCC->CFGR |=RCC_CFGR_PPRE2_DIV2; /* PCLK1 = HCLK / 4*/ RCC->CFGR|= RCC_CFGR_PPRE1_DIV4;
可以看到AHB分頻系數是1,APB1分頻系數是4。
timer3的時鍾為120M/1/4*2= 60MHZ。
這里有個疑問,ST提供的system_stm32f2xx.c注釋為什么是HCLK,PCLK2,PCLK1,卻沒有上文提到的APB,AHB字眼,具體看下我之前寫過的文章《STM32F207時鍾系統解析》。
其實我們出了分析代碼,system_stm32f2xx.c文件頭也是有注釋的,方便查看。
當然,這要求我們的外部晶振是25M的,且system_stm32f2xx.c是沒有被修改過的,如果大家需要修改這個文件單片機超頻運行,建議把文件頭的注釋修改,養成一個良好的習慣。
03、時基單元
可編程高級定時器控制模塊主要是一個帶有相關自動重載16位計數器。這個計數器可以向上計數,向下計數或者交替遞增和遞減計數。計數器時鍾可以通過一個分頻器分頻。
計數器的自動重載寄存器和預分頻寄存器可以通過軟件讀寫。即使當計數器正在運行也可以讀寫。
時基單元包括
-
計數器寄存器 (TIMx_CNT)
-
預分頻器寄存器 (TIMx_PSC)
-
自動重載寄存器 (TIMx_ARR)
-
重復計數器寄存器 (TIMx_RCR)
自動重載寄存器是預裝載的。從自動重載寄存器寫入或讀取會訪問預裝載寄存器。預裝載寄存器的內容既可以直接傳送到影子寄存器,也可以在每次發生更新事件(UEV)時傳送到影子寄存器,這取決於TIMx_CR1 寄存器中的自動重載預裝載使能位(ARPE)。當計數器達到上溢值(或者在遞減計數時達到下溢值)並且TIMx_CR1 寄存器中的UDIS 位為0時,將發送更新事件。該更新事件也可由軟件產生。
計數器由預分頻器輸出CK_CNT 提供時鍾,僅當TIMx_CR1 寄存器中的計數器啟動位(CEN)置1 時,才會啟動計數器。
預分頻器說明
預分頻器可對計數器時鍾頻率進行分頻,分頻系數介於1 和65536 之間。該預分頻器基於TIMx_PSC寄存器中的16 位寄存器所控制的16位計數器。由於該控制寄存器具有緩沖功能,因此可對預分頻器進行實時更改。而新的預分頻比將在下一更新事件發生時被采用。
下圖以一些示例說明在預分頻比實時變化時計數器的行為。
預分頻器分頻由1 變為2 時的計數器時序圖
預分頻器分頻由1 變為4 時的計數器時序圖
04、計數模式
4.1、向上計數模式
在向上計數模式中,計數器從0增加到自動重載值(TIMx_ARR寄存器的值),然后從0重新開始並產生一個計數器溢出事件。
如果使用重復計數器,則當遞增計數的重復次數達到重復計數器寄存器中編程的次數加一次(TIMx_RCR+1)后,將生成更新事件(UEV)。否則,將在每次計數器上溢時產生更新事件。
將TIMx_EGR寄存器的UG位置1通過軟件或使用從模式控制器時,也將產生更新事件。
通過軟件將TIMx_CR1寄存器中的UDIS位置1可禁止UEV事件。這可避免向預裝載寄存器寫入新值時更新影子寄存器。在UDIS位寫入0之前不會產生任何更新事件。不過,計數器和預分頻器計數器都會重新從0開始計數(而預分頻比保持不變)。此外,如果TIMx_CR1寄存器中的URS位(更新請求選擇)已置1,則將UG位置1會生成更新事件UEV,但不會將UIF標志置1(因此,不會發送任何中斷或DMA請求)。這樣一來,如果在發生捕獲事件時將計數器清零,將不會同時產生更新中斷和捕獲中斷。
發生更新事件時,將更新所有寄存器且將更新標志(TIMx_SR寄存器中的UIF位)置1取決於URS位)
-
重復計數器中將重新裝載TIMx_RCR寄存器的內容
-
自動重載影子寄存器將以預裝載值 (TIMx_ARR) 進行更新
-
預分頻器的緩沖區中將重新裝載預裝載值(TIMx_PSC寄存器的內容)
計數器時序圖,1 分頻內部時鍾
計數器時序圖,2 分頻內部時鍾
從上面兩圖看出,中斷標志是需要軟件清除的
計數器時序圖,ARPE=0 時更新事件(TIMx_ARR 未預裝載)
從上面兩圖看出,向上計數,還沒有到達0x36,就把自動重載寄存器修改為0x36,就會在計數到0x36時產生動作。
計數器時序圖,ARPE=1 時更新事件(TIMx_ARR 預裝載)
從上面兩圖看出,向上計數,還沒有到達0x36,就把自動重載預裝載寄存器修改為0x36,就不會在計數到0x36時產生動作,會在這個時將自動重載預裝載寄存器值賦給自動重載影子寄存器。
4.2、向下計數模式
在向下計數模式中,計數器從自動重載值(TIMx_ARR寄存器的值)向下計數到0,然后從自動重載值(重新開始並產生一個計數器溢出事件。
如果使用重復計數器,則當遞減計數的重復次數達到重復計數器寄存器中編程的次數加一次(TIMx_RCR+1)后,將生成更新事件(UEV)。否則,將在每次計數器下溢時產生更新事件。
將TIMx_EGR 寄存器的UG 位置1(通過軟件或使用從模式控制器)時,也將產生更新事件。
通過軟件將TIMx_CR1 寄存器中的UDIS 位置1 可禁止UEV 更新事件。這可避免向預裝載寄存器寫入新值時更新影子寄存器。在UDIS 位寫入0之前不會產生任何更新事件。不過,計數器會重新從當前自動重載值開始計數,而預分頻器計數器則重新從0 開始計數(但預分頻比保持不變)。
此外,如果TIMx_CR1 寄存器中的URS 位(更新請求選擇)已置1,則將UG 位置1 會生成更新事件UEV,但不會將UIF 標志置1(因此,不會發送任何中斷或DMA 請求)。這樣一來,如果在發生捕獲事件時將計數器清零,將不會同時產生更新中斷和捕獲中斷。
發生更新事件時,將更新所有寄存器且將更新標志(TIMx_SR 寄存器中的UIF 位)置1(取決於 URS 位):
-
重復計數器中將重新裝載 TIMx_RCR 寄存器的內容
-
預分頻器的緩沖區中將重新裝載預裝載值( TIMx_PSC 寄存器的內容)
-
自動重載活動寄存器將以預裝載值( TIMx_ARR 寄存器的內容)進行更新。注意,自動重載寄存器會在計數器重載之前得到更新,因此,下一個計數周期就是我們所希望的新的周期長度
以下各圖以一些示例說明當TIMx_ARR=0x36 時不同時鍾頻率下計數器的行為
計數器時序圖,1 分頻內部時鍾
計數器時序圖,2 分頻內部時鍾
計數器時序圖,未使用重復計數器時更新事件
4.3、中央對齊(向上/向下計數模式)
在中心對齊模式下,計數器從0 開始計數到自動重載值(TIMx_ARR 寄存器的內容)-1,生成計數器上溢事件;然后從自動重載值開始向下計數到1 並生成計數器下溢事件。之后從0開始重新計數。
當TIMx_CR1 寄存器中的CMS位不為“00”時,中心對齊模式有效。將通道配置為輸出模式時,其輸出比較中斷標志將在以下模式下置1,即:計數器遞減計數(中心對齊模式1,CMS=“01”)、計數器遞增計數(中心對齊模式2,CMS =“10”)以及計數器遞增/遞減計數(中心對齊模式3,CMS =“11”)。
在此模式下,TIMx_CR1 寄存器的DIR 方向位不可寫入值,而是由硬件更新並指示當前計數器方向。
每次發生計數器上溢和下溢時都會生成更新事件,或將TIMx_EGR 寄存器中的UG 位置1(通過軟件或使用從模式控制器)也可以生成更新事件。這種情況下,計數器以及預分頻器計數器將重新從0 開始計數。
通過軟件將TIMx_CR1 寄存器中的UDIS 位置1 可禁止UEV 更新事件。這可避免向預裝載寄存器寫入新值時更新影子寄存器。在UDIS 位寫入0 之前不會產生任何更新事件。不過,計數器仍會根據當前自動重載值進行遞增和遞減計數。
此外,如果TIMx_CR1 寄存器中的URS 位(更新請求選擇)已置1,則將UG 位置1 會生成UEV 更新事件,但不會將UIF 標志置1(因此,不會發送任何中斷或DMA 請求)。這樣一來,如果在發生捕獲事件時將計數器清零,將不會同時產生更新中斷和捕獲中斷。
發生更新事件時,將更新所有寄存器且將更新標志(TIMx_SR 寄存器中的UIF 位)置1(取決於URS 位):
-
重復計數器中將重新裝載 TIMx_RCR 寄存器的內容
-
預分頻器的緩沖區中將重新裝載預裝載值( TIMx_PSC 寄存器的內容)
-
自動重載活動寄存器將以預裝載值( TIMx_ARR 寄存器的內容)進行更新。注意,如果更新操作是由計數器上溢觸發的,則自動重載寄存器在重載計數器之前更新,因此,下一個計數周期就是我們所希望的新的周期長度(計數器被重載新的值)。
以下各圖以一些示例說明不同時鍾頻率下計數器的行為
計數器時序圖,1 分頻內部時鍾,TIMx_ARR = 0x6
計數器時序圖,2 分頻內部時鍾
計數器時序圖,ARPE=1 時的更新事件(計數器下溢)
計數器時序圖,ARPE=1 時的更新事件(計數器上溢)
05、基礎定時代碼
10ms中斷配置代碼
關於設置分頻值
TIM3CLK = 2 * PCLK1=2*HCLK / 4= HCLK / 2 = SystemCoreClock /2=60MHZ
所以下圖紅框內就是TIM3CLK
這里的值是分頻系數=TIM3CLK/定時器實際頻率,所以定時器頻率是10000,也就是說除數就是定時器頻率。一個clk是1/10000s。定時時間=1/10000*定時器重載值。根據上面的配置,定時器重載值是100,也就是定時器中斷周期是=1/10000*100=0.01s=10ms,也就是100HZ。
如果在定時器翻轉LED燈,那么LED燈閃爍頻率是50Hz。
上面的的分頻值當然可以直接賦值5999,如果想修改為定時器頻率為1000,那么還要重新計算。如果按照上面的寫法,直接將除數修改為1000即可。
看到這里大家會有疑問,給的重載值明明是99,分頻率值也減去1。下面將說明分頻值和自動重載周期值都需要減去1的原因。
自動重載值:因為從0開始計算,賦值10,從0開始計數到10是11次。
分頻值:在TIMx_PSC寄存器有以下描述。
特別說明
時鍾分頻因子
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV2;
其實仔細看過技術手冊后發現這句話與PWM輸出實驗其實是沒關系的,這句話是設置定時器時鍾(CK_INT)頻率與數字濾波器(ETR,TIx)使用的采樣頻率之間的分頻比例的(與輸入捕獲相關),0表示濾波器的頻率和定時器的頻率是一樣的。
首先這個colck_division時鍾分割系數並不是對定時器的時鍾頻率進行分割。我們都知道輸入捕獲模式下有一個數字濾波器,這個數字濾波器可以通過配置寄存器改變他的采樣頻率,從而將一些頻率濾除。
具體細節在輸入捕獲中詳解。
我們也可以根據定時器的計數器的特性,使用查詢計數器的方法實現精確延時,具體請看《STM32延時函數的四種方法》。
定時器代碼開源地址:
https://github.com/strongercjd/STM32F207VCT6
點擊查看本文所在的專輯,STM32F207教程