stm32測量信號頻率及佔空比

基於stm32f103單片機對信號頻率、佔空比的測量。

最近開始儀器儀表方面的學習了,計劃後期做一個示波器。所以這周就在stm32f103上面做了一個測量頻率、佔空比的小設計。總體上精度還是比較高的,測量頻率量程在35Hz—190KHz。頻率可以精確到小數點後四位,佔空比測量的精度也比較高,可以到小數點後兩位。
說到用stm32測頻率,都會想到用定時器的輸入捕獲模式,只需要一個定時器和一個IO口即可,前幾天在論壇上看到還有一種是用兩個定時器測頻率,一個定時器用來檢測信號跳變沿,另外一個用來精準定時,比如說用TIM1檢測跳變沿(假設爲上升沿),TIM2開一個1s的定時器中斷,這個1s就比較準確,在1s內TM1檢測到了多少個上升沿改信號的頻率就是多少。這種方法我本週二試過,精度比輸入捕獲模式下的高,而且還比較穩定,缺點是用到了兩個定時器,佔用的cpu資源較多。考慮到我後面任務需要,定時器可能會不夠用,故還是用的輸入捕獲模式。

實驗平臺:stm32f103zet6
定時器及通道:TIM2的通道2
IO口:PA1

定時器及輸入捕獲模式的配置:

u8 Edge_Flag;  //高低電平的標誌位
u16 Rising,Falling,Rising_Last;   

//定時器2輸入捕獲中斷初始化
void TIM2_Cap_Init()
{
	GPIO_InitTypeDef GPIO_InitStruct;   
	TIM_TimeBaseInitTypeDef TIM_InitStruct;
	TIM_ICInitTypeDef TIM_ICInitStruct;
	NVIC_InitTypeDef NVIC_InitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;   //浮空
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	TIM_InitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_InitStruct.TIM_CounterMode=TIM_CounterMode_Up;  //向上計數
	TIM_InitStruct.TIM_Period=0xffff;
	TIM_InitStruct.TIM_Prescaler=0;                //分頻係數 當分頻係數越大時,可測量頻率最小值越小
	TIM_TimeBaseInit(TIM2,&TIM_InitStruct);
	
	TIM_ICInitStruct.TIM_Channel=TIM_Channel_2;
	TIM_ICInitStruct.TIM_ICFilter=0x00;         //不濾波
	TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;     //第一次是檢測上升沿進入中斷
	TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1;
	TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI; //直接映射
	TIM_ICInit(TIM2,&TIM_ICInitStruct);
	TIM_ITConfig(TIM2,TIM_IT_CC2,ENABLE);
	
	NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0x00;    //搶佔優先級
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=0x01;          //響應優先級
	NVIC_Init(&NVIC_InitStruct);
	
	TIM_Cmd(TIM2,ENABLE);     //使能
}

定時器2的中斷服務函數:

void TIM2_IRQHandler(void)
{ 
	if(TIM_GetITStatus(TIM2,TIM_IT_CC2)!=RESET)    //捕獲到上升沿
	{
		if(Edge_Flag==1)
		{
			Rising=TIM2->CCR2;    //第一次檢測到下降沿
			TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising);  //再次改爲上升沿觸發
			Edge_Flag++; 
		}
		else if(Edge_Flag==2)
		{
			Rising_Last=TIM2->CCR2;
			TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising);
			Edge_Flag=0;     //標誌位清0
			TIM_SetCounter(TIM2,0); //定時器清0
		}
		else
		{
			Falling=TIM2->CCR2;     //第一次檢測到上升沿
			TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Falling);    //將上升沿觸發改爲下降沿觸發
			Edge_Flag++;
		}
	}
	TIM_ClearITPendingBit(TIM2,TIM_IT_CC2);    //清除標誌位
}

輸入捕獲模式下,中斷服務函數裏面處理的內容要儘量少,所以在記錄定時器捕獲到值時,直接將TIM2的CCR2寄存器裏面的值賦值給相應變量。

主函數:

int main(void)
{	
	delay_init();    //延時函數初始化	  
	
	OLED_Init();  //oled屏幕初始化
	OLED_On();
	OLED_Clear();
	
	TIM2_Cap_Init();   //定時器初始化

    while(1)
	{			
		OLED_ShowString(0,0,"Freq:",16);
		OLED_ShowString(100,0,"Hz",16);
		OLED_ShowNum(50,0,72000000/(Rising_Last-Falling),6,16);
		OLED_ShowString(0,2,"Duty:",16);
		OLED_ShowString(95,2,"%",16);
		OLED_Showdecimal(50,2,(float)(Rising-Falling)/(float)(Rising_Last-Falling)*100,2,3,16);
		delay_ms(100);
	}		
}

變量Rising_Last爲第二次檢測到上升沿捕獲到的值,Falling爲第一次捕獲到的值,兩者之差爲定時器計數的數值差,根據TIM_Prescaler=0(即不分頻),主頻爲72M,頻率f=72M/(Rising_Last-Falling)。而Rising-Falling爲高電平的時間,除以一個週期就是佔空比了。

實驗現象
頻率10K,佔空比60%

在這裏插入圖片描述
頻率102.56K,佔空比33%
在這裏插入圖片描述
可見效果還是挺不錯的,但是這種辦法包括上面講到的雙定時器法都有一個缺點,那就是當信號幅度小於2v時,單片機就檢測不到跳變沿。外部還需要硬件對信號進行適當放大。也是前幾天,在論壇上看到有人提出一個測頻率的叫過零檢測法,用ADC讀信號電壓值,ADC值爲0時進行記錄,再次爲0就相當於經過了半個週期。計算兩次ADC爲0的時間差,就可以計算出信號的頻率,這種方法不會受限於信號幅度大小。原理也比較簡單,相關程序我也正在寫,等測試沒問題後我再發出來和大家分享!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章