超声波平面定位\跟踪系统-----STM32控制6路超声波

最近参见了学校的电子设计大赛(三流财经类学校比较水其中的题目大多数是这几年点电赛的题目)当时看到了这个题,几个人商量之后,就决定做这个题目,题目如下在这里插入图片描述(用的板子是原子哥的STM32 mini版)
在这里插入图片描述在这里插入图片描述
初步想的是用两个超声波,分别放在X、Y轴,分别测量物体距离超声波轴的值,然后减去超声波距离X、Y轴的距离作为机械误差,最后得到坐标,然后在LCD上显示出来,但是在测量中发现,一个超声波模块其左右两侧最大测量范围只有4CM,所有就只能选择用6个超声波对10CM的范围全覆盖,X,Y各三个。(用的最普通HC_SR_04超声波模块)请忽略我凌乱的书桌和凌乱的杜邦线
最后的成品也如上图所示,单个的超声波测距恐怕是对在座的大佬们一如反掌(文尾也会附带单个超声波的例程哟!),但这个同时控制六个超声波其实也不难,因为要避免各个发出的超声波存在干扰,所以要在一个在测距是其他超声波是关闭的,依次轮流打开,有点类似于动态数码管的显示。
因为要用到6个超声波所以要用到6个定时器的通道口,这里我用的是T2 的123通道和T4的123通道,进行一般的初始化使能就可以了

这里我只写定时器2的三个通道的初始化,定时器4的初始化是一样的				    		  HCSR04_TIM2_Cap_Init(0XFFFF,72-1);	//以1MHZ的频率计数,1us记一次数
void HCSR04_TIM2_Cap_Init(u16 arr,u16 psc)
{
	GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO初始化的结构体
	TIM_ICInitTypeDef  TIM2_ICInitStructure;//定义通道的初始化结构体
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;//定义定时器的初始化结构体
	NVIC_InitTypeDef NVIC_InitStructure;//中断初始化结构体
	
	//使能相关时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); 
	//三个超声波模块的ECHO引脚初始化
	//ECHO1
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;         //PA0 ECH0  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0输入无需设置速度
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	//ECHO2
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;         //PA1 ECH0  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA1 
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	//ECHO3
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_2;         //PA2 ECH0  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA2 
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	//	//三个超声波模块的TRIF引脚初始化   (引脚是随机选的不是很有序)用的C13 C0 C12
	//控制端一
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;					 //PC13
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;		     
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	         
  GPIO_Init(GPIOC, &GPIO_InitStructure);	               
	//控制端二
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;					 //PC0
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;		    
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	         
  GPIO_Init(GPIOC, &GPIO_InitStructure);	//³õʼ»¯ÍâÉèGPIO 
	//控制端三
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;					 //PC2½ÓTRIG
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	         
  GPIO_Init(GPIOC, &GPIO_InitStructure);	//³õʼ»¯ÍâÉèGPIO 

	//拉低电平等待高电平
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);             //PA0下拉
	GPIO_ResetBits(GPIOA,GPIO_Pin_1);             //PA1下拉
	GPIO_ResetBits(GPIOA,GPIO_Pin_2);             //PA2下拉
	//定时器二的初始化	
	TIM_TimeBaseStructure.TIM_Period = arr; 
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 	
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); 						   
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
	//三个通道的初始化 
	//oc1
	TIM2_ICInitStructure.TIM_Channel = TIM_Channel_1;//通道1
  TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//上升沿触发	
  TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1
  TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	//时钟不分频
  TIM2_ICInitStructure.TIM_ICFilter = 0x00;//不滤波
  TIM_ICInit(TIM2, &TIM2_ICInitStructure);
	//TIM2 OC2
	TIM2_ICInitStructure.TIM_Channel = TIM_Channel_2; 
  TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 
  TIM2_ICInitStructure.TIM_ICFilter = 0x00;
  TIM_ICInit(TIM2, &TIM2_ICInitStructure);
	//TIM2 OC3
	TIM2_ICInitStructure.TIM_Channel = TIM_Channel_3;
  TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	
  TIM2_ICInitStructure.TIM_ICFilter = 0x00;
  TIM_ICInit(TIM2, &TIM2_ICInitStructure);
	
	//因为要用到中断所以要初始化中断
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; 
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure); ÷ 
	//使能三个通道和更新中断
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	TIM_ITConfig(TIM2,TIM_IT_CC1,ENABLE);
	TIM_ITConfig(TIM2,TIM_IT_CC2,ENABLE);
	TIM_ITConfig(TIM2,TIM_IT_CC3,ENABLE);
		
  TIM_Cmd(TIM2,ENABLE );使能定时器二
	
}

