基于STM32F103HAL库的声音定位系统:实现聆听世界


这是一道学校出的电赛题目,要求在100*100cm的平面上实现定位实现声音定位。由于一米太大了,我们就做了40cm的,下面的讲解我按照40厘米的写。用到的处理器是stm32f103c8t6接下来分享一下调试心得。
硬件部分需要制作发声装置和接收装置,详细可以参考这个文章.
需要知道的是,扬声器发出的声音经过接收装置,得到的是一个方波信号,所以单片机需要根据这些方波求出距离

解题思路

一.直线

既然我们说,接收模块得到的是方波信号,那么单片机肯定可以检测到方波的下降沿和上升沿,在直线上,声源距离接收模块越近,声音先到达,就先接收到下降沿,相反,声源距离接收模块远,就后接受到下降沿,这样我们可以得到时间差。接下来就是小学数学了(手动狗头),根据两个时间可以算出距离。请添加图片描述
用C语言描述就是这样

传入参数:两个下降沿分别到两个接收器之间的时间差
传出参数:距离
float Find_Line(float ltime)
{
	float S=0,timeall=0,timefst=0;
	timeall=0.0012;        //单位是秒,根据ltime单位做调整     0.4/346
	timefst=(timeall+ltime)/2;
	S=0.4*(timefst/timeall);
	
	return S;
}

二.平面

在平面上,就需要三个接收模块,这样就能得出两个时间差。
像下面这幅图
请添加图片描述

用C语言描述是这样,得到的arv1和arv2就是坐标值,
整个函数就是解方程的思路,用穷举法求出合适的值,我在后面又对得到的值做了求平均处理。

//传入参数:两个时间差
void Find_Square(float ctime1,float ctime2)
{
	int count,i=0;

   for(x=0.0; x<=40.0; x++)
    {
        for(y=0.0; y<=40.0;y++)
        {
            if(fabs(sqrt(x*x+(y-40)*(y-40))-sqrt(x*x +y*y)-34000*ctime1)<3 && fabs(sqrt(x*x +y*y)-sqrt((40-x)*(40-x)+y*y)-34000*ctime2)<3)  
             {  
                    a[i]=x;
                    b[i]=y;
                    i++;
                    count =i;
                printf("x=%.2f  y=%.2f count=%d\n",x,y,count);
             }  
            else{printf("方程无解");}
        }
    }
//		count=Del_Zero(a,count);
//		Del_Zero(b,count);
        for(int i=0; i< count; i++)
        {
            sum1=sum1+a[i];
            sum2=sum2+b[i];
            arv1=sum1/count;
            arv2=sum2/count;
						
             printf("a[i]=%.2f  b[i]=%.2f count=%d  %f\n",a[i],b[i],count,sum1);
        }
        printf("arv1=%.2f arv2=%.2f\n",arv1,arv2);
				sum1=0;sum2=0;
}

二.单片机获取时间

其实解题思路不难,难点就在于获取到准确的时间,我使用的获取方法是外部中断,当单片机的一个IO口检测到下降沿,定时器开始计时,另一个IO口检测到下降沿停止计时,这样就得到时间差

1.cubemx配置

1.外部中断


2.定时器
由于晶振是72MHZ,而且接收到下降沿的时间在微秒级别,所以将单位时间设置为100us(72000000/72/100=10000hz)(1/10000=100us)

