我的小车调试进度之:实现超声波测距并显示

小车调试进度六更~~


利用定时器的输入捕获功能得到ECHO口高电平持续的时间,进而得到距离。


  • 关于超声波模块HC-SR04

    超声波测距模块工作原理:
    (1)采用IO口TRIG触发测距,给至少10us的高电平信号;
    (2)模块自动发送8个40khz的方波,自动检测是否有信号返回;
    (3)有信号返回,通过IO口ECHO输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间测试距离=(高电平时间*声速(340M/S))/2;
    (4)本模块使用方法简单,一个控制口(Trig)发一个10US以上的高电平,就可以在接收口(Echo)等待高电平输出。

  • 关于定时器的输入捕获功能

  1. 通用定时器以及高级定时器都具有输入捕获功能,且每个定时器都有四路输入捕获通道;
  2. 利用定时器的输入捕获功能捕获Echo口的高电平(先捕获一个上升沿,再捕获一个下降沿,即可判断捕获到高电平)进而可得到高电平的持续时间;
  3. 超声波模块的Echo引脚需要接入定时器的一路输入捕获通道(也就是和定时器的某个通道的GPIO口相连),如图:
     我的小车调试进度之:实现超声波测距并显示_第1张图片
    在这里插入图片描述
    我是将Echo接到了PB0口上,也就是定时器三的通道三上,所以我们在配置定时器时,需要把通道三设置为捕获模式,且初始化设置为上升沿捕获,这样一旦捕获到一个上升沿就会进入中断,我们在中断里再将捕获方式设置为下降沿捕获,并将计数器清零,那么下一次捕获到下降沿时才会再进入中断,则此时的计数值即为高电平的计数值,再经过处理后可转换为实际的高电平持续时间,进而可得到距离,思路就是这样,里面还有一些处理的细节,先看程序叭。
  • TIM3_CH3(通道三)输入捕获初始化
/*TIM3_CH3(通道三)输入捕获初始化*/
TIM_ICInitTypeDef  TIM3_ICInitStructure;
void TIM3_Cap_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_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //使能GPIOB时钟
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PB0 输入(ECHO)  
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;     
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     //PB1输出(Trig)
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;     //2M
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	//初始化定时器3 TIM3	 
	TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值 
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 	//预分频器   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
	//初始化TIM3输入捕获参数
	TIM3_ICInitStructure.TIM_Channel = TIM_Channel_3; //CC1S=01 	选择输入端 IC3映射到TI3上
	TIM3_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
	TIM3_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//映射关系,这里必须映射到TI寄存器上
	TIM3_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置输入分频,不分频 
	TIM3_ICInitStructure.TIM_ICFilter = 0x00;//配置输入滤波器 不滤波
	TIM_ICInit(TIM3, &TIM3_ICInitStructure);
	
	//中断分组初始化
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级0级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 	
	TIM_ITConfig(TIM3,TIM_IT_Update|TIM_IT_CC3,ENABLE);//允许更新中断 ,允许CC3IE捕获中断	
	TIM_Cmd(TIM3,ENABLE ); 	//使能定时器3
}	

  • 中断处理函数(在stm32f10x_it.c文件中写)
extern u16 TIM3CH3_CAPTURE_STA,TIM3CH3_CAPTURE_VAL;//输入捕获状态、输入捕获值