这里是中断处理函数,设置了两个变量,用了一个协议来记录高电平的时间,

u8  TIM2CH1_CAPTURE_STA=0;	//输入捕获状态	    				
u16	TIM2CH1_CAPTURE_VAL;	//输入捕获值

u8  TIM2CH2_CAPTURE_STA=0;//输入捕获状态		    				
u16	TIM2CH2_CAPTURE_VAL;	//输入捕获值

u8  TIM2CH3_CAPTURE_STA=0;	//输入捕获状态		    				
u16	TIM2CH3_CAPTURE_VAL;	//输入捕获值


//定时器二中断处理函数
void TIM2_IRQHandler(void)
{ 
//通道1捕获中断函数
 	if((TIM2CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
	{	  
		if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) //开始捕获但还未捕获完成
		{	    
			if(TIM2CH1_CAPTURE_STA&0X40)//已经捕获到了高电平
			{
				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)//捕获以发生捕获事件
			{	
			if(TIM2CH1_CAPTURE_STA&0X40)		//捕获到一个下降沿
			{	  			
				TIM2CH1_CAPTURE_STA|=0X80;		//标记成功捕获到一个下降沿
				TIM2CH1_CAPTURE_VAL=TIM_GetCapture1(TIM2);
		   		TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Rising); //设为上升沿捕获
			}else  								//未开始 第一次捕获到上升沿
			{
				TIM2CH1_CAPTURE_STA=0;			//清空
				TIM2CH1_CAPTURE_VAL=0;
	 			TIM_SetCounter(TIM2,0);             //计数器的值设为0
				TIM2CH1_CAPTURE_STA|=0X40;		/捕获到了上升沿
		   	TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Falling);		//CC1P=1设为下降沿捕获
			}		    
		}				
 	}
	//IC2中断捕获函数   内容大体与OC1函数一致,要细心把相关位换成通道2的标志字
	 	if((TIM2CH2_CAPTURE_STA&0X80)==0)
	{	  
		if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) 
		{	    
			if(TIM2CH2_CAPTURE_STA&0X40)
			{
				if((TIM2CH2_CAPTURE_STA&0X3F)==0X3F)
				{
					TIM2CH2_CAPTURE_STA|=0X80;
					TIM2CH2_CAPTURE_VAL=0XFFFF;
				}else TIM2CH2_CAPTURE_STA++;    
			}	 
		}
	if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)  
			{	
			if(TIM2CH2_CAPTURE_STA&0X40)			
			{	  			
				TIM2CH2_CAPTURE_STA|=0X80;	
				TIM2CH2_CAPTURE_VAL=TIM_GetCapture2(TIM2);
		   		TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising); 
			}else  							
			{
				TIM2CH2_CAPTURE_STA=0;			
				TIM2CH2_CAPTURE_VAL=0;
	 			TIM_SetCounter(TIM2,0);       
				TIM2CH2_CAPTURE_STA|=0X40;	
		   	TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Falling);	
			}		    
		}				
 	}
