STM32定時器輸入捕獲

通用定時器作爲輸入捕獲的使用。我們將用 TIM5 的通道 PA0)來做輸入捕獲,捕獲 PA0 上高電平的脈寬(用 WK_UP 按鍵輸入高電平),通過串口打印高電平脈寬時間

輸入捕獲簡介

輸入捕獲模式可以用來測量脈衝寬度或者測量頻率。 STM32 的定時器,除了 TIM6 和 TIM7,其他定時器都有輸入捕獲功能。STM32 的輸入捕獲,簡單的說就是通過檢測 TIMx_CHx 上的邊沿信號,在邊沿信號發生跳變(比如上升沿/下降沿)的時候,將當前定時器的值(TIMx_CNT)存放到對應的通道的捕獲/比較寄存器(TIMx_CCRx)裏面,完成一次捕獲。同時還可以配置捕獲時是否觸發中斷/DMA 

我們用到 TIM5_CH1 來捕獲高電平脈寬,也就是要先設置輸入捕獲爲上升沿檢測,記錄發生上升沿的時候 TIM5_CNT 的值。然後配置捕獲信號爲下降沿捕獲,當下降沿到來時,發生捕獲,並記錄此時的 TIM5_CNT 值。這樣,前後兩次 TIM5_CNT 之差,就是高電平的脈寬,同時 TIM5 的計數頻率我們是知道的,從而可以計算出高電平脈寬的準確時間。

輸入捕獲的配置步驟:

1)開啓 TIM5 時鐘和 GPIOA 時鐘,配置 P A0 爲下拉輸入

要使用 TIM5,我們必須先開啓 TIM5 的時鐘。這裏我們還要配置 P A0 爲下拉輸入,因爲我們要捕獲 TIM5_CH1 上面的高電平脈寬,而 TIM5_CH1 是連接在 PA0 上面的。

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);  //使能 TIM5 時鐘

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //使能 GPIOA 時鐘

2初始化 TIM5,設置 TIM5 的 ARR 和 PSC

在開啓了 TIM5 的時鐘之後,我們要設置 ARR 和 PSC 兩個寄存器的值來設置輸入捕獲的自動重裝載值和計數頻率。 這在庫函數中是通過 TIM_TimeBaseInit 函數實現的

點擊(此處)摺疊或打開

  1.    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  2.    TIM_TimeBaseStructure.TIM_Period = arr; //設定計數器自動重裝值 
  3.    TIM_TimeBaseStructure.TIM_Prescaler =psc; //設置預分頻值 
  4.    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // TDTS = Tck_tim
  5.    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上計數模式
  6.    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);

3設置 TIM5 的輸入比較參數,開啓輸入捕獲

輸入比較參數的設置包括映射關係,濾波,分頻以及捕獲方式等。這裏我們需要設置通道 1爲輸入模式,且 IC1 映射到 TI1(通道 1)上面,並且不使用濾波(提高響應速度)器,上升沿捕獲。庫函數是通過 TIM_ICInit 函數來初始化輸入比較參數的:

void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct)

同樣,我們來看看參數設置結構體 TIM_ICInitTypeDef 的定義:

點擊(此處)摺疊或打開

  1.    typedef struct
  2.    {
  3.        uint16_t TIM_Channel; //設置通道
  4.        uint16_t TIM_ICPolarity; //設 置 輸 入 信 號 的 有效 捕獲 極性 
  5.        uint16_t TIM_ICSelection; //設置映射關係
  6.        uint16_t TIM_ICPrescaler; //設置 輸入捕獲分頻係數
  7.        uint16_t TIM_ICFilter; //設置濾波器長度
  8.    } TIM_ICInitTypeDef;

配置代碼是:

點擊(此處)摺疊或打開

  1.    TIM_ICInitTypeDef TIM5_ICInitStructure; 
  2.    TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //選擇輸入端 IC1 映射到 TI1 上
  3.    TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕獲
  4.    TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到 TI1 上
  5.    TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置輸入分頻,不分頻 
  6.    TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置輸入濾波器 不濾波
  7.    TIM_ICInit(TIM5, &TIM5_ICInitStructure);