/*超声波回波脉宽读取中断*/
void TIM3_IRQHandler(void)
{ 		    		  			    
	u16 tsr;
	tsr=TIM3->SR;//将TIM3的状态寄存器值赋给tsr
	if((TIM3CH3_CAPTURE_STA&0X80)==0)//还未捕获完成	
	{
		if(tsr&0X01)//溢出
		{	    
			if(TIM3CH3_CAPTURE_STA&0X40)//已经捕获到高电平了
			{
				if((TIM3CH3_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
				{
					TIM3CH3_CAPTURE_STA|=0X80;//标记成功捕获了一次
					TIM3CH3_CAPTURE_VAL=0XFFFF;
				}
				else 
					TIM3CH3_CAPTURE_STA++;
			}	 
		}
		if(tsr&0x08)//捕获3发生捕获事件
		{	
			if(TIM3CH3_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
			{	  			
				TIM3CH3_CAPTURE_STA|=0X80;		//标记成功捕获到一次高电平脉宽
				TIM3CH3_CAPTURE_VAL=TIM3->CCR3;	//获取当前的捕获值.
				TIM3->CCER&=~(1<<9);			//CC1P=0 设置为上升沿捕获
			}
			else  								//还未开始,第一次捕获上升沿
			{
				TIM3CH3_CAPTURE_STA=0;			//清空
				TIM3CH3_CAPTURE_VAL=0;
				TIM3CH3_CAPTURE_STA|=0X40;		//标记捕获到了上升沿
				TIM3->CNT=0;					//计数器清空
				TIM3->CCER|=1<<9; 				//CC1P=1 设置为下降沿捕获
			}		    
		}			     	    					   
	}
	TIM3->SR=0;//清除中断标志位 	     
}

这里做几个说明:

  1. 关于tsr=TIM3->SR;
    这里需要知道TIMX的SR寄存器(状态寄存器)的相关知识,具体可在《stm32中文参考手册》中找,这里我们只需要知道第一位第四位分别是更新中断标志位捕获/比较3中断标志位就可以:
    在这里插入图片描述
    我的小车调试进度之:实现超声波测距并显示_第2张图片
    要注意: 我们在TIM3_CH3(通道三)输入捕获初始化函数中的设置会产生两种进入中断的方式:向上计数溢出和捕获到上升沿,所以在中断处理函数中就需要做一个判断,那么就需要用到TIMX的状态寄存器SR了,如果位0为1,那么就意味着是一个溢出中断如果位3为1,就意味着是一个捕获中断。
    也就是if(tsr&0X01)//溢出if(tsr&0x08)//捕获3发生捕获事件这两行代码的作用了。
  2. 关于TIM3CH3_CAPTURE_STA
    这个是我们设置的变量,但是实际中我们把它作为一个自定义的寄存器来用,也就是说我们把每一位都自定义上不同的功能,这样可以减少变量的定义(这一思想来自于《STM开发指南-库函数版》)
    我的小车调试进度之:实现超声波测距并显示_第3张图片
  • 超声波距离转换显示函数(main.c文件中写,主函数while(1)内每200ms获取一次转换距离,不宜一直获取!!!)
/*超声波回波接收函数*/
void Read_Distance(void)
{   
	PBout(1)=1;
	delay_us(15);  
	PBout(1)=0;	
	if(TIM3CH3_CAPTURE_STA&0X80)//成功捕获到了一次高电平
	{
		Distance = TIM3CH3_CAPTURE_STA&0X3F;
		Distance *= 65536;					        //溢出时间总和
		Distance += TIM3CH3_CAPTURE_VAL;		//得到总的高电平时间(单位为us)
		Distance = Distance*170/1000;//距离转换为mm(超声波测距范围为2cm到450cm)
		OLEDShowString(0, 10, "当前Distance:");
        OLED_ShowNumber(20,40,Distance/10,3,16);
        OLED_ShowChar(45,40,'.',16,1);//人为加入小数点
        OLED_ShowNumber(50,40,Distance%10,1,16);
        OLEDShowString(80, 40, "cm");
        OLED_Refresh_Gram();
		TIM3CH3_CAPTURE_STA=0;			//开启下一次捕获
	}				
}
  • 显示效果
    我的小车调试进度之:实现超声波测距并显示_第4张图片
    注意一点:用TIM3的输入捕获功能之后在同一程序内不能再用TIM3的其它功能,因为用的是同一个计数器!!!

你可能感兴趣的:(麦克纳姆轮小车制作)