//IC3通道捕获函数
	 	if((TIM2CH3_CAPTURE_STA&0X80)==0)
	{	  
		if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) 
		{	    
			if(TIM2CH3_CAPTURE_STA&0X40)
			{
				if((TIM2CH3_CAPTURE_STA&0X3F)==0X3F)
				{
					TIM2CH3_CAPTURE_STA|=0X80;
					TIM2CH3_CAPTURE_VAL=0XFFFF;
				}else TIM2CH3_CAPTURE_STA++;    
			}	 
		}
	if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)
			{	
			if(TIM2CH3_CAPTURE_STA&0X40)	 		
			{	  			
				TIM2CH3_CAPTURE_STA|=0X80;		
				TIM2CH3_CAPTURE_VAL=TIM_GetCapture3(TIM2);
		   		TIM_OC3PolarityConfig(TIM2,TIM_ICPolarity_Rising); 
			}else  							
			{
				TIM2CH3_CAPTURE_STA=0;	
				TIM2CH3_CAPTURE_VAL=0;
	 			TIM_SetCounter(TIM2,0);        
				TIM2CH3_CAPTURE_STA|=0X40;	
		   	TIM_OC3PolarityConfig(TIM2,TIM_ICPolarity_Falling);		
			}		    
		}				
 	}
	
//清除中断标志位
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update); 
 		TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
		TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);  
		TIM_ClearITPendingBit(TIM2, TIM_IT_CC3); 	
}

以上函数写完之后就可以使用超声波模块了,但要用以下函数将高电平时间转换成距离

double HCSR_T2Length_1; //长度
double HCSR_T2_Length_1(void)
{
	
		u32 temp=0;
		delay_ms(10);
		
		if(TIM2CH1_CAPTURE_STA&0X80)//已经成功捕获到一次上升沿
		{
			temp=TIM2CH1_CAPTURE_STA&0X3F;
			temp*=65536;//溢出时间的总和(最大值65535)
			temp+=TIM2CH1_CAPTURE_VAL;//得到总的高电平时间us
			//距离公式为 长度=高电平时间*声速/2
			HCSR_T2Length_1 = temp*340/2000; //单位mm
			TIM2CH1_CAPTURE_STA=0;//开启下一次捕获
		}
		return HCSR_T2Length_1;
}
//超声二的距离换算函数
double HCSR_T2Length_2; 
double HCSR_T2_Length_2(void)
{
	
		u32 temp=0;
		delay_ms(10);
		
		if(TIM2CH2_CAPTURE_STA&0X80)
		{
			temp=TIM2CH2_CAPTURE_STA&0X3F;
			temp*=65536;
			temp+=TIM2CH2_CAPTURE_VAL;

			HCSR_T2Length_2 = temp*340/2000;
			TIM2CH2_CAPTURE_STA=0;
		}
		
		return HCSR_T2Length_2 ;//50Ϊ»úеÎó²î
}
//超声三的距离换算函数
double HCSR_T2Length_3;
double HCSR_T2_Length_3(void)
{
	
		u32 temp=0;
		delay_ms(10);
		
		if(TIM2CH3_CAPTURE_STA&0X80)//³É¹¦²¶»ñµ½ÁËÒ»´ÎÉÏÉýÑØ
		{
			temp=TIM2CH3_CAPTURE_STA&0X3F;
			temp*=65536;//Òç³öʱ¼ä×ܺͣ¨Òç³öÒ»´Î¼ÆÊý65536£©
			temp+=TIM2CH3_CAPTURE_VAL;//µÃµ½×ܵĸߵçƽʱ¼ä us
			//¹«Ê½Îª  ³¤¶È=¸ßµçƽʱ¼ä*ÉùËÙ/2
			HCSR_T2Length_3 = temp*340/2000; //µ¥Î»Îªmm
//			printf("HIGH:%d us\r\n",temp);//´òÓ¡×ܵĸߵãƽʱ¼ä
			TIM2CH3_CAPTURE_STA=0;//¿ªÆôÏÂÒ»´Î²¶»ñ
		}
		return HCSR_T2Length_3;//54Ϊ»úеÎó²î
}

最后时三个的开启信号(避免各个超声波之间干扰延时5ms开启下一个超声波)

