深入浅出STM32 定时器输入捕获实验之四路超声波讲解(寄存器版)

输入捕获

定时器的输入捕获模式可以用来测量脉冲宽度或者测量频率。本教程通过测量脉冲宽度,实现四路超声波测量距离的目的。如图所示:
深入浅出STM32 定时器输入捕获实验之四路超声波讲解(寄存器版)_第1张图片
首先,我们设定定时器工作模式为向上计数模式,图中t1-t2时间间隔就是我们需要测量的脉宽时间(即高电平时间)。测量方法如下:
1:设定定时器某通道为上升沿捕获,这样在t1时刻,就会捕获到当前值CNT值,然后马上清零,同时设置该通道为下降沿捕获,这样到t2时刻,又发生捕获事件,得到此时的CNT值,记为CCRx2。这样,根据定时器的计数频率可以算出t1-t2的时间,从而得到高电平脉宽。
2:在t1-t2的时间段里,有可能发生N次溢出,我们需要对此进一步处理,防止高电平过长,导致数据不准确。因此,CNT计数的次数等于N*ARR+CCRx2,有了这个计数次数,再乘以 CNT 的计数周期,即可得到 t2-t1 的时间长度,即高电平持续时间。输入捕获的原理,我们就介绍到这。

超声测距

超声测距究竟是什么高大上的玩意呢???超声测距其实就是通过单片机控制超声波模块发出一系列超声波,当超声波遇到障碍物时反弹回来,根据声音在空气中的传播速率340m/s,再结合上述所讲的方法求得时间t,应用初高中物理知识,即可求解距离。
(驱动超声波模块需要给它一个10-20us高电平)

即 测量距离=(高电平持续时间(340M/S))/2*
至于为啥要除以2,自个慢慢思量啦!!!)
此外,还可以这样快速计算出距离:
由于,我们算出的高电平时间是以us为单位,因此我们可以把声波传输速度看成大约为340M/S,合34,000CM/S。 即34,000除以1,000,000CM/US。 即为:0.0340CM/US 换种角度:1/(0.0343 CM/US) 即:29.00 US/CM。 这就意味着,每290.0US表示10CM的距离。1厘米就是29.00US。 但是发送后到接收到回波,声音走过的是2倍的距离呀。 所以实际距离就是1厘米,对应58.0US。
即 测量距离=(高电平持续时间/58.0(CM))

超声测距实现步骤

