目錄
- 1.引入
- 1.1 簡介
- 1.2 類型
- 1.2.1 基本定時器
- 1.2.2 通用定時器
- 1. 觸發控制單元 (Trigger Control Unit)
- 2. 輸入捕獲單元 (Input Capture Unit)
- 3. 輸出比較單元 (Output Compare Unit)
- 4. CNT 計數器
- 5. 自動重裝載寄存器 (ARR)
- 6. 預分頻器 (PSC)
- 7. 中斷與 DMA 事件
- 8. 剎車功能 (Break Input, BRK)
- 1.2.3 高級定時器
- 1. 觸發控制單元 (Trigger Control Unit)
- 2. 時基單元 (Counter and ARR)
- 3. 輸入捕獲單元 (Input Capture Unit)
- 4. 輸出比較單元 (Output Compare Unit)
- 5. 剎車輸入單元 (Break Input)
- 6. 死區時間控制單元 (Dead-Time Generator, DTG)
- 7. 主從模式控制 (Master-Slave Mode Control)
- 8. 事件生成與中斷
- 9.重復計數器 (Repetition Counter, REP)
- 1.3 定時中斷基本結構圖
- 1.4 時序
- 1.4.1 PSC預分頻器
- 1.4.2 計時器時序
- 1.4.3 計數器無預裝時序
- 1.4.4 計數器有預裝時序
- 1.5 RCC時鐘樹
- 2.基本使用
- 2.1 步驟
- 2.2 相關函數
- 2.3 結構體
- 2.4 編寫
- 2.4.1 定時器內部中斷
- 2.4.2 定時器外部中斷
1.引入
1.1 簡介
TIM(Timer)定時器
定時器可以對輸入的時鐘進行計數,并在計數值達到設定值時觸發中斷
16位計數器、預分頻器、自動重裝寄存器的時基單元,在72MHz計數時鐘下可以實現最大59.65s的定時
不僅具備基本的定時中斷功能,而且還包含內外時鐘源選擇、輸入捕獲、輸出比較、編碼器接口、主從觸發模式等多種功能
根據復雜度和應用場景分為了高級定時器、通用定時器、基本定時器三種類型
所謂定時器,就是鬧鐘,時間到后你就要做某些事。有 2 個要素:時間、做事,換成程序員的話就是:超時時間、函數。
比如以按鍵為例,使用定時器可以解決抖動現象,未超時就發生中斷,就重新計時,當抖動停止了,超過定時的時間,就可以取調用中斷函數去處理穩定后的按鍵中斷事件。(注:上圖的提到的函數是linux開發板的,但是原理其實是差不多的)
1.2 類型
類型 | 編號 | 總線 | 功能 |
---|---|---|---|
高級定時器 | TIM1、TIM8 | APB2 | 擁有通用定時器全部功能,并額外具有重復計數器、死區生成、互補輸出、剎車輸入等功能 |
通用定時器 | TIM2、TIM3、TIM4、TIM5 | APB1 | 擁有基本定時器全部功能,并額外具有內外時鐘源選擇、輸入捕獲、輸出比較、編碼器接口、主從觸發模式等功能 |
基本定時器 | TIM6、TIM7 | APB1 | 擁有定時中斷、主模式觸發DAC的功能 |
STM32F103C8T6定時器資源:TIM1、TIM2、TIM3、TIM4
1.2.1 基本定時器
預分頻器:
- 連接的是基準計數時鐘的輸入,其一般來自RCC的TIMxCLK,一般是系統的主頻72MHz。
- 而預分頻器則是對輸入的基準時鐘72MHz進行預分配,比如為0時,72/1=72MHz;為1時,72/2=36MHz
- 這個分配器是16位的,也就是最多可以分頻2的16次方
CNT計數器:
- 對預分屏后的計數時鐘進行計數,計數時鐘每來一個上升沿,則+1
自動重裝載寄存器:
- 存儲目標時鐘計數,CNT計數器計的時鐘數和其存儲的目標時鐘計數相同時,就發送中斷信號(UI)或更新事件(U)。
- 同時CNT計數器會清0
三者構成了時基單元。
同時還提到了主模式觸發DAC的功能,主從觸發模式是stm32的一大特色,它能讓內部的硬件在不受程序的控制下實現自動運行。
在框圖中有個部分寫著至DAC,在我們使用DAC的時候,可能會用DAC(數字模擬信號轉換器)輸出一段波形,那么就需要每隔一段時間來觸發一次DAC,讓它輸出下一個電壓點。
知道了定時器后我們可能會想到先設置一個定時器,每隔一段時間產生中斷,在中斷函數中去讓DAC進行轉換、輸出。雖然這樣沒問題,但是會造成CPU負擔,因為需要中斷的次數不少,主程序也老是處于被頻段中斷的狀態。這時候就可以使用主模式觸發DAC
也就是將更新事件(U)映射到TRGC(Trogger Out)的位置,然后接到DAC轉換器上,這樣就可以采用事件觸發的方式去通知DAC進行轉換。
其實就是和Linux開發板中的MSI或MSI-X的消息中斷方式是有點相似的,不是采用傳統的INTx引腳中斷觸發方式,而是event消息來觸發中斷。雖然stm32這種不算是中斷,但其實也是用event事件來讓某些設備開始工作。
1.2.2 通用定時器
擁有基本定時器全部功能,并額外具有內外時鐘源選擇、輸入捕獲、輸出比較、編碼器接口、主從觸發模式等功能
中間的還是時基單元,和基本定時器是一樣的功能。不過對于CNT計數器,在這里不是只有向上計數的模式了(自增),在這里還支持:
- 向下計數:自減到重裝值,產生中斷,清0
- 中央對齊計數:先先上自增到重裝值,產生中斷,然后再向下自減到0,產生中斷
1. 觸發控制單元 (Trigger Control Unit)
這個模塊用于生成和選擇不同的觸發信號,包括:
- 內部觸發輸入 (ITR):可以從其他定時器或模塊獲取觸發信號(例如 ITR0、ITR1、ITR2、ITR3)。可以實現定時器級聯。比如初始化好TIM3,將計數器處的事件更新觸發(U)映射到TRGO,然后TRGO再去連接到TIM2的ITR2,實現計時器的級聯,這樣TIM3的更新事件就能去驅動TIM2的計數單元。
-
外部觸發輸入 (ETR):可以從外部引腳獲取觸發信號,通過 ETRP 控制輸入極性,經過輸入濾波器后,得到觸發信號
ETRF
。 -
- 就是可以在一些支持TIM功能的引腳上接一個外部時鐘(ETR),這樣就不是像基本通用定時器那樣通過CK_INT來接入系統內部的來自RCC的CK_TIM18內部時鐘。
- 可以選擇通過ETRF觸發信號到觸發控制器,再到PSC分頻器。
- 也可以通過TRQI到從模式控制器,觸發定時器的從模式,再到PSC分頻器。
-
TRGO 輸出信號:將觸發信號輸出至 DAC 或 ADC,進行協調同步。
-
從模式控制:允許定時器在主從模式下工作,通過從模式寄存器配置觸發信號(例如復位、使能、向上/向下計數等)。
對于TRGI,獲得時鐘,其中除了TRC的ITR使用級聯定時器的方式還有通過TIIF_ED,也就是通過CH1引腳獲得時鐘,這種方式獲得時鐘上升沿和下降沿都有效。除此之外TRGI除了TRC,還能通過TI1FP1和TI2FP2獲得,分別連接到IC1和IC2。
其實主要就是時鐘的來源,除了基本定時器的來自系統RCC的內部時鐘,還能有其它方式:
2. 輸入捕獲單元 (Input Capture Unit)
-
通用定時器支持四路輸入捕獲通道(TI1、TI2、TI3、TI4),用于捕獲外部輸入信號的事件,主要用于頻率測量、信號周期或占空比測量等。
-
每個輸入捕獲通道具有以下功能組件:
-
- 輸入濾波器:對輸入信號進行去抖和濾波處理,以減少噪聲的干擾。
- 預分頻器 (ICxPS):可以將輸入信號分頻后再捕獲,以支持不同頻率信號的測量。
- 捕獲比較寄存器 (CCRx):當發生輸入事件時,捕獲當前的 CNT 值并存儲在對應的 CCR 寄存器中。
3. 輸出比較單元 (Output Compare Unit)
-
通用定時器的輸出比較單元支持四個輸出通道(OC1、OC2、OC3、OC4),用于比較 CNT 計數器和預設的比較值 (CCRx),主要用于產生 PWM 信號、輸出定時事件等。
-
輸出比較單元通過比較 CNT 計數器和指定的比較值 (CCRx) 來控制輸出行為,支持多種模式:
-
- PWM 模式:通過設置比較值實現 PWM 輸出,用于調節輸出信號的占空比。
- 定時模式:在計數器值等于比較值時生成輸出事件,適合用于定時控制。
- 單脈沖模式:產生單個脈沖信號,可以用于控制單一事件。
4. CNT 計數器
-
這是整個定時器的核心部件,負責記錄時鐘的計數值。計數器有多種計數模式:
-
- 向上計數模式:計數器值從 0 增加到自動重裝載寄存器 (ARR) 的值,然后生成更新事件 (U) 或中斷 (UI),并清零重新計數。
- 向下計數模式:計數器值從 ARR 的值減少到 0,產生更新事件或中斷,然后重新從 ARR 開始計數。
- 中心對齊計數模式:計數器值從 0 增加到 ARR,然后反向減少到 0,在上升沿和下降沿分別生成中斷或更新事件。
5. 自動重裝載寄存器 (ARR)
- ARR 存儲目標計數值,即計數器達到該值時會觸發更新事件。ARR 的配置值決定了計數器的計數周期,可以用來設置定時器的周期。
6. 預分頻器 (PSC)
- 預分頻器用于分配基準計數時鐘 (CK_PSC),將輸入的時鐘頻率降低到合適的計數頻率。比如輸入時鐘為 72MHz,預分頻器設置為 1 時,分頻為 72MHz;若設置為 71,則分頻為 72/72 = 1MHz。預分頻器最大支持 16 位設置。
7. 中斷與 DMA 事件
- 通用定時器可以在一些事件發生時(例如計數器達到 ARR 值,或輸入捕獲發生時)觸發中斷信號 UI 和更新事件 U,用于通知 CPU 或其他模塊進行處理。
- 也支持 DMA 傳輸,在捕獲事件觸發時可以將數據自動傳輸到內存,減少 CPU 干預。
8. 剎車功能 (Break Input, BRK)
- 剎車功能可以通過
BRK
引腳直接觸發,用于緊急停止輸出。常用于電機控制場景,檢測到過流或故障時可以迅速停機,保護電路和設備。
1.2.3 高級定時器
高級定時器在功能上相較于通用定時器更為復雜,主要用于電機控制等要求較高的場景。以下是高級定時器的詳細結構功能解析:
1. 觸發控制單元 (Trigger Control Unit)
- 該單元類似于通用定時器的觸發控制單元,但功能更為豐富,支持多種觸發信號和觸發模式。
- 內部觸發 (ITR0 - ITR3):可以接收其他定時器的觸發信號,用于實現定時器的主從模式同步。
- 外部觸發 (ETR):來自外部引腳的觸發信號,經過極性設置和濾波后輸出到復位或從模式控制。
- ETRF 信號:經過濾波的 ETR 信號用于控制計數的復位、使能等。
- TRGI/ TRGO 信號:用于與其他外部模塊(如 ADC、DAC 等)進行同步。
2. 時基單元 (Counter and ARR)
- 預分頻器 (PSC):控制時基單元的輸入時鐘,進行分頻以降低時鐘頻率。
- CNT 計數器:高級定時器支持多種計數模式,除了向上、向下計數,還支持中心對齊模式,用于產生對稱的 PWM 信號。中心對齊模式會使 CNT 計數器在到達自動重裝載值 (ARR) 后反向計數。
- 自動重裝載寄存器 (ARR):存儲計數器的最大計數值,到達該值時產生更新事件或中斷,用于控制 PWM 周期。
3. 輸入捕獲單元 (Input Capture Unit)
- 高級定時器支持四個輸入通道 (TI1-TI4),每個通道可以獨立捕獲外部輸入信號。
- 每個輸入通道配有輸入濾波器、極性控制和分頻器,用于捕獲外部事件,主要用于測量信號的頻率、周期或占空比。
- 輸入捕獲結果存儲在捕獲/比較寄存器 (CCRx) 中,供后續計算和處理。
4. 輸出比較單元 (Output Compare Unit)
-
高級定時器支持四路輸出比較單元,用于 PWM 信號生成和定時輸出。
-
每個輸出比較通道配有比較寄存器 (CCRx) 和比較模式配置,可以生成多種 PWM 信號:
-
- PWM 模式 1/模式 2:設置占空比,支持高精度的 PWM 信號輸出。
- 強制輸出模式:可強制改變輸出狀態,適用于需要特殊信號控制的場景。
-
輸出比較單元還配備死區控制器 (DTG),用于在雙向控制場景下添加死區時間,以避免上下橋驅動器的短路。
5. 剎車輸入單元 (Break Input)
- 剎車輸入 (BRK) 是高級定時器的一項重要功能,用于緊急停止。通常用于電機控制,避免因故障或過流導致的損壞。
- BI 信號:當檢測到 BRK 引腳信號時,輸出立即被關閉或進入安全狀態。
- 剎車輸入功能可配置觸發級別,如有效電平、響應時間等。
6. 死區時間控制單元 (Dead-Time Generator, DTG)
- DTG 單元用于在橋式控制場景下生成死區時間。死區時間是上下橋臂之間的安全間隔時間,防止兩個橋臂同時導通導致短路。
- DTG 的設置決定了 PWM 輸出信號的上升沿和下降沿之間的延遲時間。
- 可以通過編程調整死區時間,以適應不同功率電路的需求。
7. 主從模式控制 (Master-Slave Mode Control)
- 高級定時器支持主從模式控制,用于多定時器同步。
- 可以選擇一個定時器作為主定時器,其產生的信號(如 TRGO)可以作為其他從定時器的觸發源,實現多定時器之間的同步控制。
- 常用于多通道 PWM 信號同步或電機控制應用中,以實現復雜的控制邏輯。
8. 事件生成與中斷
- 高級定時器可以在多種事件(如計數溢出、捕獲事件、比較匹配等)發生時產生中斷,供 CPU 或 DMA 進行相應處理。
- 更新事件 (U):計數器溢出時產生的事件。
- 比較中斷:當 CNT 計數值與比較寄存器匹配時觸發,用于定時輸出。
- 這些事件可以獨立配置,使高級定時器具備更靈活的控制方式。
9.重復計數器 (Repetition Counter, REP)
- 在一些定時器(如高級定時器)中,支持重復計數功能,可以設定一個重復計數值,經過設定次數的更新事件后才觸發一次輸出,這在電機控制中較為常見。
相比通用定時器,主要就是多了死區時間控制單元 (Dead-Time Generator, DTG)、剎車輸入單元 (Break Input)、重復計數器 (Repetition Counter, REP)。
1.3 定時中斷基本結構圖
看了上面的介紹下后再來看這個基本結構體,其實一目了然了。就是對于通用定時器來說,時鐘可以有多種來源,
1.4 時序
1.4.1 PSC預分頻器
計數器計數頻率:CK_CNT = CK_PSC / (PSC + 1)
-
CK_PSC:輸入的未分頻的計數時鐘。
-
CNT_EN:計時器使能,高電平正常運行,低電平停止運行。
-
CK_CNT:分頻后的計數時鐘,圖中以紅虛線可以看出分為了兩個頻率,前半和未分頻的時鐘頻率一樣,也就是PSC=1;后半段則產生的分頻,PSC是2,CK_CNT = CK_PSC / (2 + 1) = CK_PSC / 3
-
計數器寄存器:顧名思義,進行計數的,當計數達到目標值就產生中斷。計數的頻率和分頻后的時鐘頻率是正比的。可以看出紅線左邊的自動重裝值就是FC,計數達到后清0,FC --> 00
-
- CNT 寄存器用于保存計數值,每當 CK_CNT 上升沿到來時,CNT 加一。
- 當 CNT 達到自動重裝載寄存器 (ARR) 的值時,會觸發中斷,并將 CNT 清零,重新開始計數。
- CNT 的計數頻率與 CK_CNT 是正比的,分頻后的時鐘頻率決定了 CNT 的更新速度。
-
更新事件(UEV):計時器計數達到重裝值了,同時下一個時鐘來了,分頻后進入到CNT計數器,產生一個更新事件(圖中唯一一個高電平),開始新的計時。
-
- 更新事件(UEV)是在 CNT 到達自動重裝載寄存器值并清零時觸發的事件。
- UEV 通常用于同步其他模塊或者產生中斷,告知外部系統計時周期已經完成。
- 提到的圖中唯一的高電平(高電平代表觸發更新事件)符合描述。
-
預分頻控制寄存器( PSC Register) ):供我們讀寫用的。比如將0改成了1,那么在時鐘周期結束后,預分配緩沖器也會從0–>1,此時預分頻率計數器就會按照0、1、0、1來輸出,一個0、1就對應到一個分頻器輸出的CK_CNT,也就是對應到一個計數。到1 CK_CNT就輸出高電平。
-
- PSC 是一個可以讀寫的控制寄存器,用于設置分頻系數 PSC + 1。
- 修改 PSC 值會改變 CK_CNT 的頻率。在下一個計時周期開始時,新值會加載到預分頻緩沖器,確保分頻變更生效。
-
預分頻緩沖器:真正起作用的寄存器,如果沒有這個寄存器,我們在某個時間內對預分頻控制寄存器寄存器進行寫入,把0改成1,此時還在進行著一個周期的時鐘,這就會導致該周期的時鐘的前半部分和后半部分頻率不一樣了,這樣是不行的。因此就有了預分頻緩沖器,即使我們對某一個正在進行的時鐘周期內對預分頻控制寄存器寄存器改變了值,也會等到該周期結束了,才對預分頻緩沖器的值進行更改,產生新的事件,開始新的時鐘周期。
-
- 預分頻緩沖器在某個周期結束后更新為新值,避免在計數周期中途修改 PSC 導致分頻不一致。
- 當修改 PSC 值時,新值會先存儲在控制寄存器中,當前計數周期繼續使用舊值。
- 當當前周期結束時,預分頻緩沖器會同步更新為新的 PSC 值,并在下一計數周期生效。這樣可以確保時鐘頻率的連續性和穩定性,不會因為修改 PSC 而引起不必要的頻率波動。
1.4.2 計時器時序
計數器溢出頻率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)
1.4.3 計數器無預裝時序
就是自動重裝載寄存器是否啟用了緩沖寄存器,其實和PSC預分頻器的緩沖器道理是一樣的。
對于有陰影部分的器件,其都是有緩沖寄存器的功能的。
對于沒有啟用緩沖器的重裝載寄存器,在某一個時鐘周期內(紅虛線左邊),對目標計數進行了更新,也就是原本是FF改為了36,那么原來該時鐘周期的計數目標是到FF才產生申請中斷,到由于更改了,計數到36就申請中斷,計數清0
這和PSC是一樣的,如果不開啟緩沖器,那么就會導致在某一個周期內產生新的分頻,造成時鐘的計數頻率不一樣。
1.4.4 計數器有預裝時序
這種在某一個周期內更改了目標計數,只會在下一個時鐘周期生效,不會影響到當前的周期
1.5 RCC時鐘樹
紅色左邊的都是時鐘的產生電路,右邊的則是時鐘的分配電路。中間的SYSCLK就是系統的時鐘72MHz,不過時鐘一開始輸出的并不是72MHz,在時鐘產生電路中,是有4個振蕩源,從上往下分別是:
- 8 MHz HSI RC:內部的8MHz的高速RC振蕩器
- 4-16 MHz HSE OSC:外部的4-16MHz高速石英晶體振蕩器,也就是晶振,一般都是接8MHz
- LSE OSC 32.768 kHz:外部的 32.768 kHz的低速晶振,一般是給RTC使用的
- LSI RC 40 KHz:內部的40 KHz低速RC晶振,給看門狗提供時鐘的
對于前兩個是給系統提供戲中的,AHB、APB1、APB2總線的時鐘都是由其提供的。一個內部一個外部,都是可以使用的,1只不過外部的石英晶體振蕩器會比內部的RC振蕩器更穩定。
對于系統內部的時鐘,是有提供的init函數來進行初始化的,其思路是這樣的,先啟用的第一個8 MHz HSI RC作為系統時鐘,之后再去配置第二個外部4-16 MHz HSE OSC,等其經過PLLMUL鎖相環進行倍頻,8MHz倍頻9倍,也就是得到穩定的72MHz后,再啟用其作為系統的時鐘,原本的第一個時鐘關閉,實現了從8MHz切換到了72MHz。
所以如果在某些時刻,明明你定的時鐘是1s,結果大約10s后才發生中斷,那么就有可能此時使用的還是第一個內部的8MHz的高速RC振蕩器作為系統的時鐘,第二個時鐘的晶振可能出現問題了。
- 此外里面還有一個CSS的器件,也就是時鐘安全系統,負責切換時鐘,可以檢測外部時鐘的運行狀態,一旦外部時鐘失效就會切換到內部時鐘,防止程序一直卡死在那。
2.基本使用
2.1 步驟
按照這個結構體去進行配置定時器:
- RCC開啟時鐘,在模塊程序編寫的時候都是基本會執行的。打開后定時器的基準時鐘和整個外設時鐘都會打開
- 選擇時基單元的時鐘源,這里我們選擇的是RCC內部時鐘,因此配置為內部時鐘模式
- 配置時基單元:PSC預分頻器、CNT計數器、ARP自動重裝器,這一部分用結構體配置就好了
- 配置中斷輸出控制,允許更新中斷輸出到NVCI
- 配置NVIC,在NVIC中打開定時器中斷的通道,并配置一個優先級
- 運行控制配置,也就是使能定時器
- 定義定時器中斷函數
2.2 相關函數
按照配置步驟,去調用相關的庫函數,有哪些庫函數可以去查看stm32f10x_tim.h文件
庫函數手冊中也已經詳細的講解了。下面根據配置步驟,給出所需要的相關函數:
- RCC開啟時鐘,在模塊程序編寫的時候都是基本會執行的。打開后定時器的基準時鐘和整個外設時鐘都會打開
- 選擇時基單元的時鐘源,這里我們選擇的是RCC內部時鐘,因此配置為內部時鐘模式
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
將定時器的時鐘源配置為內部時鐘,即系統時鐘(比如72 MHz)。void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
配置定時器 TIMx 的外部時鐘源為某個內部觸發源(ITRx)。
TIM_InputTriggerSource 用于指定觸發源(如TRGI信號)。void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource, uint16_t TIM_ICPolarity, uint16_t ICFilter);
配置定時器 TIMx 的外部時鐘為 TIx 引腳的輸入信號。
參數包括輸入通道、極性(上升沿或下降沿觸發)以及輸入濾波器配置。void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
配置定時器 TIMx 的外部時鐘模式1,使用 ETR(外部觸發)作為時鐘源。
可以設置 ETR 的預分頻、極性和濾波器。oid TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
配置定時器 TIMx 的外部時鐘模式2,使用 ETR(外部觸發)作為時鐘源。
與模式1類似,但具體行為略有不同,通常用于更復雜的同步需求。
- 配置時基單元:PSC預分頻器、CNT計數器、ARP自動重裝器,這一部分用結構體配置就好了
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
初始化定時器的基本時間基配置。
TIM_TimeBaseInitStruct 是一個結構體,包含定時器的預分頻系數、計數模式、計數器周期、時鐘分頻、重復計數等參數。、void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
將 TIM_TimeBaseInitTypeDef 結構體初始化為默認值。------------------------------------------------------------------------------
//下面是和時基單元另外相關的函數,基礎知識在上面對定時器的介紹的時候也講過:
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
配置定時器的預分頻器值 Prescaler。
TIM_PSCReloadMode 指定是否立即加載新的分頻值(一般為 TIM_PSCReloadMode_Immediate)。void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
設置定時器的計數器值,即直接對 CNT 寄存器賦值。void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
配置自動重裝載寄存器(ARR)的預裝載功能,控制 ARR 值的更新方式。
若啟用預裝載,ARR 值將在下一個更新事件時加載到計數器。void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
設置定時器的計數器值,即直接對 CNT 寄存器賦值。void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
設置自動重裝載值(ARR),用于控制計數周期的長度。uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
獲取當前計數器的值(CNT寄存器的值)。uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
獲取當前的預分頻器值(PSC寄存器的值)。FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
獲取定時器的特定標志狀態(如更新標志、觸發標志等)。返回標志的狀態是設置還是清除。void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
清除指定的定時器標志,將對應的標志位復位。ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
檢查指定的定時器中斷是否發生,返回中斷狀態。void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
清除指定的定時器中斷掛起位,即重置中斷標志,使得該中斷能夠再次觸發。
- 配置中斷輸出控制,允許更新中斷輸出到NVCI
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
配置定時器的中斷功能,使能或禁用指定的中斷。
該函數用于控制定時器的特定中斷源的使能狀態。當 NewState 為 ENABLE 時,開啟指定的中斷源;當 NewState 為 DISABLE 時,關閉指定的中斷源。
- 配置NVIC,在NVIC中打開定時器中斷的通道,并配置一個優先級。 — 這一部分的函數在之前講解NVIC的基本一致。
- 運行控制配置,也就是使能定時器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
用于開啟或關閉指定的定時器。當 NewState 為 ENABLE 時,定時器開始運行;當 NewState 為 DISABLE 時,定時器停止運行。
- 定義定時器中斷函數
2.3 結構體
以下是結構體 TIM_TimeBaseInitTypeDef
的成員變量的中文注釋,并附上詳細的解釋:
typedef struct
{uint16_t TIM_Prescaler; /*!< 指定用于分頻定時器時鐘的預分頻器值。此參數可以是 0x0000 到 0xFFFF 之間的一個數值 */uint16_t TIM_CounterMode; /*!< 指定計數器模式。此參數可以是 @ref TIM_Counter_Mode 的一個值 */uint16_t TIM_Period; /*!< 指定將在下一個更新事件時裝載到活動自動重裝載寄存器中的周期值。此參數必須是 0x0000 到 0xFFFF 之間的一個數值 */ uint16_t TIM_ClockDivision; /*!< 指定時鐘分頻系數。此參數可以是 @ref TIM_Clock_Division_CKD 的一個值 */uint8_t TIM_RepetitionCounter; /*!< 指定重復計數器的值。每當 RCR 遞減計數器達到零時,生成一個更新事件并從 RCR 值 (N) 重新開始計數。在 PWM 模式下,這意味著 (N+1) 對應于:- 邊沿對齊模式下的 PWM 周期數- 中心對齊模式下的半個 PWM 周期數此參數必須是 0x00 到 0xFF 之間的一個數值。@note 此參數僅對 TIM1 和 TIM8 有效 */
} TIM_TimeBaseInitTypeDef;
TIM_Prescaler
(預分頻器):
-
- 作用:設置定時器時鐘的預分頻值。
- 范圍:可以取 0x0000 到 0xFFFF 之間的數值。
- 解釋:定時器的時鐘頻率是通過預分頻器分頻得到的,即
CK_CNT = CK_PSC / (TIM_Prescaler + 1)
,其中CK_PSC
為輸入的時鐘頻率。預分頻值越大,CK_CNT
的頻率就越低。通過控制預分頻器,可以更靈活地控制定時器的計數速度。
TIM_CounterMode
(計數器模式):
-
- 作用:設置計數器的計數模式。
- 取值:可以是
TIM_Counter_Mode
的枚舉值,常見的值包括:
-
-
- 向上計數模式:
TIM_CounterMode_Up
- 向下計數模式:
TIM_CounterMode_Down
- 中心對齊計數模式(可以產生對稱信號,常用于 PWM):
TIM_CounterMode_CenterAligned1、TIM_CounterMode_CenterAligned2、TIM_CounterMode_CenterAligned3
- 向上計數模式:
-
-
- 解釋:在不同的模式下,計數器可以進行向上計數(從 0 計數到設定的周期值)或向下計數(從設定的周期值計數到 0)。中心對齊模式則會先向上計數到設定值,然后再向下計數到 0。
TIM_Period
(周期):
-
- 作用:設置自動重裝載寄存器的周期值。
- 范圍:0x0000 到 0xFFFF 之間的數值。
- 解釋:計數器計數到
TIM_Period
的值后會產生更新事件(UEV),然后計數器會重置為 0。周期值的大小決定了計數器溢出所需的時間,因此可以通過調整周期值來設置計時周期。
TIM_ClockDivision
(時鐘分頻):
-
- 作用:控制定時器時鐘的分頻系數。
- 取值:可以是
TIM_Clock_Division_CKD
的枚舉值,通常包括以下選項:
-
-
- 不分頻 (
0
):TIM_CKD_DIV1
- 分頻系數為 2 (
1
):TIM_CKD_DIV2
- 分頻系數為 4 (
2
):TIM_CKD_DIV4
- 不分頻 (
-
-
- 解釋:在某些情況下,可以對定時器的內部時鐘進行分頻,以滿足不同應用場景的需求。通常用于一些特殊的計時應用中。還有就是對于一些外部的時鐘,要達到時基單元是要經過濾波器進行濾波的,濾波器的原理就是在固定的時鐘頻率下對波形進行采樣,如果連續N個采樣點都是相同的,則代表信號穩定,就將采樣值輸出出去。而這個固定的時鐘采樣頻率就是可以由內部的時鐘頻率而來,也可以是由內部時鐘加一個分頻器后分頻而來,后者這個分頻是多少,就是由該成員決定。
TIM_RepetitionCounter
(重復計數器):
-
- 作用:設置重復計數器值。
- 范圍:0x00 到 0xFF 之間的數值。
- 解釋:每當重復計數器遞減至零時,會生成一個更新事件(UEV),然后重復計數器會重新加載設定的值。在 PWM 模式下,這一參數用于控制 PWM 信號的重復周期數。對于邊沿對齊模式,重復計數器的值加 1 就是 PWM 周期數;而對于中心對齊模式,則是半個 PWM 周期數。注意,重復計數器只適用于高級定時器(如 TIM1 和 TIM8),在普通定時器中無效。
2.4 編寫
2.4.1 定時器內部中斷
User:
- 📎main.c
System:
- 📎Timer.h
- 📎Timer.c
Hardware:
- 📎OLED_Font.h
- 📎OLED.c
- 📎OLED.h
#include "stm32f10x.h" // Device header/*** 函 數:定時中斷初始化*/
void Timer_Init(void)
{/*開啟時鐘*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //開啟TIM2的時鐘/*配置時鐘源*/TIM_InternalClockConfig(TIM2); //選擇TIM2為內部時鐘,若不調用此函數,TIM默認也為內部時鐘/*時基單元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定義結構體變量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //時鐘分頻,選擇不分頻,此參數用于配置濾波器時鐘,不影響時基單元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //計數器模式,選擇向上計數TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //計數周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //預分頻器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重復計數器,高級定時器才會用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //將結構體變量交給TIM_TimeBaseInit,配置TIM2的時基單元 /*中斷輸出配置*/TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定時器更新標志位//TIM_TimeBaseInit函數末尾,手動產生了更新事件//若不清除此標志位,則開啟中斷后,會立刻進入一次中斷//如果不介意此問題,則不清除此標志位也可TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //開啟TIM2的更新中斷/*NVIC中斷分組*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC為分組2//即搶占優先級范圍:0~3,響應優先級范圍:0~3//此分組配置在整個工程中僅需調用一次//若有多個中斷,可以把此代碼放在main函數內,while循環之前//若調用多次配置分組的代碼,則后執行的配置會覆蓋先執行的配置/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure; //定義結構體變量NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //選擇配置NVIC的TIM2線NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC線路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //指定NVIC線路的搶占優先級為2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC線路的響應優先級為1NVIC_Init(&NVIC_InitStructure); //將結構體變量交給NVIC_Init,配置NVIC外設/*TIM使能*/TIM_Cmd(TIM2, ENABLE); //使能TIM2,定時器開始運行
}/* 定時器中斷函數,可以復制到使用它的地方
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}
*/
對72MHz(72000000Hz)進行7200的分頻,就是10KHz的頻率,也就是1/10K s記一次數,那么計數10000次不就是剛好1s,所以這個時鐘中斷是每1s申請一次中斷。
2.4.2 定時器外部中斷
User:
- 📎main.c
Hardware:
- 📎OLED_Font.h
- 📎OLED.c
- 📎OLED.h
System:
- 📎Timer.h
- 📎Timer.c
#include "stm32f10x.h" // Device header/*** 函 數:定時中斷初始化* 參 數:無* 返 回 值:無* 注意事項:此函數配置為外部時鐘,定時器相當于計數器*/
void Timer_Init(void)
{/*開啟時鐘*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //開啟TIM2的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //開啟GPIOA的時鐘/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //將PA0引腳初始化為上拉輸入/*外部時鐘配置*/TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);//選擇外部時鐘模式2,時鐘從TIM_ETR引腳輸入//注意TIM2的ETR引腳固定為PA0,無法隨意更改//最后一個濾波器參數加到最大0x0F,可濾除時鐘信號抖動/*時基單元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定義結構體變量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //時鐘分頻,選擇不分頻,此參數用于配置濾波器時鐘,不影響時基單元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //計數器模式,選擇向上計數TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; //計數周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //預分頻器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重復計數器,高級定時器才會用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //將結構體變量交給TIM_TimeBaseInit,配置TIM2的時基單元 /*中斷輸出配置*/TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定時器更新標志位//TIM_TimeBaseInit函數末尾,手動產生了更新事件//若不清除此標志位,則開啟中斷后,會立刻進入一次中斷//如果不介意此問題,則不清除此標志位也可TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //開啟TIM2的更新中斷/*NVIC中斷分組*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC為分組2//即搶占優先級范圍:0~3,響應優先級范圍:0~3//此分組配置在整個工程中僅需調用一次//若有多個中斷,可以把此代碼放在main函數內,while循環之前//若調用多次配置分組的代碼,則后執行的配置會覆蓋先執行的配置/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure; //定義結構體變量NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //選擇配置NVIC的TIM2線NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC線路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //指定NVIC線路的搶占優先級為2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC線路的響應優先級為1NVIC_Init(&NVIC_InitStructure); //將結構體變量交給NVIC_Init,配置NVIC外設/*TIM使能*/TIM_Cmd(TIM2, ENABLE); //使能TIM2,定時器開始運行
}/*** 函 數:返回定時器CNT的值* 參 數:無* 返 回 值:定時器CNT的值,范圍:0~65535*/
uint16_t Timer_GetCounter(void)
{return TIM_GetCounter(TIM2); //返回定時器TIM2的CNT
}/* 定時器中斷函數,可以復制到使用它的地方
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}
*/