STM32-定時器
時間之河奔騰不息,子在川上曰:“逝者如斯夫,不捨晝夜。”
我們使用各種方法來估量時間,具體到STM32,當然是少不了定時器!
本文關於定時器的內容,分為下面幾部分:
1,定時器功能的實現(TIM1);
2,定時間隔的計算公式;
3,依賴的庫檔案;
4,另一個定時器的實現(TIM14);
先簡單介紹下開發環境,晶片型別是stm32F030C8,整合開發環境用的是Keil5 MDK-ARM,模擬器使用JLINK。
1,定時器功能的實現(TIM1)
定時器功能程式碼,主要分2部分:初始化函式,實現中斷處理函式。
另外,實現了一個延時函式,供外部呼叫。
初始化函式:
void TIM1_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 系統中TIM1用的是APB2,TIM14時鐘用的是APB1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //tim1時鐘使能,APB1時鐘8M
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV2; //分頻係數為2 //是對APB1的2倍頻進行分頻,分頻係數為2,所以頻率還是8M
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上計數
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重複計數設定 //對於TIM1是必須設定的
// 計算定時週期: t=(9+1)*1/f=10/(8M/(799+1))=10/10k(s)=1ms
TIM_TimeBaseInitStructure.TIM_Period = 9; //定時1000us - 1ms //最大65536
// TIM_TimeBaseInitStructure.TIM_Period = 99; //定時10ms,降低進入中斷的頻率 //最大65536
TIM_TimeBaseInitStructure.TIM_Prescaler = 799; //時鐘8M
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
TIM_ClearITPendingBit(TIM1,TIM_IT_Update);//清除TIM1的中斷待處理位:TIM 中斷源
TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE); //允許定時器1更新中斷
TIM_Cmd(TIM1,ENABLE); //使能定時器1
// 設定中斷優先順序
NVIC_InitStructure.NVIC_IRQChannel = TIM1_BRK_UP_TRG_COM_IRQn; //定時器1中斷
NVIC_InitStructure.NVIC_IRQChannelPriority = 0; //優先順序0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
中斷處理函式:
void TIM1_BRK_UP_TRG_COM_IRQHandler(void)
{
if(TIM_GetITStatus(TIM1,TIM_IT_Update) != RESET) //溢位中斷
{
if(gTimer>0){
gTimer--;
}
}
TIM_ClearITPendingBit(TIM1,TIM_IT_Update); //清除中斷標誌位
}
延時函式:
void delay_ms_tim(uint32_t nTimer)
{
gTimer=nTimer;
// gTimer=nTimer/10; //定時10ms,降低進入中斷的頻率。也降低了定時的準確性。
while(gTimer);
}
2,定時間隔的計算公式
先要確定系統工作的主頻
在我的系統中,沒有對系統時鐘做設定,使用了預設的內部晶振HSI,主頻為8M。
TIM1連線在APB2上,APB2與系統頻率一致,即8M。
具體到每個定時器,又有一個分頻係數,我們是這麼設定的:
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV2;
由於系統中是對APB1的2倍頻進行分頻,這裡取的分頻係數為2,所以TIM1的頻率還是8M。
定時器的預分頻引數與週期數如下設定的:
TIM_TimeBaseInitStructure.TIM_Prescaler = 799; //時鐘8M
TIM_TimeBaseInitStructure.TIM_Period = 9; //定時1000us - 1ms //最大65536
計算定時週期:
t=(TIM_Period+1)*1/(f/(TIM_Prescaler+1))
t=(9+1)*1/(f/(799+1))=10/(8M/800)=10/10k(s)=1/1000(s)=1ms
看到這裡,我們就知道了,這樣設定的TIM1,每1ms進入一次溢位中斷。
假如我想減少進入中斷的次數,例如每10ms進入一次,應該怎麼改呢?
有兩種方法:
1,其他設定不變,加大TIM_Period,如下:
TIM_TimeBaseInitStructure.TIM_Period = 99; //定時10ms,降低進入中斷的頻率
t=(99+1)*1/(f/(799+1))=100/(8M/800)=100/10k(s)=10/1000(s)=10ms
2,其他設定不變,加大TIM_Prescaler,如下:
TIM_TimeBaseInitStructure.TIM_Prescaler = 7999;
t=(9+1)*1/(f/(7999+1))=10/(8M/8000)=10/1k(s)=10ms
3,依賴的庫檔案
在本工程下的目錄:STM32F030x8_Timer\STM32F03x_FWLib\src\stm32f0xx_tim.c
相應的標頭檔案:stm32f0xx_tim.h
結構體:TIM_TimeBaseInitTypeDef
關鍵函式,例如:
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
適當的檢視庫函式名、庫函式的實現,會增加對stm32系統的瞭解。
4,另一個定時器的實現(TIM14)
相對於TIM,主要的改變就是,將TIM1修改為TIM14。
另外還有3個地方需要注意:
1,不同定時器,所在外設匯流排不同,在stm32系統中TIM14時鐘用的是APB1,TIM1用的是APB2。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //tim1時鐘使能,APB1時鐘8M
2,TIM1是高階計數器,必須將重複計數設定為0
repetitioncounter是重複計數,就是重複溢位多少次才給你來一個溢位中斷,
是在本次定時結束後,再重灌載定時幾次,才進入中斷。對於通常的應用,設定為0。
TIM_TimeBaseInitStructure.repetitioncounter = 0;//重複計數設定
當然,在TIM14的初始化中也這樣設定上,也是沒錯的。
3,中斷入口函式名的差異:
TIM1:TIM1_BRK_UP_TRG_COM_IRQn()
TIM14:TIM14_IRQHandler()
最後的實現如下:
初始化函式:
void TIM14_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 系統中TIM14時鐘用的是APB1,TIM1用的是APB2
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE); //tim14時鐘使能,APB1時鐘8M
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV2; //分頻係數為2 //是對APB1的2倍頻進行分頻,分頻係數為2,所以頻率還是8M
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上計數
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重複計數設定 //對於TIM1是必須設定的
// 計算定時週期: t=(9+1)*1/f=10/(8M/(799+1))=10/10k(s)=1ms
TIM_TimeBaseInitStructure.TIM_Period = 9; //定時1000us - 1ms //最大65536
// TIM_TimeBaseInitStructure.TIM_Period = 99; //定時10ms,降低進入中斷的頻率 //最大65536
TIM_TimeBaseInitStructure.TIM_Prescaler = 799; //時鐘8M
TIM_TimeBaseInit(TIM14, &TIM_TimeBaseInitStructure);
TIM_ClearITPendingBit(TIM14,TIM_IT_Update);//清除TIM14的中斷待處理位:TIM 中斷源
TIM_ITConfig(TIM14,TIM_IT_Update,ENABLE); //允許定時器14更新中斷
TIM_Cmd(TIM14,ENABLE); //使能定時器14
// 設定中斷優先順序
NVIC_InitStructure.NVIC_IRQChannel = TIM14_IRQn;//定時器14中斷
NVIC_InitStructure.NVIC_IRQChannelPriority = 0; //優先順序0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
中斷處理函式:
void TIM14_IRQHandler(void)
{
if(TIM_GetITStatus(TIM14,TIM_IT_Update) != RESET) //溢位中斷
{
if(gTimer>0){
gTimer--;
}
}
TIM_ClearITPendingBit(TIM14,TIM_IT_Update); //清除中斷標誌位
}
延時函式沒有變化。
注意,此工程中不能同時將TIM1與TIM14的初始化開啟,若打開了,由於兩個中斷處理裡面操作了同一個全域性變數gTimer,會導致計數不符合預期。
5,這個demo的工程程式碼,可以從如下地址獲取:
https://download.csdn.net/download/lintax/10734630