4)使能捕獲和更新中斷(設置 TIM5 的 DIER 寄存器)

因爲我們要捕獲的是高電平信號的脈寬,所以,第一次捕獲是上升沿,第二次捕獲時下降沿,必須在捕獲上升沿之後,設置捕獲邊沿爲下降沿,同時,如果脈寬比較長,那麼定時器就會溢出,對溢出必須做處理,否則結果就不準了。這兩件事,我們都在中斷裏面做,所以必須開啓捕獲中斷和更新中斷。 

這裏我們使用定時器的開中斷函數 TIM_ITConfig 即可使能捕獲和更新中斷:

TIM_ITConfig( TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允許更新中斷和捕獲中斷

5設置中斷分組,編寫中斷服務函數

設置中斷分組主要是通過函數 NVIC_Init()來完成。分組完成後,我們還需要在中斷函數裏面完成數據處理和捕獲設置等關鍵操作,從而實現高電平脈寬統計。在中斷服務函數裏面,跟以前的外部中斷和定時器中斷實驗中一樣,我們在中斷開始的時候要進行中斷類型判斷,在中斷結束的時候要清除中斷標誌位。使用到的函數分別爲 TIM_GetITStatus()函數和 TIM_ClearITPendingBit()函數。

if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET){}//判斷是否爲更新中斷

if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET){}//判斷是否發生捕獲事件

TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update);//清除中斷和捕獲標誌位

6)使能定時器(設置 TIM5 的 CR1 寄存器) 

最後,必須打開定時器的計數器開關,  啓動 TIM5 的計數器,開始輸入捕獲。 

TIM_Cmd(TIM5,ENABLE );    //使能定時器 5


例程:

點擊(此處)摺疊或打開

  1. #include "timer.h"
  2. #include "led.h"
  3. #include "usart.h"

  4. /**
  5.  * 定時器5通道1輸入捕獲配置
  6.  */
  7.  void TIM5_Cap_Init(u16 arr,u16 psc)
  8. {
  9.     GPIO_InitTypeDef GPIO_InitStructure;
  10.     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  11.     TIM_ICInitTypeDef TIM5_ICInitStructure;
  12.     NVIC_InitTypeDef NVIC_InitStructure;
  13.     
  14.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);    /*使能TIM5時鐘*/
  15.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); /*使能GPIOA時鐘*/
  16.     
  17.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; /**/
  18.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; /*PA0 輸入*/
  19.     GPIO_Init(GPIOA,&GPIO_InitStructure);
  20.     GPIO_ResetBits(GPIOA,GPIO_Pin_0); /*PA0 下拉*/
  21.     
  22.     /*初始化定時器5 TIM5*/
  23.     TIM_TimeBaseStructure.TIM_Period = arr; /*設定計數器自動重裝值 */
  24.     TIM_TimeBaseStructure.TIM_Prescaler = psc; /*預分頻器 */
  25.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; /*設置時鐘分割:TDTS = Tck_tim*/
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; /*TIM向上計數模式*/
  27.     TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure); /*根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位*/
  28.             
  29.     /* 初始化TIM5輸入捕獲參數 */
  30.     TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; /*CC1S=01     選擇輸入端 IC1映射到TI1上*/    
  31.     TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; /*上升沿捕獲*/
  32.     TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; /*映射到TI1上*/
  33.     TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; /*配置輸入分頻,不分頻*/
  34.     TIM5_ICInitStructure.TIM_ICFilter = 0; /*IC1F=0000 配置輸入濾波器 不濾波*/
  35.     TIM_ICInit(TIM5,&TIM5_ICInitStructure); 
  36.     
  37.     /*中斷分組初始化*/
  38.     NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;    /*TIM5中斷*/
  39.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; /*先佔優先級2級*/
  40.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; /*從優先級0級*/
  41.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; /*IRQ通道被使能*/
  42.     NVIC_Init(&NVIC_InitStructure);
  43.     
  44.     TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);/*允許更新中斷 ,允許CC1IE捕獲中斷*/
  45.     TIM_Cmd(TIM5,ENABLE); /*使能定時器5*/    
  46. }

  47. u8 TIM5CH1_CAPTURE_STA=0;    //輸入捕獲狀態                         
  48. u16    TIM5CH1_CAPTURE_VAL;    //輸入捕獲值

  49. /**
  50.  * 定時器5中斷服務程序     
  51.  */
  52. void TIM5_IRQHandler(void)
  53. { 
  54.     if((TIM5CH1_CAPTURE_STA&0X80)==0)//還未成功捕獲    
  55.     {
  56.         if(TIM_GetITStatus(TIM5,TIM_IT_Update) != RESET)
  57.         {
  58.             if(TIM5CH1_CAPTURE_STA&0X40)//已經捕獲到高電平了
  59.             {
  60.                 if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高電平太長了
  61.                 {
  62.                     TIM5CH1_CAPTURE_STA|=0X80;//標記成功捕獲了一次
  63.                     TIM5CH1_CAPTURE_VAL=0XFFFF;
  64.                 }else TIM5CH1_CAPTURE_STA++;
  65.             }
  66.         }
  67.         
  68.         if(TIM_GetITStatus(TIM5,TIM_IT_CC1) !=RESET)
  69.         {
  70.             if(TIM5CH1_CAPTURE_STA & 0x40)
  71.             {
  72.                 TIM5CH1_CAPTURE_STA|=0X80;        //標記成功捕獲到一次上升沿
  73.                 TIM5CH1_CAPTURE_VAL = TIM_GetCounter(TIM5);
  74.                 TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 設置爲上升沿捕獲
  75.             }
  76.             else
  77.             {
  78.                 TIM5CH1_CAPTURE_STA=0;            //清空
  79.                 TIM5CH1_CAPTURE_VAL=0;
  80.                 
  81.                 TIM_SetCounter(TIM5,0);
  82.                 TIM5CH1_CAPTURE_STA|=0X40;        //標記捕獲到了上升沿
  83.                 TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);        //CC1P=1 設置爲下降沿捕獲
  84.             }
  85.         }
  86.         
  87.     }
  88.     TIM_ClearITPendingBit(TIM5,TIM_IT_CC1|TIM_IT_Update); /*清除中斷標誌位*/
  89. }

點擊(此處)摺疊或打開

  1. extern u8 TIM5CH1_CAPTURE_STA;        //輸入捕獲狀態                         
  2. extern u16    TIM5CH1_CAPTURE_VAL;    //輸入捕獲值    
  3.  int main(void)
  4.  {        
  5.      u32 temp=0; 
  6.     delay_init();          //延時函數初始化     
  7.     NVIC_Configuration();      //設置NVIC中斷分組2:2位搶佔優先級,2位響應優先級
  8.     uart_init(9600);     //串口初始化爲9600
  9.      LED_Init();             //LED端口初始化
  10.  
  11.      TIM5_Cap_Init(0XFFFF,72-1);    //以1Mhz的頻率計數 
  12.        while(1)
  13.     {
  14.          delay_ms(10);
  15.                   
  16.          if(TIM5CH1_CAPTURE_STA&0X80)//成功捕獲到了一次上升沿
  17.         {
  18.             temp=TIM5CH1_CAPTURE_STA&0X3F;
  19.             temp*=65536;//溢出時間總和
  20.             temp+=TIM5CH1_CAPTURE_VAL;//得到總的高電平時間
  21.             printf("HIGH:%d us\r\n",temp);//打印總的高點平時間
  22.             TIM5CH1_CAPTURE_STA=0;//開啓下一次捕獲
  23.         }
  24.     }
  25.  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章