void HCSR04_TIM2_StartMeasure_1(void)
{

  GPIO_SetBits(GPIOC,GPIO_Pin_0); 		 
  delay_us(20);		              //给20us的高电平时间
  GPIO_ResetBits(GPIOC,GPIO_Pin_0);
	delay_ms(5);

	
}
void HCSR04_TIM2_StartMeasure_2(void)
{	

	GPIO_SetBits(GPIOC,GPIO_Pin_13); 		
  delay_us(20);		               
  GPIO_ResetBits(GPIOC,GPIO_Pin_13);
	delay_ms(5);

	
}
void HCSR04_TIM2_StartMeasure_3(void)
{

	GPIO_SetBits(GPIOC,GPIO_Pin_12); 	
  delay_us(20);		                  
  GPIO_ResetBits(GPIOC,GPIO_Pin_12);
	delay_ms(5);
}

以下是主函数,可以用串口显示超声波返回的距离

int main(void)
{
	
	
	delay_init();    
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	 
	uart_init(115200);	 /
	LED_Init();     
	HCSR04_TIM2_Cap_Init(0XFFFF,72-1);	//以1us的时间计数
	HCSR04_TIM4_Cap_Init(0XFFFF,72-1);	//以1us的时间计数
	while(1)
		{
		
		  HCSR04_TIM2_StartMeasure_1();
			printf("Ultrasonic measure the length is TIM2_OC1 %f mm\r\n",HCSR_T2_Length_1());
			HCSR04_TIM2_StartMeasure_2();
			printf("Ultrasonic measure the length is TIM2_OC2 %f mm\r\n",HCSR_T2_Length_2());
			HCSR04_TIM2_StartMeasure_3();
			printf("Ultrasonic measure the length is TIM2_OC3 %f mm\r\n",HCSR_T2_Length_3());

			HCSR04_TIM4_StartMeasure_1();
		  printf("Ultrasonic measure the length is TIM4_OC1 %f mm\r\n",HCSR_T4_Length_1());

			HCSR04_TIM4_StartMeasure_2();
			printf("Ultrasonic measure the length is TIM4_OC2 %f mm\r\n",HCSR_T4_Length_2());

			HCSR04_TIM4_StartMeasure_3();
			printf("Ultrasonic measure the length is TIM4_OC3 %f mm\r\n\r\n",HCSR_T4_Length_3());
			delay_ms(1000);
		
   }
}

最后的其实代码最后不要放在一个函数内,这样容易出现各个超声波之间发生混乱!!!(我也不知道为甚麽,我猜是因为进入中断的时间发生了冲突,在中断函数里面反转极性不恰当导致),但分开写的话是等待一个一个测试完成之后才开启下一个,不会发生混乱的现象。

最后组装完成之后经过测试,对于长宽5cm的物体也就是大于超声波模块的宽度的物体,精度大约在2-1mm之内,但对于测小的物体,小于超声波模块的宽度的物体,误差会越来越大,差20-30 mm也是正常的,原因就是HC_SR_04超声波是两个大眼睛发射超声波,物体小于两个大眼睛的宽度时,只会返回一部分的超声波,会导致超声波强度不够,计算距离偏远。例如你用一个尺子和一只笔在同一个位置用超声波侧距离,用笔(竖直放)测的的距离大约比用尺子大2cm。不同的位置,误差尽不相同,不会太大,也就几厘米,但是在以mm为精度的话,2—3CM误差太大了。
这也是最艰难的地方,不同的物体返回的误差不同,这也就是说,你用不同的物体测得的距离是不同的。滤波算法是没用的,这是硬性误差。
最后我们是决定用小的物体,把每个地方的误差算出来,直接减去,然后分别设置了测大物体和小物体两种模式。大物体精度在3mm内,小物体大约在5mm左右。
如果哪位大佬有好的算法,还请评论一下,小弟感激不尽。
最后附上三套源码
1是单个的超声波输入捕获输入捕获超声波
2是六路超声波同时测距然后串口发送六路超声波
3是完全版在OLED屏幕上显示坐标OLED屏幕上显示坐标

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值