HY-SRF05 五针超声波测距模块 在stm32f4上实现 附代码 个人经验

测距原理

HC-SRF05超声波测距模块可提供2cm-400cm的非接触式距离感测功能,测距精度可达高到3mm;模块包括超声波发射器、接收器与控制电路。像智能小车的测距以及转向,或是一些项目中,常常会用到。智能小车测距可以及时发现前方的障碍物,使智能小车可以及时转向,避开障碍物。
HY-SRF05 五针超声波测距模块 在stm32f4上实现 附代码 个人经验_第1张图片
原理:
1.给超声波模块接入电源和地;
2.给脉冲触发引脚(trig)输入一个长为20us的高电平方波;
3.输入方波后,模块会自动发射8个40KHz的声波,与此同时回波引脚(echo)端的电平会由0变为1;(此时应该启动定时器计时);
4.当超声波返回被模块接收到时,回波引 脚端的电平会由1变为0;(此时应该停止定时器计数),定时器记下的这个时间即为超声波由发射到返回的总时长;
5.根据声音在空气中的速度为344米/秒,即可计算出所测的距离。
HY-SRF05 五针超声波测距模块 在stm32f4上实现 附代码 个人经验_第2张图片
时序图表明你只需要提供一个10us以上脉冲信号,该模块内部将发出8个40kHz周期电平并检测回波。一旦检测到有回波信号则输出回响信号。回响信号的脉冲宽度与所测的距离成正比。由此通过发射信号到收到的回响信号时间间隔可以计算得到距离。

个人经验

上面的理论大可不必了解,这个东西无非就是通过声波测距,重点还是在代码上,搞了将近两周,踩了一些坑,和大家分享下:
1、5v的电压一定要接好,不然串口就会返回一个固定的值!
2、如果使用一般的延时来计时的话,配置好定时器就好了;但如果选择输入捕获来完成,注意配置时通道要对应好,否则串口会返回0或者很大的数。
HY-SRF05 五针超声波测距模块 在stm32f4上实现 附代码 个人经验_第3张图片
3、如果使用不同定时器进行则分别配置,如使用同一个定时器的不同通道,则注意逻辑:不能接到上升/下降沿就关闭定时器,会相互影响。
4、注意定时器时32位还是16位,可能存在数据位数导致的错误。

代码

我使用的是stm32f4,两个定时器,两路超声波,如果需要一个定时器稍稍改一下逻辑即可:
初始化定时器:(正点原子例程也有):

     TIM_ICInitTypeDef  TIM3_ICInitStructure;
	 GPIO_InitTypeDef GPIO_InitStructure;
	 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	 NVIC_InitTypeDef NVIC_InitStructure;
//定时器3通道4,定时器9通道1输入捕获配置
void HC_Reci_TIM3_Init(u16 arr,u16 psc)
{
	 GPIO_InitTypeDef GPIO_InitStructure;
	 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	 NVIC_InitTypeDef NVIC_InitStructure;
	
	 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  	//TIM3时钟使能 
	 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); 	//使能PORTB时钟	
	 	
	 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//PB1定时器3通道4
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能

	 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度100MHz
	 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
	 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉
	 GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化PB1

	 GPIO_PinAFConfig(GPIOB,GPIO_PinSource1,GPIO_AF_TIM3); //PB1复用位定时器3
		   
	 TIM_TimeBaseStructure.TIM_Prescaler=psc;  //定时器分频
	 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
	 TIM_TimeBaseStructure.TIM_Period=arr;   //自动重装载值
	 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
	
	 TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
	 
	//初始化TIM3通道4输入捕获参数
	 TIM3_ICInitStructure.TIM_Channel = TIM_Channel_4; //CC1S=01 	选择输入端 IC4映射到TI4上
   TIM3_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
   TIM3_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
   TIM3_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置输入分频,不分频 
   TIM3_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
   TIM_ICInit(TIM3, &TIM3_ICInitStructure);
	 
	 //允许更新中断,触发方式中断
	 TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
	 TIM_ITConfig(TIM3,TIM_IT_Trigger,ENABLE);
 
     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
	 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
	 NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;		//子优先级0
	 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	 NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器、
	 
	 TIM_ITConfig(TIM3,TIM_IT_Update|TIM_IT_CC4,ENABLE);//允许更新中断 ,允许CC1IE捕获中断	
 
   TIM_Cmd(TIM3,ENABLE );  //使能定时器3
}

输入捕获:

void TIM3_IRQHandler(void)//TIM3通道4
{
	 if((HC_1.sta&0X80)==0)//还未成功捕获	
	 {
	    if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)//溢出
		  {	
         Send_sound(GPIOB,GPIO_Pin_0);				
			   if(HC_1.sta&0X40)//已经捕获到高电平了
			   {
				    if((HC_1.sta&0X3F)==0X3F)//高电平太长了
				    {
					     HC_1.sta|=0X80;		//标记成功捕获了一次
					     HC_1.val=0XFFFF;
				    }else HC_1.sta++;
			   }	 
		  }
      if(TIM_GetITStatus(TIM3, TIM_IT_CC4) != RESET)//捕获1发生捕获事件
      {	
	       if(HC_1.sta&0X40)		//捕获到一个下降沿 		
			   {	  			
			      HC_1.sta|=0X80;		//标记成功捕获到一次高电平脉宽
			      HC_1.val=(u16)TIM_GetCapture4(TIM3);//获取当前的捕获值.
					 
					  HC_1.temp=HC_1.sta&0X3F; 
			      HC_1.temp*=0XFFFF;		 		         //溢出时间总和
			      HC_1.temp+=HC_1.val;			//得到总的高电平时间
			      HC_1.Distance=HC_1.temp*172/10000;//cm
			      HC_1.sta=0;			     //开启下一次捕获
					  printf("HC1: %d\n",HC_1.Distance);
	 			    TIM_OC4PolarityConfig(TIM3,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
//					  Send_sound(GPIOB,GPIO_Pin_0);
			   }
			   else  								//还未开始,第一次捕获上升沿
			   {
				    HC_1.sta=0;			//清空
				    HC_1.val=0;
				    HC_1.sta|=0X40;		//标记捕获到了上升沿
				    TIM_Cmd(TIM3,DISABLE ); 	//关闭定时器3
	 			    TIM_SetCounter(TIM3,0);
	 			    TIM_OC4PolarityConfig(TIM3,TIM_ICPolarity_Falling);		//CC1P=1 设置为下降沿捕获
				    TIM_Cmd(TIM3,ENABLE ); 	//使能定时器3
			   }		    
	    }			     	    					   
   }
   TIM_ClearITPendingBit(TIM3, TIM_IT_CC4|TIM_IT_Update);
}

发送高电平:

void Send_sound(GPIO_TypeDef* GPIOx,uint16_t Pinx)
{
   GPIO_SetBits(GPIOx,Pinx);
	 delay_us(20);
	 GPIO_ResetBits(GPIOx,Pinx);
}

那么任务函数就很简洁了:

void sentry_ultrasonic ()
{

	 HC_ult_send();
 	 HC_Reci_TIM3_Init(0XFFFF,84-1); //以1Mhz的频率计数 1us计数
	 HC_Reci_TIM9_Init(0XFFFF,84-1);
   	while(1)
   {		 
      task_delay_ms(50);
	 }	
}

以上代码已经经过串口验证无误,超声波测距正常。搞了快两周的测距模块终于差不多了,尽管踩坑无数。

萌新初来报道,多多包涵,大家凑合看哈。拜谢支持。

你可能感兴趣的:(HY-SRF05 五针超声波测距模块 在stm32f4上实现 附代码 个人经验)