使用STM32F407控制5路超声波传感器

       最近在做小车避障的功能,需要用STM32F407控制5个传感器,结合板子的示例程序,调试了一段时间,终于成功读出5路传感器的测距信息。

      传感器原理不详细说了,基本上从Trig脚给一个脉宽10us以上的脉冲,然后发射探头发出8个40KHz的脉冲,检测到回波后,从Echo管脚输出高电平,高电平的持续时间与距离值关系为:(高电平时间*340m/s)/2。使用中,只要采集到Echo脚高电平的持续时间,就能测距了。

       在STM32F407上测量某一管脚的电平持续时间需要使用定时器的捕获功能(不知道有没有更简单的实现方法),查了手册,对于捕获模式的解释:在输入捕获模式下,当相应的ICx 信号检测到跳变沿后,将使用捕获/比较寄存器(TIMx_CCRx)来锁存计数器的值。发生捕获事件时,会将相应的CCXIF 标志(TIMx_SR 寄存器)置1,并可发送中断或DMA 请求(如果已使能)。如果发生捕获事件时CCxIF 标志已处于高位,则会将重复捕获标志CCxOF(TIMx_SR 寄存器)置1。可通过软件向CCxIF 写入0 来给CCxIF清零,或读取存储在TIMx_CCRx寄存器中的已捕获数据。向CCxOF 写入0 后会将其清零。其实就是在上升沿和下降沿分别记录定时器计数器的值,其差值相当于脉冲宽度。关于指点,网上找的图可以较清晰地表达:使用STM32F407控制5路超声波传感器_第1张图片

       首先设置定时器通道 x 为上升沿捕获,这样, t1 时刻,就会捕获到当前的 CNT 值,然后立即清零 CNT,并设置通道 x为下降沿捕获,这样到 t2 时刻,又会发生捕获事件,得到此时的 CNT 值,记为 CCRx2。 这样,根据定时器的计数频率,可以算出 t1~t2 的时间,从而得到高电平脉宽。 在t1~t2时,可能计数器会超过设定值arr,产生定时器溢出,这样就要对定时器溢出处理。最终,如图所示,t1~t2之间,CNT计数的次数等于:N*ARR+CCRx2,有了这个计数次数,再乘以 CNT 的计数周期,即可得到 t2-t1 的时间长度,即高电平持续时间。

       我用了TIM2和TIM5两个定时器,这两个定时器可提供8路通道,我用了5个。其中TIM5的CH2通道被占用,只要开中断就会不停进中断服务程序,故不用。定时器的设置是最重要的部分,放在TIM_Cap_Init()中,代码流程如下:

       首先配置TIM2和TIM5的中断优先级(NVIC_InitStructure),注册中断类型。然后配置TIM2和TIM5对应通道时钟,使能TIM时钟和GPIO时钟,配置GPIO工作模式(GPIO_Init),包括管脚、下拉、功能、速度等,再将管脚复用映射到对应定时器上(GPIO_PinAFConfig)。设置定时器的计数频率和计数模式等(TIM_TimeBaseInit)。之后设置捕获参数(TIM_ICInit):上升沿捕获、分频、滤波、用到的通道等等。设置完成后允许中断(TIM_ITConfig),使能定时器(TIM_Cmd),定时器开始计数。

      当进入中断服务程序时,先判断引起中断的类型,如果是某个通道引起的中断,调用单通道捕获处理函数,否则表示update中断,需要清除中断标志位。根据实际情况,上电时进入中断服务程序,中断类型TIM_IT_Update,要是不清除标志位,会不停进入中断。中断服务程序代码:

void TIM5_IRQHandler(void)
{ 
	TIM_ClearITPendingBit(TIM5, TIM_IT_Update); //Çå³ýÖжϱê־λ
	if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET) 
   {								       
		  SingleChannelHandler( &TIM5CH1);
	 }
		if(TIM_GetITStatus(TIM5, TIM_IT_CC3) !=RESET) 
	 {								   
			SingleChannelHandler( &TIM5CH3); 
	 }
}

