本章參考資料:《 STM32F4xx 參考手冊》、《 STM32F4xx 規格書》、庫幫助文檔
《 stm32f4xx_dsp_stdperiph_lib_um.chm》。
學習本章時,配合《 STM32F4xx 參考手冊》 基本定時器章節一起閱讀,效果會更佳,
特別是涉及到寄存器說明的部分。
特別說明,本書內容是以 STM32F42x 系列控制器資源講解。
TIM 簡介
定時器(Timer)最基本的功能就是定時了,比如定時發送 USART 數據、定時采集 AD
數據等等。如果把定時器與 GPIO 結合起來使用的話可以實現非常豐富的功能,可以測量
輸入信號的脈沖寬度,可以生產輸出波形。定時器生產 PWM 控制電機狀態是工業控制普
遍方法,這方面知識非常有必要深入了解。
STM32F42xxx 系列控制器有 2 個高級控制定時器、 10 個通用定時器和 2 個基本定時器,
還有 2 個看門狗定時器。看門狗定時器不在本章討論范圍,有專門講解的章節。控制器上
所有定時器都是彼此獨立的,不共享任何資源。各個定時器特性參考表 31-1。
再看參考手冊中的描述:
之前時鍾章節的博客:
我們程序設定的分頻系數是4和2,所以TIMx_CLK =2乘以PCLK1(因為 基本定時器是掛載在APB1上的) =90M。
基本定時器功能框圖
基本定時器的功能框圖包含了基本定時器最核心內容,掌握了功能框圖,對基本定時
器就有一個整體的把握,在編程時思路就非常清晰,見圖 31-1。
首先先看圖 31-1 中綠色框內容,第一個是帶有陰影的方框,方框內容一般是一個寄存
器名稱,比如圖中主體部分的自動重載寄存器(TIMx_ARR)或 PSC 預分頻器(TIMx_PSC),
這里要特別突出的是陰影這個標志的作用,它表示這個寄存器還自帶有影子寄存器,在硬
件結構上實際是有兩個寄存器,源寄存器是我們可以進行讀寫操作,而影子寄存器我們是
完全無法操作的,有內部硬件使用。影子寄存器是在程序運行時真正起到作用的,源寄存
器只是給我們讀寫用的,只有在特定時候(特定事件發生時)才把源寄存器的值拷貝給它的
影子寄存器。多個影子寄存器一起使用可以到達同步更新多個寄存器內容的目的。
接下來是一個指向右下角的圖標,它表示一個事件,而一個指向右上角的圖標表示中
斷和 DMA 輸出。這個我們把它放在圖中主體更好理解。圖中的自動重載寄存器有影子寄
存器,它左邊有一個帶有“ U”字母的事件圖標,表示在更新事件生成時就把自動重載寄
存器內容拷貝到影子寄存器內,這個與上面分析是一致。寄存器右邊的事件圖標、中斷和
DMA 輸出圖標表示在自動重載寄存器值與計數器寄存器值相等時生成事件、中斷和 DMA輸出。
①時鍾源
定時器要實現計數必須有個時鍾源,基本定時器時鍾只能來自內部時鍾,高級控制定
時器和通用定時器還可以選擇外部時鍾源或者直接來自其他定時器等待模式。我們可以通
過 RCC 專用時鍾配置寄存器(RCC_DCKCFGR)的 TIMPRE 位設置所有定時器的時鍾頻率,
我們一般設置該位為默認值 0,使得表 31-1 中可選的最大定時器時鍾為 90MHz,即基本定
時器的內部時鍾(CK_INT)頻率為 90MHz。
基本定時器只能使用內部時鍾,當 TIM6 和 TIM7 控制寄存器 1(TIMx_CR1)的 CEN 位
置 1 時,啟動基本定時器,並且預分頻器的時鍾來源就是 CK_INT。對於高級控制定時器
和通用定時器的時鍾源可以來找控制器外部時鍾、其他定時器等等模式,較為復雜,我們
在相關章節會詳細介紹。
②控制器
定時器控制器控制實現定時器功能,控制定時器復位、使能、計數是其基礎功能,基
本定時器還專門用於 DAC 轉換觸發。
③計數器
基本定時器計數過程主要涉及到三個寄存器內容,分別是計數器寄存器(TIMx_CNT)、
預分頻器寄存器(TIMx_PSC)、自動重載寄存器(TIMx_ARR),這三個寄存器都是 16 位有效
數字,即可設置值為 0 至 65535。
首先我們來看圖 31-1 中預分頻器 PSC,它有一個輸入時鍾 CK_PSC 和一個輸出時鍾
CK_CNT。輸入時鍾 CK_PSC 來源於控制器部分,基本定時器只有內部時鍾源所以
CK_PSC 實際等於 CK_INT,即 90MHz。在不同應用場所,經常需要不同的定時頻率,通
過設置預分頻器 PSC 的值可以非常方便得到不同的 CK_CNT,實際計算為: fCK_CNT 等
於 fCK_PSC/(PSC[15:0]+1)。
圖 31-2 是將預分頻器 PSC 的值從 1 改為 4 時計數器時鍾變化過程。原來是 1 分頻,
CK_PSC 和 CK_CNT 頻率相同。向 TIMx_PSC 寄存器寫入新值時,並不會馬上更新
CK_CNT 輸出頻率,而是等到更新事件發生時,把 TIMx_PSC 寄存器值更新到影子寄存器
中,使其真正產生效果。更新為 4 分頻后,在 CK_PSC 連續出現 4 個脈沖后 CK_CNT 才產
生一個脈沖。
在定時器使能(CEN 置 1)時,計數器 COUNTER 根據 CK_CNT 頻率向上計數,即每來
一個 CK_CNT 脈沖, TIMx_CNT 值就加 1。當 TIMx_CNT 值與 TIMx_ARR 的設定值相等
時就自動生成事件並 TIMx_CNT 自動清零,然后自動重新開始計數,如此重復以上過程。
為此可見,我們只要設置 CK_PSC 和 TIMx_ARR 這兩個寄存器的值就可以控制事件生成的
時間,而我們一般的應用程序就是在事件生成的回調函數中運行的。在 TIMx_CNT 遞增至
與 TIMx_ARR 值相等,我們叫做為定時器上溢。
自動重載寄存器 TIMx_ARR 用來存放於計數器值比較的數值,如果兩個數值相等就生
成事件,將相關事件標志位置位,生成 DMA 和中斷輸出。 TIMx_ARR 有影子寄存器,可
以通過 TIMx_CR1 寄存器的 ARPE 位控制影子寄存器功能,如果 ARPE 位置 1,影子寄存
器有效,只有在事件更新時才把 TIMx_ARR 值賦給影子寄存器。如果 ARPE 位為 0,修改
TIMx_ARR 值馬上有效。
為什么要加1,因為參考手冊這樣說的啊:
計數器、 自動重裝載寄存器
定時器使能(CEN 置 1)后,計數器 CNT在CK_CNT 驅
動下向上計數,當 TIMx_CNT 值不 TIMx_ARR 的設
定值相等時就自動生成事件並 TIMx_CNT 自動清零,
然后自動重新開始計數,如此重復以上過程。
定時時間的計算如何實現0.5S的定時 ?
思考我們基本定時器的最小定時時間(除了零之外)是多少?分母最大,分子最小的時刻,1/90M hz=0.01111us=11nm;最大定時時間,分母最小,分子最大,65536/(90M/65536)=47.7s。
定時器初始化結構體詳解
標准庫函數對定時器外設建立了四個初始化結構體,基本定時器只用到其中一個即
TIM_TimeBaseInitTypeDef,該結構體成員用於設置定時器基本工作參數,並由定時器基本
初始化配置函數 TIM_TimeBaseInit 調用,這些設定參數將會設置定時器相應的寄存器,達
到配置定時器工作環境的目的。這一章我們只介紹 TIM_TimeBaseInitTypeDef 結構體,其
他結構體將在相關章節介紹。
初始化結構體和初始化庫函數配合使用是標准庫精髓所在,理解了初始化結構體每個
成員意義基本上就可以對該外設運用自如了。初始化結構體定義在 stm32f4xx_tim.h 文件中,
初始化庫函數定義在 stm32f4xx_tim.c 文件中,編程時我們可以結合這兩個文件內注釋使用。
(1) TIM_Prescaler:定時器預分頻器設置,時鍾源經該預分頻器才是定時器時鍾,它設定
TIMx_PSC 寄存器的值。可設置范圍為 0 至 65535,實現 1 至 65536 分頻。
(2) TIM_CounterMode:定時器計數方式,可是在為向上計數、向下計數以及三種中心對
齊模式。基本定時器只能是向上計數,即 TIMx_CNT 只能從 0 開始遞增,並且無需初
始化。
(3) TIM_Period:定時器周期,實際就是設定自動重載寄存器的值,在事件生成時更新到
影子寄存器。可設置范圍為 0 至 65535。
(4) TIM_ClockDivision:時鍾分頻,設置定時器時鍾 CK_INT 頻率與數字濾波器采樣時鍾
頻率分頻比,基本定時器沒有此功能,不用設置。
(5) TIM_RepetitionCounter:重復計數器,屬於高級控制寄存器專用寄存器位,利用它可
以非常容易控制輸出 PWM 的個數。這里不用設置。
雖然定時器基本初始化結構體有 5 個成員,但對於基本定時器只需設置其中兩個就可
以,想想使用基本定時器就是簡單。
編程實戰:
//#define BASIC_TIM TIM6 //#define BASIC_TIM_CLK RCC_APB1Periph_TIM6 #define BASIC_TIM TIM7 #define BASIC_TIM_CLK RCC_APB1Periph_TIM7 #define BASIC_TIM_IRQn TIM7_IRQn #define BASIC_TIM_IRQHandler TIM7_IRQHandler //#define BASIC_TIM_IRQn TIM6_DAC_IRQn //#define BASIC_TIM_IRQHandler TIM6_DAC_IRQHandler
BH給的課后練習,歷程使用TIM6,完成TIM7的基本定時器,更改中斷源和中斷服務函數名,這個不是單獨只把之前歷程中的6改成7就行,因為中斷源有些變化,那么我們有沒辦法去掉這樣的弊病呢?在我之前用山外庫的時候,就做得很好,所以,我覺得,我需要寫一個更好用的庫函數版本:
通過自己用宏把后端給替換了,這樣在編程的時候,就真的是只用6改成7就行,這樣的庫函數對新手使用上來說更方便。
初始化處先清除確保不受干擾,再開啟更新中斷,最后使能定時器:
中斷服務函數中,先確保發生了中斷,然后反轉LED,最后清除中斷發生標志,確保下一次中斷服務函數正常工作。
又發現一點可以改進的地方:
這里還需要庫函數使用者去計算時間,而作為專業的庫函數,應該是自己寫好一個基准,比如定時1ms的,然后我調用一個函數 dealy_ms(50);就表示精確延時50ms,這里也是可以通過庫函數配置好一個最小時間基准例如ms級別的,通過形參傳遞參數,然后用這個形參倍數於之前的時間基准,這樣的庫函數,明顯更好。