目錄
一、TIM(Timer)定時器簡介
二、定時器類型
2.1基本定時器結構
2.2通用定時器結構
2.3高級定時器結構
三、定時中斷基本結構
四、時序圖分析
4.1 預分頻器時序
4.2 計數器時序
4.3 計數器無預裝時序(無影子寄存器)
4.4?計數器有預裝時序(有影子寄存器)
五、RCC時鐘樹?
六、開發步驟
七、定時器函數
八、實驗
8.1定時器定時中斷
8.2定時器外部時鐘
一、TIM(Timer)定時器簡介
①定時器可以對輸入的時鐘進行計數,并在計數值達到設定值時觸發中斷
②16位計數器、預分頻器、自動重裝寄存器的時基單元,在72MHz計數時鐘下可以實現最大59.65s的定時計數器:執行計數定時的一個寄存器,每來一個時鐘,計數器加1
預分頻器:對計數器的時鐘進行分頻,讓這個計數更加靈活
自動重裝寄存器:計數的目標值,計多少個時鐘來申請一次中斷
③不僅具備基本的定時中斷功能,而且還包含內外時鐘源選擇、輸入捕獲、輸出比較、編碼器接口、主從觸發模式等多種功能
④根據復雜度和應用場景分為了高級定時器、通用定時器、基本定時器三種類型
二、定時器類型
2.1基本定時器結構
主模式觸發DAC功能:把定時器的更新事件映射到觸發輸出TRGO(Trigger Out)的位置,TRGO直接接到DAC的觸發轉換引腳上,這樣就不需要通過中斷來觸發DAC轉換了。實現了硬件的自動化。??
?通用定時器和高級定時器除了向上計數模式,還有向下計數模式和中央對齊模式
2.2通用定時器結構
2.3高級定時器結構
三、定時中斷基本結構
四、時序圖分析
4.1 預分頻器時序
計數器計數頻率:CK_CNT = CK_PSC / (PSC + 1)
4.2 計數器時序
計數器溢出頻率:CK_CNT_OV = CK_CNT / (ARR + 1)= CK_PSC / (PSC + 1) / (ARR + 1)
計數器時間:(PSC + 1)(ARR + 1)/CK_PSC
4.3 計數器無預裝時序(無影子寄存器)
4.4?計數器有預裝時序(有影子寄存器)
五、RCC時鐘樹?
作用:產生和配置時鐘,將配置好的時鐘發送到各個外設系統
開發技巧:
在SystemInit函數中,首先啟動內部8MHz時鐘為系統時鐘運行,然后再啟動外部時鐘,進入PLL鎖相環進行倍頻,8MHz倍頻9倍,得到72MHz,鎖相環輸出穩定之后,選擇鎖相環輸出為系統時鐘,這樣就把系統時鐘由8MHz變成了72MHz。
實際問題:
如果外部晶振出現問題,程序時鐘慢了大概10倍,定時器定時1s,結果過了大概10s才進中斷。是因為現在是以內部時鐘8MHz運行的
六、開發步驟
①RCC打開時鐘
②選擇時基單元的時鐘源
③結構體配置時基單元(預分頻器,自動重裝器,計數模式)
④配置輸出中斷控制,允許更新中斷輸出到NVIC
⑤配置NVIC,在NVIC中打開定時器中斷的通道,并分配一個優先級
⑥運行控制,使能計數器
⑦寫定時器中斷函數
七、定時器函數
=====================================================================
=================================基本函數=============================
void TIM_DeInit(TIM_TypeDef* TIMx);
//定時器恢復缺省配置
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//時基單元初始化
//第一個參數:某個定時器,第二個參數:結構體
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
//把結構體變量賦默認值
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
//使能計數器
//第一個參數:TIMx選擇定時器,第二個參數:使能或失能
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
//使能中斷輸出信號(中斷輸出控制)
//第一個參數:TIMx選擇定時器,第二個參數:哪個中斷輸出,第三個參數:使能或失能
=====================================================================
=========================配置時鐘輸入的函數=============================
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
//選擇內部時鐘
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//選擇ITRx其他定時器時鐘
//第一個參數:選擇配置哪個定時器,第二個參數:選擇要接入哪個定時器
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,uint16_t TIM_ICPolarity, uint16_t ICFilter);//選擇TIx捕獲通道的時鐘
//第一個參數:TIMx,第二個參數:TIMx具體哪個引腳,第三、四個參數:輸入極性和濾波器
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);//ETR通過外部時鐘模式1輸入時鐘
//第一個參數:TIMx,第二個參數:外部觸發預分頻器,第三、四個參數:輸入極性和濾波器
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,?
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);//ETR通過外部時鐘模式2輸入時鐘
//第一個參數:TIMx,第二個參數:外部觸發預分頻器,第三、四個參數:輸入極性和濾波器
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);//單獨配置ETR引腳的預分頻器、極性、濾波器
//第一個參數:TIMx,第二個參數:外部觸發預分頻器,第三、四個參數:輸入極性和濾波器
=====================================================================
===================更改關鍵參數函數(預分頻值,自動重裝載值)==============
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
//修改預分頻值
//第一個參數:TIMx,第二個參數:預分頻值,第三個參數:寫入模式
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);//修改計數器的計數模式
//第一個參數:TIMx,第二個參數:新的計數器模式
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//自動重裝器預裝功能配置
//第一個參數:TIMx,第二個參數:預裝功能使能或失能
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
//給計數器值
//第一個參數:TIMx,第二個參數:計數器值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);//給自動重裝器寫入值
//第一個參數:TIMx,第二個參數:自動重裝值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
//獲取計數器值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);//獲取預分頻值
=====================================================================
============================獲取和清除標志位函數========================
//主函數
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);
八、實驗
8.1定時器定時中斷
實驗現象:1s計數加一
代碼實現:
Timer.c
#include "stm32f10x.h" // Device headervoid Timer_Init(void) {/*一、RCC開啟時鐘*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);/*二、選擇時基單元的時鐘源*/TIM_InternalClockConfig(TIM2);//選擇內部時鐘/*三、配置時基單元(預分頻器,自動重裝器,計數模式)*/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);/*避免剛初始化就進入中斷(復位就是1,而不是0)*/TIM_ClearFlag(TIM2,TIM_FLAG_Update);/*四、配置輸出中斷控制,允許更新中斷輸出到NVIC*/TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//開啟更新中斷到NVIC的通路/*五、配置NVIC,在NVIC打開定時器中斷的通道,分配優先級*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//中斷通道NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);/*六、運行控制,使能計數器*/TIM_Cmd(TIM2,ENABLE); }/*中斷函數模板 void TIM2_IRQHandler(void) {if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET){TIM_ClearITPendingBit(TIM2,TIM_IT_Update);} } */
Timer.h
#ifndef __TIMER_H #define __TIMER_Hvoid Timer_Init(void);#endif
main.c
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Timer.h"uint16_t Num;int main(void) {OLED_Init();Timer_Init();OLED_ShowString(1,1,"Num:");OLED_ShowString(2,1,"CNT:");while (1){OLED_ShowNum(1,5,Num,5);OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);//觀察CNT計數器值的變化情況} }void TIM2_IRQHandler(void) {if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET){Num ++;TIM_ClearITPendingBit(TIM2,TIM_IT_Update);} }
8.2定時器外部時鐘
方法:定時器指定的外部引腳輸入一個方波信號,來提供定時器計數的時鐘?
實驗現象:利用對射式紅外傳感器來手動模擬一個外部時鐘,用擋光片,依次遮擋、移開來模擬一個方波,定時器計數值(CNT)逐次加一,當CNT到9后,產生一次中斷,Num加一,CNT清零重新計數
代碼實現:
Timer.c
#include "stm32f10x.h" // Device headervoid Timer_Init(void) {/*一、RCC開啟時鐘 + GPIOA的初始化*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);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);/*二、選擇時基單元的時鐘源*/TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);//通過ETR的外部時鐘模式2,不分頻,不反向(高電平/上升沿有效),外部觸發濾波器/*三、配置時基單元(預分頻器,自動重裝器,計數模式)*/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);/*避免剛初始化就進入中斷(復位就是1,而不是0)*/TIM_ClearFlag(TIM2,TIM_FLAG_Update);/*四、配置輸出中斷控制,允許更新中斷輸出到NVIC*/TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);/*五、配置NVIC,在NVIC打開定時器中斷的通道,分配優先級*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);/*六、運行控制,使能計數器*/TIM_Cmd(TIM2,ENABLE); }/*查看CNT的值*/ uint16_t Timer_GetCounter(void) {return TIM_GetCounter(TIM2); }/*中斷函數模板 void TIM2_IRQHandler(void) {if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET){TIM_ClearITPendingBit(TIM2,TIM_IT_Update);} } */
Timer.h
#ifndef __TIMER_H #define __TIMER_Hvoid Timer_Init(void); uint16_t Timer_GetCounter(void);#endif
main.c
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Timer.h"uint16_t Num;int main(void) {OLED_Init();Timer_Init();OLED_ShowString(1,1,"Num:");OLED_ShowString(2,1,"CNT:");while (1){OLED_ShowNum(1,5,Num,5);OLED_ShowNum(2,5,Timer_GetCounter(),5);//觀察CNT計數器值的變化情況} }void TIM2_IRQHandler(void) {if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET){Num ++;TIM_ClearITPendingBit(TIM2,TIM_IT_Update);} }