这是一道学校出的电赛题目,要求在100*100cm的平面上实现定位实现声音定位。由于一米太大了,我们就做了40cm的,下面的讲解我按照40厘米的写。用到的处理器是stm32f103c8t6接下来分享一下调试心得。 硬件部分需要制作发声装置和接收装置,详细可以参考这个文章. 需要知道的是,扬声器发出的声音经过接收装置,得到的是一个方波信号,所以单片机需要根据这些方波求出距离
解题思路
一.直线
既然我们说,接收模块得到的是方波信号,那么单片机肯定可以检测到方波的下降沿和上升沿,在直线上,声源距离接收模块越近,声音先到达,就先接收到下降沿,相反,声源距离接收模块远,就后接受到下降沿,这样我们可以得到时间差。接下来就是小学数学了(手动狗头),根据两个时间可以算出距离。 用C语言描述就是这样
传入参数:两个下降沿分别到两个接收器之间的时间差
传出参数:距离
float Find_Line(float ltime)
{
float S=0,timeall=0,timefst=0;
timeall=0.0012;
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("方程无解");}
}
}
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)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin==GPIO_PIN_0)
{
if(time1==0)
{
wosignA=0;
HAL_TIM_Base_Start_IT(&htim2);
}
else
{
wosignA=time1+255*time2;
}
signA=1;
}
if(GPIO_Pin==GPIO_PIN_1)
{
if(time1==0)
{
wosignB=0;
HAL_TIM_Base_Start_IT(&htim2);
}
else
{
wosignB=time1+255*time2;
}
signB=1;
}
if(GPIO_Pin==GPIO_PIN_11)
{
if(time1==0)
{
wosignC=0;
HAL_TIM_Base_Start_IT(&htim2);
}
else
{
wosignC=time1+255*time2;
}
signC=1;
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == htim2.Instance)
{
time1++;
if(time1==255){time2++;time1=0;}
}
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;
}
}
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;}
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);
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引脚接收不到方波信号,拆了板子打电表,查不出问题,结果换了个引脚,好了,,,,,,还有驱动AD9833出正弦波,一会有一会没(官方代码),,,maybe买到了华强北单片机(笑哭),误人误事,,,, 欢迎指正
|