1. 程式人生 > >STM32-定時器

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