/**
  * 函数功能: 按键外部中断回调函数
  * 输入参数: GPIO_Pin:中断引脚
  * 返 回 值: 无
  * 说    明: 无
  */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	
  if(GPIO_Pin==GPIO_PIN_0)//A
  {
		if(time1==0)
		{
			wosignA=0;
			HAL_TIM_Base_Start_IT(&htim2); //使能刚刚配置的定时器
		}
		else
		{
			wosignA=time1+255*time2;
			//HAL_TIM_Base_Stop_IT(&htim2);
		}
				signA=1; 
	}
		 if(GPIO_Pin==GPIO_PIN_1)//B
		{
			if(time1==0)
			{
				wosignB=0;
				HAL_TIM_Base_Start_IT(&htim2); //使能刚刚配置的定时器
			}
			else
			{
				wosignB=time1+255*time2;
				//HAL_TIM_Base_Stop_IT(&htim2);
			}
			signB=1;
			//EXTI->IMR &= ~(GPIO_PIN_1); 
		}
		 if(GPIO_Pin==GPIO_PIN_11)//B
		{
			if(time1==0)
			{
				wosignC=0;
				HAL_TIM_Base_Start_IT(&htim2); //使能刚刚配置的定时器
			}
			else
			{
				wosignC=time1+255*time2;
				//HAL_TIM_Base_Stop_IT(&htim2); //使能刚刚配置的定时器
			}
			signC=1;
			//EXTI->IMR &= ~(GPIO_PIN_1); 
		}

}
/**
  * 函数功能: 定时器中断回调函数
  * 输入参数: 
  * 返 回 值: 无
  * 说    明: 无
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{

		 if (htim->Instance == htim2.Instance)
		 {

					time1++;	 
					if(time1==255){time2++;time1=0;}//定时器计数255次溢出一次
		 }
		 if (htim->Instance == htim1.Instance)
		 {

		 }
}
		key=read_key();
		if(key==1) {mode=1;OLED_Clear();}
		if(key==2) {mode=2;OLED_Clear();}
		if(key==3) {mode=3;OLED_Clear();}
		if(mode==1)
		{
				if(signA&&signB)
			{
				EXTI->IMR &= ~(GPIO_PIN_0);//关闭外部中断
				EXTI->IMR &= ~(GPIO_PIN_1);
				HAL_TIM_Base_Stop_IT(&htim2);
				if(wosignA<wosignB){t1=wosignB;}
				else	{t1=wosignA;}
				if(t1>11){t1=11;}
				s=Find_Line(fabs((float)(t1))*100/1000000);
				printf("%d   %d    %f   %d   %d  %d %d\n",time1,time2,s,wosignA,wosignB,wosignC,t1);
				OLED_ShowFloat(0,0,s,2,4,16);
				HAL_Delay(100);
				EXTI->IMR |= GPIO_PIN_0; //开启外部中断
				EXTI->IMR |= GPIO_PIN_1;
				signA=0;signB=0;
				time1=0;time2=0;
			}
		}
//printf("%d   %d   %d    %d  %d  %d\n",signA,signB,signC,wosignC,wosignA,wosignB);
			if(mode==2)
			{
						if(signA&&signB&&signC)
					{
						EXTI->IMR &= ~(GPIO_PIN_0);
						EXTI->IMR &= ~(GPIO_PIN_1);
						EXTI->IMR &= ~(GPIO_PIN_11);
						HAL_TIM_Base_Stop_IT(&htim2);
						if((wosignA<wosignB)&&(wosignB<wosignC)){t2=wosignB;t3=wosignC;}
						if((wosignA<wosignC)&&(wosignB>wosignC)){t2=wosignC;t3=wosignB;}
						if((wosignB<wosignA)&&(wosignA<wosignC)){t2=wosignA;t3=wosignC;}
						if((wosignB<wosignC)&&(wosignC<wosignA)){t2=wosignC;t3=wosignA;}
						if((wosignC<wosignA)&&(wosignA<wosignB)){t2=wosignA;t3=wosignB;}
						if((wosignC<wosignB)&&(wosignB<wosignA)){t2=wosignB;t3=wosignA;}
						if(t2>11){t2=11;}
						if(t3>11){t3=11;}
//						t2=wosignA-wosignB;
//						t3=wosignB-wosignC;
						printf("%d   %d    %f   %d   %d  %d %d %d\n",time1,time2,s,wosignA,wosignB,wosignC,t2,t3);
						Find_Square((float)t2/10000,(float)t3/10000);
						OLED_ShowFloat(0,0,arv1,2,4,16);
						OLED_ShowFloat(0,2,arv2,2,4,16);
						//HAL_Delay(100);
						EXTI->IMR |= GPIO_PIN_0; 
						EXTI->IMR |= GPIO_PIN_1;
						EXTI->IMR |= GPIO_PIN_11;
						signA=0;signB=0;signC=0;
						time1=0;time2=0;
					}
				}
			if(mode==3){goto MENU;}
			}

可以用自己的按键,设置标志位做两个模式,完成持续监测。

成品的误差稍微有些大,不乏温度,湿度,以及硬件误差,和单片机检测误差,没有滤波等等,总的来说,大体方向是对的,我们的作品还能进一步完善。
在调试的时候,单片机PB10引脚接收不到方波信号,拆了板子打电表,查不出问题,结果换了个引脚,好了,,,,,

物联沃分享整理
物联沃-IOTWORD物联网 » 基于STM32F103HAL库的声音定位系统:实现聆听世界

发表评论