HC-SRF05超声波测距模块可提供2cm-400cm的非接触式距离感测功能,测距精度可达高到3mm;模块包括超声波发射器、接收器与控制电路。像智能小车的测距以及转向,或是一些项目中,常常会用到。智能小车测距可以及时发现前方的障碍物,使智能小车可以及时转向,避开障碍物。
原理:
1.给超声波模块接入电源和地;
2.给脉冲触发引脚(trig)输入一个长为20us的高电平方波;
3.输入方波后,模块会自动发射8个40KHz的声波,与此同时回波引脚(echo)端的电平会由0变为1;(此时应该启动定时器计时);
4.当超声波返回被模块接收到时,回波引 脚端的电平会由1变为0;(此时应该停止定时器计数),定时器记下的这个时间即为超声波由发射到返回的总时长;
5.根据声音在空气中的速度为344米/秒,即可计算出所测的距离。
时序图表明你只需要提供一个10us以上脉冲信号,该模块内部将发出8个40kHz周期电平并检测回波。一旦检测到有回波信号则输出回响信号。回响信号的脉冲宽度与所测的距离成正比。由此通过发射信号到收到的回响信号时间间隔可以计算得到距离。
上面的理论大可不必了解,这个东西无非就是通过声波测距,重点还是在代码上,搞了将近两周,踩了一些坑,和大家分享下:
1、5v的电压一定要接好,不然串口就会返回一个固定的值!
2、如果使用一般的延时来计时的话,配置好定时器就好了;但如果选择输入捕获来完成,注意配置时通道要对应好,否则串口会返回0或者很大的数。
3、如果使用不同定时器进行则分别配置,如使用同一个定时器的不同通道,则注意逻辑:不能接到上升/下降沿就关闭定时器,会相互影响。
4、注意定时器时32位还是16位,可能存在数据位数导致的错误。
我使用的是stm32f4,两个定时器,两路超声波,如果需要一个定时器稍稍改一下逻辑即可:
初始化定时器:(正点原子例程也有):
TIM_ICInitTypeDef TIM3_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//定时器3通道4,定时器9通道1输入捕获配置
void HC_Reci_TIM3_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_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //使能PORTB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//PB1定时器3通道4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉
GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化PB1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource1,GPIO_AF_TIM3); //PB1复用位定时器3
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
//初始化TIM3通道4输入捕获参数
TIM3_ICInitStructure.TIM_Channel = TIM_Channel_4; //CC1S=01 选择输入端 IC4映射到TI4上
TIM3_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM3_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM3_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM3_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
TIM_ICInit(TIM3, &TIM3_ICInitStructure);
//允许更新中断,触发方式中断
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
TIM_ITConfig(TIM3,TIM_IT_Trigger,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
TIM_ITConfig(TIM3,TIM_IT_Update|TIM_IT_CC4,ENABLE);//允许更新中断 ,允许CC1IE捕获中断
TIM_Cmd(TIM3,ENABLE ); //使能定时器3
}
输入捕获:
void TIM3_IRQHandler(void)//TIM3通道4
{
if((HC_1.sta&0X80)==0)//还未成功捕获
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)//溢出
{
Send_sound(GPIOB,GPIO_Pin_0);
if(HC_1.sta&0X40)//已经捕获到高电平了
{
if((HC_1.sta&0X3F)==0X3F)//高电平太长了
{
HC_1.sta|=0X80; //标记成功捕获了一次
HC_1.val=0XFFFF;
}else HC_1.sta++;
}
}
if(TIM_GetITStatus(TIM3, TIM_IT_CC4) != RESET)//捕获1发生捕获事件
{
if(HC_1.sta&0X40) //捕获到一个下降沿
{
HC_1.sta|=0X80; //标记成功捕获到一次高电平脉宽
HC_1.val=(u16)TIM_GetCapture4(TIM3);//获取当前的捕获值.
HC_1.temp=HC_1.sta&0X3F;
HC_1.temp*=0XFFFF; //溢出时间总和
HC_1.temp+=HC_1.val; //得到总的高电平时间
HC_1.Distance=HC_1.temp*172/10000;//cm
HC_1.sta=0; //开启下一次捕获
printf("HC1: %d\n",HC_1.Distance);
TIM_OC4PolarityConfig(TIM3,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
// Send_sound(GPIOB,GPIO_Pin_0);
}
else //还未开始,第一次捕获上升沿
{
HC_1.sta=0; //清空
HC_1.val=0;
HC_1.sta|=0X40; //标记捕获到了上升沿
TIM_Cmd(TIM3,DISABLE ); //关闭定时器3
TIM_SetCounter(TIM3,0);
TIM_OC4PolarityConfig(TIM3,TIM_ICPolarity_Falling); //CC1P=1 设置为下降沿捕获
TIM_Cmd(TIM3,ENABLE ); //使能定时器3
}
}
}
TIM_ClearITPendingBit(TIM3, TIM_IT_CC4|TIM_IT_Update);
}
发送高电平:
void Send_sound(GPIO_TypeDef* GPIOx,uint16_t Pinx)
{
GPIO_SetBits(GPIOx,Pinx);
delay_us(20);
GPIO_ResetBits(GPIOx,Pinx);
}
那么任务函数就很简洁了:
void sentry_ultrasonic ()
{
HC_ult_send();
HC_Reci_TIM3_Init(0XFFFF,84-1); //以1Mhz的频率计数 1us计数
HC_Reci_TIM9_Init(0XFFFF,84-1);
while(1)
{
task_delay_ms(50);
}
}
以上代码已经经过串口验证无误,超声波测距正常。搞了快两周的测距模块终于差不多了,尽管踩坑无数。
萌新初来报道,多多包涵,大家凑合看哈。拜谢支持。