void TIM2_IRQHandler(void)
{
	 TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //Çå³ýÖжϱê־λ
	if(TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET) 
   {								  
     
	     
		  SingleChannelHandler( &TIM2CH2);
		 
	 }
	 	if(TIM_GetITStatus(TIM2, TIM_IT_CC3) !=RESET) 
   {								  
     
		  SingleChannelHandler( &TIM2CH3);
		 
	 }
	 	if(TIM_GetITStatus(TIM2, TIM_IT_CC4) !=RESET) 
   {								  
     
		  SingleChannelHandler( &TIM2CH4);
		 
	 }

	
}
        主要的捕获处理放在SingleChannelHandler()中,参考了开发板的例程改的,代码如下:
void SingleChannelHandler(TIM_CAPTURE * tch )
{
	

	if((tch->CAPTURE_STA&0X80)==0)//»¹Î´³É¹¦²¶»ñ	
	{
		if(TIM_GetITStatus(tch->timX, TIM_IT_Update) != RESET)//Òç³ö
		{	     
			if(tch->CAPTURE_STA&0X40)//ÒѾ­²¶»ñµ½¸ßµçƽÁË
			{
				if((tch->CAPTURE_STA&0X3F)==0X3F)//¸ßµçƽ̫³¤ÁË
				{
					tch->CAPTURE_STA|=0X80;		//±ê¼Ç³É¹¦²¶»ñÁËÒ»´Î
					tch->CAPTURE_VAL=0XFFFFFFFF;
				}else tch->CAPTURE_VAL++;
			}	 
		}
		if(TIM_GetITStatus(tch->timX, tch->TIM_IT_CCX) != RESET)//²¶»ñ1·¢Éú²¶»ñʼþ
		{	
			if(tch->CAPTURE_STA&0X40)		//ÒѾ­²¶»ñµ½¸ßµçƽ£¬±¾´Î²¶»ñµ½Ï½µÑØ		
			{	  			
				tch->CAPTURE_STA|=0X80;		//±ê¼Ç³É¹¦²¶»ñµ½Ò»´Î¸ßµçƽÂö¿í
			  //tch->CAPTURE_VAL=TIM_GetCapture1(tch->timX);//»ñÈ¡µ±Ç°µÄ²¶»ñÖµ.
				
				switch( tch->TIM_IT_CCX ) // ¸ù¾Ý²»Í¬Í¨µÀÑ¡Ôñº¯Êý
				{
					case TIM_IT_CC1:						
						tch->CAPTURE_VAL=TIM_GetCapture1(tch->timX);//»ñÈ¡µ±Ç°µÄ²¶»ñÖµ.
						TIM_OC1PolarityConfig(tch->timX,TIM_ICPolarity_Rising); //CC1P=0 ÉèÖÃΪÉÏÉýÑز¶»ñ
						break;
					case TIM_IT_CC2:
						tch->CAPTURE_VAL=TIM_GetCapture2(tch->timX);//»ñÈ¡µ±Ç°µÄ²¶»ñÖµ.
						TIM_OC2PolarityConfig(tch->timX,TIM_ICPolarity_Rising); //CC1P=0 ÉèÖÃΪÉÏÉýÑز¶»ñ
						break;
					case TIM_IT_CC3:
						tch->CAPTURE_VAL=TIM_GetCapture3(tch->timX);//»ñÈ¡µ±Ç°µÄ²¶»ñÖµ.
						TIM_OC3PolarityConfig(tch->timX,TIM_ICPolarity_Rising); //CC1P=0 ÉèÖÃΪÉÏÉýÑز¶»ñ
						break;
					case TIM_IT_CC4:
						tch->CAPTURE_VAL=TIM_GetCapture4(tch->timX);//»ñÈ¡µ±Ç°µÄ²¶»ñÖµ.
						TIM_OC4PolarityConfig(tch->timX,TIM_ICPolarity_Rising); //CC1P=0 ÉèÖÃΪÉÏÉýÑز¶»ñ
						break;
					default:
						break;
				}
				//printf(" tch->TIM_IT_CCX = %d ",tch->TIM_IT_CCX); //´òÓ¡×ܵĸߵãƽʱ¼ä
	 			//TIM_OC1PolarityConfig(tch->timX,TIM_ICPolarity_Rising); //CC1P=0 ÉèÖÃΪÉÏÉýÑز¶»ñ

			}else  								//»¹Î´¿ªÊ¼,µÚÒ»´Î²¶»ñÉÏÉýÑØ
			{
				tch->CAPTURE_STA=0;			//Çå¿Õ
				tch->CAPTURE_VAL=0;
				tch->CAPTURE_STA|= 0X40;		//±ê¼Ç²¶»ñµ½ÁËÉÏÉýÑØ
				TIM_Cmd(tch->timX,DISABLE ); 	//¹Ø±Õ¶¨Ê±Æ÷
	 			TIM_SetCounter(tch->timX,0);
				
//				printf(" tch->CAPTURE_STA = %d ",tch->CAPTURE_STA); //´òÓ¡×ܵĸߵãƽʱ¼ä
	 			//TIM_OC1PolarityConfig(tch->timX,TIM_ICPolarity_Falling);		//CC1P=1 ÉèÖÃΪϽµÑز¶»ñ
				switch( tch->TIM_IT_CCX )
				{
					case TIM_IT_CC1:
						TIM_OC1PolarityConfig(tch->timX,TIM_ICPolarity_Falling); //CC1P=0 ÉèÖÃΪÉÏÉýÑز¶»ñ
						break;
					case TIM_IT_CC2:
						TIM_OC2PolarityConfig(tch->timX,TIM_ICPolarity_Falling); //CC1P=0 ÉèÖÃΪÉÏÉýÑز¶»ñ
						break;
					case TIM_IT_CC3:
						TIM_OC3PolarityConfig(tch->timX,TIM_ICPolarity_Falling); //CC1P=0 ÉèÖÃΪÉÏÉýÑز¶»ñ
						break;
					case TIM_IT_CC4:
						TIM_OC4PolarityConfig(tch->timX,TIM_ICPolarity_Falling); //CC1P=0 ÉèÖÃΪÉÏÉýÑز¶»ñ
						break;
					default:
						break;
				}
			
				TIM_Cmd(tch->timX,ENABLE ); 	//ʹÄܶ¨Ê±Æ÷s
			}		    
		}			     	    					   
 	}
	TIM_ClearITPendingBit(tch->timX, tch->TIM_IT_CCX | TIM_IT_Update); //Çå³ýÖжϱê־λ
}

      首先自定义了标志字CAPTURE_STA,共8位,最高位D7=1表示成功捕获到高电平脉宽,即捕获到上升沿后又捕获到下降沿,D6=1表示捕获到上升沿,D5~D0表示定时器溢出次数。CAPTURE_STA初始为0,程序开始判断是否成功捕获,未成功捕获则继续,这样捕获成功后就不再执行了。然后判断捕获类型,如果是某个通道引起的,则查看标志字,如果D6=0,说明是由于首次捕获到上升沿而进入中断,设置D6=1,计数器清零,然后配置捕获下降沿,这样再次进入中断时,可以看到标志字D6=1,说明此时捕获到了下降沿,读取计数器的值,可获得高电平时间。如果进入中断的类型是TIM_IT_Update,说明定时器计数溢出,根据之前定时器的设置,TIM2和TIM5均为1us累加1次,32位的定时器,溢出需要4292s,这样现实中是不会出现的,所以可以不做处理,但是为了以防万一,强制设置捕获成功。

      在main函数中循环判断CAPTURE_STA状态,成功捕获后读取计数值可以计算出距离信息,将结果通过串口打印出来,如图:使用STM32F407控制5路超声波传感器_第2张图片

     5个传感器能检测到距离信息,下一步考虑准备安装在小车上了。









 


你可能感兴趣的:(STM32)