定時器(Timer)最基本的功能就是定時了,好比定時發送 USART 數據、定時採集 AD數據等等。若是把定時器與 GPIO 結合起來使用的話能夠實現很是豐富的功能,能夠測量輸入信號的脈衝寬度,能夠生產輸出波形。定時器生產 PWM 控制電機狀態是工業控制廣泛方法,這方面知識很是有必要深刻了解。 STM32F4xx系列控制器有 2 個高級控制定時器、10 個通用定時器和 2 個基本定時器。markdown
這裏通用定時器的時鐘頻率是由APB1的分頻係數決定,若是APB1的預分頻係數是1,則通用定時器的時鐘頻率等於APB1的時鐘頻率,不然爲APB1時鐘的2倍。函數
定時器要實現計數必須有個時鐘源,基本定時器時鐘只能來自內部時鐘,高級控制定時器和通用定時器還能夠選擇外部時鐘源或者直接來自其餘定時器等待模式。咱們能夠經過 RCC 專用時鐘配置寄存器(RCC_DCKCFGR)的 TIMPRE位設置全部定時器的時鐘頻率,咱們通常設置該位爲默認值 0,使得表中可選的最大定時器時鐘爲 90MHz,即基本定時器的內部時鐘(CK_INT)頻率爲 90MHz。基本定時器只能使用內部時鐘,當 TIM6 和 TIM7 控制寄存器 1(TIMx_CR1)的 CEN 位置 1時,啓動基本定時器,而且預分頻器的時鐘來源就是 CK_INT。對於高級控制定時器和通用定時器的時鐘源能夠來找控制器外部時鐘、其餘定時器等等模式,較爲複雜。 使用SystenInit函數初始化的時候,各時鐘頻率以下: SYSCLK = 72M AHB時鐘 = 72M APB1時鐘=36M 因此APB1的分頻係數=AHB/APB1=2 由此可得CK_INT的時鐘頻率爲2*36M = 72M. 計數器的最終的頻率還須要通過PSC預分頻計算才能獲得 工具
基本定時器計數過程主要涉及到三個寄存器內容,分別是計數器寄存器(TIMx_CNT)、預分頻器寄存器(TIMx_PSC)、自動重載寄存器(TIMx_ARR),這三個寄存器都是 16 位有效數字,便可設置值爲 0至 65535。 ui
定時事件生成時間主要由 TIMx_PSC 和 TIMx_ARR兩個寄存器值決定,這個也就是定時器的週期。好比咱們須要一個 1s週期的定時器,具體這兩個寄存器值該如何設置? 假設,咱們先設置 TIMx_ARR寄存器值爲 9999,即當 TIMx_CNT從 0開始計算,恰好等於 9999時生成事件,總共計數 10000次,那麼若是此時時鐘源週期爲 100us便可獲得恰好 1s的定時週期。 接下來問題就是設置 TIMx_PSC寄存器值使得 CK_CNT 輸出爲 100us 週期(10000Hz)的時鐘。預分頻器的輸入時鐘 CK_PSC爲 90MHz,因此設置預分頻器值爲(9000-1)便可知足。spa
typedef struct
{
uint16_t TIM_Prescaler; // 預分頻器
uint16_t TIM_CounterMode; // 計數模式
uint32_t TIM_Period; // 定時器週期
uint16_t TIM_ClockDivision; // 時鐘分頻
uint8_t TIM_RepetitionCounter; // 重複計算器
} TIM_TimeBaseInitTypeDef;
複製代碼
(1) TIM_Prescaler:定時器預分頻器設置,時鐘源經該預分頻器纔是定時器時鐘,它設定TIMx_PSC 寄存器的值。可設置範圍爲 0 至 65535,實現 1至 65536 分頻。爲啥要搞一個預分頻器,那是由於系統時鐘頻率太快了,90MHZ啊,這通常人定時器可頂不住這麼快的速度,因此分頻一下,讓他的給定時器的時鐘頻率少一點,僅此而已。 (2) TIM_CounterMode:定時器計數方式,但是在爲向上計數、向下計數以及三種中心對齊模式。基本定時器只能是向上計數,即 TIMx_CNT只能從 0開始遞增,而且無需初始化。 (3) TIM_Period:定時器週期,實際就是設定自動重載寄存器的值,在事件生成時更新到影子寄存器。可設置範圍爲 0至 65535。 自動重載寄存器的值:舉個例子,你要往桶裏面放水,水滿了以後把他倒掉。那水滿須要多少水呢?就給他設定一個值,滴水滴100000滴才滿,拿去倒掉。倒掉以後,在從新設置滴10000滴,滿了再倒掉...... (4) TIM_ClockDivision:時鐘分頻,設置定時器時鐘 CK_INT 頻率與數字濾波器採樣時鐘頻率分頻比,基本定時器沒有此功能,不用設置。 (5) TIM_RepetitionCounter:重複計數器,屬於高級控制寄存器專用寄存器位,利用它能夠很是容易控制輸出 PWM 的個數。這裏不用設置.3d
設置通用定時器,併產生相應中斷,主要分爲如下幾個步驟(以TIM3爲例)調試
void TIM3_Int_Init()
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //時鐘使能
TIM_TimeBaseStructure.TIM_Prescaler =7199; //設置用來做爲TIMx時鐘頻率除數的預分頻值 10Khz的計數頻率
TIM_TimeBaseStructure.TIM_Period = 4999; //設置在下一個更新事件裝入活動的自動重裝載寄存器週期的值 計數到5000爲500ms
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設置時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位
TIM_ITConfig(TIM3, TIM_IT_Update,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先佔優先級0級
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //從優先級3級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根據NVIC_InitStruct中指定的參數初始化外設NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx外設
}
複製代碼
使用定時器以前都必須開啓定時器時鐘,基本定時器屬於 APB1總線外設。APB1總線外設時鐘=72M。 咱們把定時器設置自動重裝載寄存器 arr 的值爲4999,設置時鐘預分頻器寄存器psc的值爲7199,則驅動計數器的時鐘:CK_CNT = APB1Periph/ (7199+1)=72M/7200=10K,則計數器計數一次的時間等於:1/CK_CNT=0.0001s=0.1ms=100us,當計數器從0計數到4999時,產生一次中斷,則中斷一次的時間爲:100usX5000=0.0001sX5000=0.5s=500ms也就是半秒鐘。code
void TIM3_IRQHandler(void) //TIM3中斷
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //檢查指定的TIM中斷髮生與否:TIM 中斷源
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx的中斷待處理位:TIM 中斷源
LED1=!LED1;
}
}
複製代碼
這個中斷服務函數開始用if語句、TIM_GetITStatus()函數判斷是否TIM3發生了中斷,若是發生了中斷就清除TIM3的中斷標誌位。讓LED1燈反轉。orm
int main(void)
{
delay_init(); //延時函數初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 設置中斷優先級分組2
LED_Init(); //初始化與LED鏈接的硬件接口
TIM3_Int_Init(); //10Khz的計數頻率,計數到5000爲500ms
while(1)
{
LED0=!LED0;
delay_ms(200);
}
}
複製代碼
主函數首先延時函數初始化、設置中斷優先級分組二、初始化與LED鏈接的硬件接口、定時器3初始化。至此LED0就會每一個0.5秒翻轉一下。同時咱們爲了比較在while函數中讓LED1燈0.2秒翻轉一下作對比。 如今咱們來用keil仿真一下,看看是否是LED0是0.2ms翻轉一下,LED1是0.5ms翻轉一下。 1.配置keil仿真調試工具。 2.打開調試, 進入調試界面後 ,打開logic analysis窗口,並設置PWM輸出引腳 3.點擊全速運行,觀察示波器 能夠清楚到看到LED0是0.5ms翻轉一次。 4.用一樣的方法配置另外一個LED1,發現是0.2ms翻轉一下,和程序配置的同樣。 總結:咱們使用定時器的時候主要是要清楚分頻係數和重裝載值。這兩個決定了你要定時多長時間,另外如若你想使用stm32定時器,定時功能的話,須要定時多長時間作相應的操做,直接在中斷服務函數裏面寫你的代碼就好了,用中斷也同樣,要幹啥事直接在你的響應的服務函數中作操做就好了。還有定時器和計數器的分別,定時器就是計數器啊。你先計數,計數到必定程度溢出不就是實現了定時嗎?因此初學者不要糾結。接口