关于STM32F103输入捕获高精度采集频率信号的方法

发布时间:2024-04-20 17:01

前言

前段时间需要做一款频率采集设备,由于成本考虑,使用了APM32F103作为主控,APM32F103和STM32F103基本完全通用,有极个别BUG。不影响本次试验。客户要求的频率信号为11KHz到23KHz,精度要求在任何频率范围误差不能大于当前频率的万分之一以上(排除温度影响),采集速度要100次每秒以上。占空比可能会有变化。这种要求其实也只能选择STM32的输入捕获功能,我使用TIM2时钟的外部输入捕获,定时器采用36MHz时钟,时钟两分频。硬件输入分频设置为8分频,因为外部有硬件滤波器和信号处理电路,在此则不加输入滤波器。(时钟跑72M,遇到定时器中断少进或多进的BUG,所以改为36M)

一、程序思路

其实采集频率的思路很简单,就是根据脉冲与脉冲直接的时间长短,得出周期时间。我采用的是全部上升沿捕获,而不是上升沿后改为下降沿。作用是防止占空比的变化影响采集精度。上升沿第一次进入中断后将TIM2计数器清空,将中间寄存器加一后退出,当中间寄存器大于等于10时,将TIM2中的计数值读取到采样寄存器中。当前采集到的就是80个输入脉冲周期的计数器值,根据要求,最小采样速度为100次每秒,11KHZ采集速度最快为137.5次每秒,也勉强满足要求。

二、程序

定时器初始化代码:

void TIM2_signal_input_Init(void)
{	 
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
   	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	//使能TIM2时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA时钟
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; 
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;  
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);						 //PA0 下拉
	
	//定时器
	TIM_TimeBaseStructure.TIM_Prescaler =1; 	//预分频  
	TIM_TimeBaseStructure.TIM_Period = 0XFFFF; //自动重装值 
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分割
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); 
  
	//输入捕获
	TIM2_ICInitStructure.TIM_Channel = TIM_Channel_1;//该是那就映射到那
	TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV8;	 //配置输入分频,不分频 
    TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
    TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
    TIM2_ICInitStructure.TIM_ICFilter = 0x00;//不滤波
    TIM_ICInit(TIM2, &TIM2_ICInitStructure);
	
	//中断
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  //TIM2中断
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级0级
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级2级
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 
	TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断 ,允许CC1IE捕获中断	
    TIM_Cmd(TIM2,ENABLE ); 	//使能定时器2
}

中断代码:

//定时器2中断服务程序	 
void TIM2_IRQHandler(void)
{ 
 static unsigned char H_C=0;
	 
 	if((TIM2CH1_CAPTURE_STA&0X80)==0)//捕获成功后等待数据处理完成后,再次捕获	
	{	  
		if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
		{	    
			if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F)  //频率太低了,不检测了
			{
				TIM2CH1_CAPTURE_STA|=0X80;
				TIM2CH1_CAPTURE_VAL=0XFFFF;
			}
			else TIM2CH1_CAPTURE_STA++;	 
		}
	if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)//捕获1发生捕获事件
		{	
			if(TIM2CH1_CAPTURE_STA&0X40)		
			{//捕获10个周期了	
				TIM2CH1_CAPTURE_VAL=TIM_GetCapture1(TIM2);
				TIM2CH1_CAPTURE_STA|=0X80;		//出去处理数据,防止再次进入中断
			}else  								
			{//第一次捕获上升沿
			    if(H_C==0)
			    {
				  TIM2CH1_CAPTURE_STA=0;			//清空
				  TIM2CH1_CAPTURE_VAL=0;
				  TIM_SetCounter(TIM2,0);
				}
				H_C++;
				if(H_C==10)
				{//捕获9个周期了
				   H_C=0;
				   TIM2CH1_CAPTURE_STA|=0X40;		
				}
			}		    
		}			     	    					   
 	}
 
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
}

转换为实际频率:

		 if(TIM2CH1_CAPTURE_STA&0X80)//成功捕获到八分频扭矩频率周期
		{
			temp=TIM2CH1_CAPTURE_STA&0X3F;
			temp*=65536;//溢出时间总和
			temp+=TIM2CH1_CAPTURE_VAL;//得到总的时间
			frequent_input=36000000.0/temp;
			frequent_input*=80;//实际为80分频
			frequent_input=frequent_input-(pow((frequent_input/100),2)*0.000312);//消除静差
			ui_cst=(unsigned int)frequent_input;
			
	        //可以自己加入数据输出函数
	        //
	        //
	        
		    TIM2CH1_CAPTURE_STA=0;			
		}

下载到板子

这是20KHZ,实际采样和输出对比。
关于STM32F103输入捕获高精度采集频率信号的方法_第1张图片
关于STM32F103输入捕获高精度采集频率信号的方法_第2张图片
这是10KHZ,实际采样和输出对比。
关于STM32F103输入捕获高精度采集频率信号的方法_第3张图片

关于STM32F103输入捕获高精度采集频率信号的方法_第4张图片

总结

如果要求采样速度有降低,可以分频系数拉大,数据可以更精确。当前程序实际测试重复精度在十万分之五左右。可以看出程序和正点原子很像,感谢正点原子的教程与例程参考。

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号