1.使能定时器时钟(寄存器APB1ENR/APB2ENR)。
2.使能复用IO口时钟(寄存器AHB1ENR )配置相应IO 口,此教成不加以说明,欲想了解翻之前博客。
3.设置重装载值与预分频系数(寄存器ARR和PSC)
4.每个通道选择输入端,设置为输入;配置滤波器于分频器(CCMR1/CCMR2寄存器)
5.每个通道设置允许捕获计数器的值到捕获寄存器中,设置捕获模式(CCER寄存器),最后开启捕获中断(DIER寄存器),这样通道配置就完成了。
6.最后,使能捕获(DIER寄存器)和使能计数器(CR1寄存器),设置中断分组(MY_NVIC_Init();//中断分组函数)
7.编写中断服务函数,距离求解函数(公式)
注:相关寄存器请自行翻阅芯片数据手册。

程序部分

void TIM4_CapHC_Init(u32 arr,u32 psc)
{
	
				RCC->APB1ENR=1<<2;//使能TIM4时钟
				RCC->AHB1ENR|=1<<1;
				GPIO_Set(GPIOB,PIN6|PIN7|PIN8|PIN9,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PD);
				GPIO_AF_Set(GPIOB,6,2);
				GPIO_AF_Set(GPIOB,7,2);
				GPIO_AF_Set(GPIOB,8,2);
				GPIO_AF_Set(GPIOB,9,2);

				TIM4->ARR=arr;     //设置重装载值              
				TIM4->PSC=psc;             //预分频器    
			
//CH1	
				TIM4->CCMR1|=1<<0;       //选择输入端 1通道设置为输入
        TIM4->CCMR1&=~(15<<4);         //配置为输入滤波器,不滤波
        TIM4->CCMR1&=~(3<<2);         //配置输入分频,不分频
        TIM4->CCER|=1<<0;           //允许捕获计数器的值到捕获寄存器中      
        TIM4->CCER&=~(1<<1);         //上升沿捕获
        TIM4->DIER|=1<<1;   //允许捕获中断
//CH2       
				TIM4->CCMR1|=1<<8;          //选择输入端 2通道设置为输入
        TIM4->CCMR1&=~(15<<12);          //配置为输入滤波器,不滤波
        TIM4->CCMR1&=~(3<<10);          //配置输入分频,不分频
        TIM4->CCER|=1<<4;         //允许捕获计数器的值到捕获寄存器中  
        TIM4->CCER&=~(1<<5);         //上升沿捕获
        TIM4->DIER|=1<<2;   //允许捕获中断
//CH3
				TIM4->CCMR2|=1<<0;          //选择输入端 3通道设置为输入
        TIM4->CCMR2&=~(15<<4);          //配置为输入滤波器,不滤波
        TIM4->CCMR2&=~(3<<2);         //配置输入分频,不分频
        TIM4->CCER|=1<<8;          //允许捕获计数器的值到捕获寄存器中  
        TIM4->CCER&=~(1<<9);         //上升沿捕获
        TIM4->DIER|=1<<3;               //允许捕获中断
//CH4	
				TIM4->CCMR2|=1<<8;          //选择输入端 3通道设置为输入
        TIM4->CCMR2&=~(15<<12);          //配置为输入滤波器,不滤波
        TIM4->CCMR2&=~(3<<10);          //配置输入分频,不分频
        TIM4->CCER|=1<<12;          //允许捕获计数器的值到捕获寄存器中  
        TIM4->CCER&=~(1<<13);         //上升沿捕获
        TIM4->DIER|=1<<3;               //允许捕获中断    				
        

				TIM4->DIER|=1<<0; 
				TIM4->CR1|=1<<7; 
				TIM4->CR1|=0x01; 
				
				MY_NVIC_Init(2,0,TIM4_IRQn,2);//抢占2,子优先0,组2

}



/**************************************************************************
函数功能:超声波回波脉宽读取中断
入口参数:无
返回  值:无
**************************************************************************/
u8 TIM4CH1_CAPTURE_STA=0;//通道一
u32 TIM4CH1_CAPTURE_VAL;
u8 TIM4CH2_CAPTURE_STA=0;//通道二
u32 TIM4CH2_CAPTURE_VAL;
u8 TIM4CH3_CAPTURE_STA=0;//通道三
u32 TIM4CH3_CAPTURE_VAL;
u8 TIM4CH4_CAPTURE_STA=0;//通道四
u32 TIM4CH4_CAPTURE_VAL;
void TIM4_IRQHandler(void)
{ 		    		  			    
	u16 tsr;
	tsr=TIM4->SR;
/*******************************************************************
	
********************************************************************/
	if((TIM4CH1_CAPTURE_STA&0X80)==0)//还未成功捕获	
				{ 
								if(tsr&0X01)//溢出
								{	    
										if(TIM4CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
										{
											if((TIM4CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
											{
												TIM4CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
												TIM4CH1_CAPTURE_VAL=0XFFFF;
											}else TIM4CH1_CAPTURE_STA++;
										}	 
								}
						   	if(tsr&0x02)//捕获1发生捕获事件
				    	{	
											if(TIM4CH1_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
											{	  			
											TIM4CH1_CAPTURE_STA|=0X80;		//标记成功捕获到一次高电平脉宽
											TIM4CH1_CAPTURE_VAL=TIM4->CCR1;	//获取当前的捕获值.
											TIM4->CCER&=~(1<<1);			//CC1P=0 设置为上升沿捕获
									  	}else  								//还未开始,第一次捕获上升沿
				   	{
											TIM4CH1_CAPTURE_STA=0;			//清空
											TIM4CH1_CAPTURE_VAL=0;
											TIM4CH1_CAPTURE_STA|=0X40;		//标记捕获到了上升沿
											TIM4->CNT=0;					//计数器清空
											TIM4->CCER|=1<<1; 				//CC1P=1 设置为下降沿捕获
						}		    
					    }			     	    					   
		  }
				
/*************************************************************************/
			 
/*************************************************************************/

	if((TIM4CH2_CAPTURE_STA&0X80)==0)//还未成功捕获	
			{ 
								if(tsr&0X01)//溢出
								{	    
										if(TIM4CH2_CAPTURE_STA&0X40)//已经捕获到高电平了
										{
											if((TIM4CH2_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
											{
												TIM4CH2_CAPTURE_STA|=0X80;//标记成功捕获了一次
												TIM4CH2_CAPTURE_VAL=0XFFFF;
											}else TIM4CH2_CAPTURE_STA++;
										}	 
								}
						   	if(tsr&0x04)//捕获2发生捕获事件
				    	{	
											if(TIM4CH2_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
											{	  			
											TIM4CH2_CAPTURE_STA|=0X80;		//标记成功捕获到一次高电平脉宽
											TIM4CH2_CAPTURE_VAL=TIM2->CCR2;	//获取当前的捕获值.
											TIM4->CCER&=~(1<<5);			//CC1P=0 设置为上升沿捕获
									  	}else  								//还未开始,第一次捕获上升沿
				   	{
											TIM4CH2_CAPTURE_STA=0;			//清空
											TIM4CH2_CAPTURE_VAL=0;
											TIM4CH2_CAPTURE_STA|=0X40;		//标记捕获到了上升沿
											TIM4->CNT=0;					//计数器清空
											TIM4->CCER|=1<<5; 				//CC1P=1 设置为下降沿捕获
						}		    
					   }			     	    					   
		   }
/***************************************************************************************
			 
****************************************************************************************/

	if((TIM4CH3_CAPTURE_STA&0X80)==0)//还未成功捕获	
				{ 
								if(tsr&0X01)//溢出
								{	    
										if(TIM4CH3_CAPTURE_STA&0X40)//已经捕获到高电平了
										{
											if((TIM4CH3_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
											{
												TIM4CH3_CAPTURE_STA|=0X80;//标记成功捕获了一次
												TIM4CH3_CAPTURE_VAL=0XFFFF;
											}else TIM4CH3_CAPTURE_STA++;
										}	 
								}
						   	if(tsr&0x08)//捕获1发生捕获事件
				    	{	
											if(TIM4CH3_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
											{	  			
											TIM4CH3_CAPTURE_STA|=0X80;		//标记成功捕获到一次高电平脉宽
											TIM4CH3_CAPTURE_VAL=TIM2->CCR3;	//获取当前的捕获值.
											TIM4->CCER&=~(1<<9);			//CC1P=0 设置为上升沿捕获
									  	}else  								//还未开始,第一次捕获上升沿
				   	{
											TIM4CH3_CAPTURE_STA=0;			//清空
											TIM4CH3_CAPTURE_VAL=0;
											TIM4CH3_CAPTURE_STA|=0X40;		//标记捕获到了上升沿
											TIM4->CNT=0;					//计数器清空
											TIM4->CCER|=1<<9; 				//CC1P=1 设置为下降沿捕获
							}		    
					    	}			     	    					   
				}

/***************************************************************************************
			 
****************************************************************************************/

	if((TIM4CH4_CAPTURE_STA&0X80)==0)//还未成功捕获	
				{ 
								if(tsr&0X01)//溢出
								{	    
										if(TIM4CH4_CAPTURE_STA&0X40)//已经捕获到高电平了
										{
											if((TIM4CH4_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
											{
												TIM4CH4_CAPTURE_STA|=0X80;//标记成功捕获了一次
												TIM4CH4_CAPTURE_VAL=0XFFFF;
											}else TIM4CH4_CAPTURE_STA++;
										}	 
								}
						   	if(tsr&0x08)//捕获1发生捕获事件
				    	{	
											if(TIM4CH4_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
											{	  			
											TIM4CH4_CAPTURE_STA|=0X80;		//标记成功捕获到一次高电平脉宽
											TIM4CH4_CAPTURE_VAL=TIM2->CCR3;	//获取当前的捕获值.
											TIM4->CCER&=~(1<<13);			//CC1P=0 设置为上升沿捕获
									  	}else  								//还未开始,第一次捕获上升沿
				   	{
											TIM4CH4_CAPTURE_STA=0;			//清空
											TIM4CH4_CAPTURE_STA=0;
											TIM4CH4_CAPTURE_STA|=0X40;		//标记捕获到了上升沿
											TIM4->CNT=0;					//计数器清空
											TIM4->CCER|=1<<13; 				//CC4P=1 设置为下降沿捕获
							}		    
					    	}			     	    					   
		   }
			 		TIM4->SR&=~(1<<0);//清除中断标志
	}

/**************************************************************************
函数功能:超声波接收回波函数
入口参数:无
返回  值:有
**************************************************************************/
u32 DisT4C1,DisT4C2,DisT4C3,DisT4C4;       //超声测距  
u8 ReadT4C1_Distane(void)
{   
	
	 PCout(4)=1;
	 delay_us(10);  
	 PCout(4)=0;	
			if(TIM4CH1_CAPTURE_STA&0X80)//成功捕获到了一次高电平
			{
			DisT4C1=TIM4CH1_CAPTURE_STA&0X3F;
			DisT4C1*=65536;					        //溢出时间总和
			DisT4C1+=TIM4CH1_CAPTURE_VAL;		//得到总的高电平时间us
      DisT4C1=DisT4C1/58.0;
			printf("DIST4C1:%d\n",DisT4C1);
			TIM4CH1_CAPTURE_STA=0;			//开启下一次捕获
		  }	
			return DisT4C1;			
}




u8 ReadT4C2_Distane(void)
{   
	 PCout(5)=1;
	 delay_us(20);  
	 PCout(5)=0;	
			if(TIM4CH2_CAPTURE_STA&0X80)//成功捕获到了一次高电平
			{
			DisT4C2=TIM4CH2_CAPTURE_STA&0X3F;
			DisT4C2*=65536;					        //溢出时间总和
			DisT4C2+=TIM4CH2_CAPTURE_VAL;		//得到总的高电平时间
			DisT4C2=DisT4C2/58.0;
			printf("DIST4C2:%d\n",DisT4C2);
			TIM4CH2_CAPTURE_STA=0;			//开启下一次捕获
		  }	
				return DisT4C2;
}

u8 ReadT4C3_Distane(void)
{   
	 PCout(6)=1;
	 delay_us(20);  
	 PCout(6)=0;	
			if(TIM4CH3_CAPTURE_STA&0X80)//成功捕获到了一次高电平
			{
			DisT4C3=TIM4CH3_CAPTURE_STA&0X3F;
			DisT4C3*=65536;					        //溢出时间总和
			DisT4C3+=TIM4CH3_CAPTURE_VAL;		//得到总的高电平时间
			DisT4C3=DisT4C3/58.0;
			printf("DIST4C3:%d\n",DisT4C3);
			TIM4CH3_CAPTURE_STA=0;			//开启下一次捕获
		  }			
		return DisT4C3;			
}

u8 ReadT4C4_Distane(void)
{   
			PCout(7)=1;
			delay_us(20);  
			PCout(7)=0;	
			if(TIM4CH4_CAPTURE_STA&0X80)//成功捕获到了一次高电平
			{
			DisT4C4=TIM4CH4_CAPTURE_STA&0X3F;
			DisT4C4*=65536;					        //溢出时间总和
			DisT4C4+=TIM4CH4_CAPTURE_VAL;		//得到总的高电平时间
			DisT4C4=DisT4C4/58.0;
			printf("DIST4C4:%d\n",DisT4C4);
			TIM4CH4_CAPTURE_STA=0;			//开启下一次捕获
		  }			
		return DisT4C4;			
}


以上仅是输入捕获初始化函数,定时器中断服务函数和距离求解函数,实测可以行得通。大家可以根据自己需求,自行添加或修改代码应用到实际工程。希望本教程对大家有所帮助,写得不好或者有误的地方,欢迎大家指正批评,谢谢大家!!!!

你可能感兴趣的:(单片机,C语言)