輸入捕獲模式可以用來測量脈沖寬度或者測量頻率。STM32 的定時器,除了 TIM6 和 TIM7,其他定時器都有輸入捕獲功能。以下是對脈沖寬度及頻率的計算。
#1、脈沖寬度
如下圖所示,采集該高電平脈沖的寬度,只需要進入輸入捕獲上升沿檢測,記錄當前的發生上升沿時的CNT值,再進行輸入捕獲下降沿檢測,也記錄當前發生下降沿時的CNT值,兩次CNT值的差值再根據計數的頻率就可以算出脈沖的寬度。 上升沿及下降沿捕獲的程序具體實現如下:
TIM8_Cap_Init(0XFFFF,72-1); //以1Mhz的頻率計數 void TIM8_UP_IRQHandler(void) { if((TIM8CH4_CAPTURE_STA&0X80)==0) //還未成功捕獲 { if (TIM_GetITStatus(TIM8,TIM_IT_Update) != RESET) { if(TIM8CH4_CAPTURE_STA&0X40) //已經捕獲到高電平了 { if((TIM8CH4_CAPTURE_STA&0X3F)==0X3F) //高電平太長了 { TIM8CH4_CAPTURE_STA|=0X80; //標記成功捕獲了一次 TIM8CH4_CAPTURE_VAL=0XFFFF; } else TIM8CH4_CAPTURE_STA++; //捕獲高電平后定時器溢出的次數++ } } } TIM_ClearITPendingBit(TIM8,TIM_IT_Update); //清除中斷標志位 } void TIM8_CC_IRQHandler(void) { if((TIM8CH4_CAPTURE_STA&0X80)==0) { if(TIM_GetITStatus(TIM8,TIM_IT_CC4) != RESET) //捕獲1發生捕獲事件 { if(TIM8CH4_CAPTURE_STA&0X40) //捕獲到一個下降沿 { TIM8CH4_CAPTURE_STA|=0X80;//標記成功捕獲到一次高電平脈寬 TIM8CH4_CAPTURE_VAL=TIM_GetCapture4(TIM8); TIM_OC4PolarityConfig(TIM8,TIM_ICPolarity_Rising); //CC1P=0 設置為上升沿捕獲 } else //還未開始,第一次捕獲上升沿 { TIM8CH4_CAPTURE_STA=0; //清空 TIM8CH4_CAPTURE_VAL=0; TIM_SetCounter(TIM8,0); //計數器清零 TIM8CH4_CAPTURE_STA|=0X40;//標記捕獲到了上升沿 TIM_OC4PolarityConfig(TIM8,TIM_ICPolarity_Falling);//CC1P=1 設置為下降沿捕獲 } } } TIM_ClearITPendingBit(TIM8, TIM_IT_CC4); //清除中斷標志位
程序中定時器輸入捕獲配置的TIM8CH4通道,CNT計數的頻率1MHZ,即計數1個就是1us。TIM8_UP_IRQHandler
是一個定時中斷函數,根據TIM8_Cap_Init(0XFFFF,72-1)
可知65536us會中斷一次,所以總的脈沖寬度時間如下:
temp=TIM8CH4_CAPTURE_STA&0X3F; //從TIM8_UP_IRQHandler中斷知中捕獲上升沿及下降沿期間進行此中斷的次數
temp*=65536;//溢出時間總和
temp+=TIM8CH4_CAPTURE_VAL; //得到總的高電平時間 TIM8CH4_CAPTURE_VAL為CNT計數的值
#2、頻率測量
如下圖所示,測量脈沖的頻率,則分別采集兩次輸入捕獲上升沿的CNT值,脈沖的頻率=f/△CNT
兩次上升沿捕獲的程序具體實現如下:
TIM8_Cap_Init(0XFFFF,72-1); //以1Mhz的頻率計數 void TIM8_CC_IRQHandler(void) { if(TIM_GetITStatus(TIM8,TIM_IT_CC4)!=RESET) { TIM_ClearITPendingBit(TIM8, TIM_IT_CC4); //清除中斷標志位 if(state==0) //捕獲第一個上升沿 { state=1; timecount=TIM_GetCapture4(TIM8); //記錄第一次上升沿的CNT值 } else if(state==1)//捕獲第二個上升沿 { state=0; timecount1=TIM_GetCapture4(TIM8); //記錄第二次上升沿的CNT值 if(timecount<timecount1) { test=timecount1-timecount; //兩次上升沿的差值 } else if(timecount>timecount1) { test=(0xffff-timecount)+timecount1; //兩次上升沿的差值 } else test=0; fq=1000000/test; //脈沖的頻率 } } }
由程序可知配置的定時器的輸入捕獲的計數的頻率為1MHZ,兩次捕獲上升沿的差值test為計數器CNT計的次數,所以總的周期即為T=1us*test
,所以頻率就fq=1000000/test HZ
;
另外,測量頻率除了還可以使用定時器的外部脈沖信號計數來進行。
3、定時器的外部計數模式測頻率
因為STM32有外部時鍾源模式,即可以根據外部脈沖信號進行計數,然后另外設定定時器定時中斷去讀取計數器的值,頻率=CNT/定時中斷時間
。程序代碼實現如下:
TIM3_Int_Init(9999,7199); //定時1s中斷一次 TIM2_Cap_Init(); //外部信號引腳脈沖檢測 TIM2_CH1_ETR void TIM2_Cap_Init(void) //配置 TIM2_CH1_ETR 為外部脈沖計數 { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能TIM2時鍾 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA時鍾 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0 清除之前設置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 輸入 GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_ResetBits(GPIOA,GPIO_Pin_0); //PA0 下拉 //初始化定時器2 TIM2 TIM_TimeBaseStructure.TIM_Period = 0xFFFF; //設定計數器自動重裝值 TIM_TimeBaseStructure.TIM_Prescaler =0; //預分頻器 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設置時鍾分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位 TIM_ITRxExternalClockConfig(TIM2,TIM_TS_ETRF); //配置外部觸發,否則不會計數 TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0); TIM_SetCounter(TIM2, 0); TIM_Cmd(TIM2,ENABLE ); //使能定時器2 } /******************************************************************************* * 名稱: TIM3_IRQHandler * 功能: 通用定時器3中斷服務函數 * 形參: 無 * 返回: 無 * 說明: 1S定時中斷一次 ******************************************************************************/ void TIM3_IRQHandler(void) { if(TIM_GetITStatus(TIM3,TIM_IT_Update)!= RESET) //檢查TIM3更新中斷發生與否 { TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIMx更新中斷標志 CNT=TIM_GetCounter(TIM2); //讀取1s內計數器計的CNT值 fq=CNT; //脈沖的頻率 TIM_SetCounter(TIM2,0); } }
因為知道定時中斷時間為1s,所以測量的頻率fq=CNT/1(HZ)
,頻率的計算及脈沖寬度的測量有以上方法測試。