一、超声波传感器介绍
1.1 测距原理
(1)采用IO口TRIG触发测距,给至少10us的高电平信号;
(2)模块自动发送8个40khz的方波,自动检测是否有信号返回;
(3)有信号返回,通过IO口ECHO输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间,发射点距离障碍物的距离S为:
S=v×∆t/2
其中,v为超声波在空气中的传播速度,∆t为计时器记录的测出发射和接收回波的时间差
1.2 超声波数据测量注意事项
超声波传感器缺点:
1、超声波传播速度易受温度和风向等环境因素干扰;
2、超声波很有可能被吸音材料(如毛毯、毛衣、多孔材料等)吸收,造成测距不稳定;
3、传感器可能受外部噪音(干扰源与传感器有同样的频率)或相同传感器干扰;
4、超声波传感器检测与自身斜角较大的物体可能出现检测不到的情况;
5、前方有平滑物体(如图书封面,镜面,墙体等)与传感器夹角大于45度(非垂直反射)时误差明显,且在5-40cm范围内读数不稳定.其中可能会出现的误差有三角误差、镜面反射、多次反射等。
实际测量要求:
测距时,被测物体面积不宜少于0.5平方米且尽量要求平整。
二、工程介绍
2.1 前期准备
先说一下,鉴于超声波传感器的影响因素过多,所以不建议超声波传感器作为主要距离探测工具,仅可作为辅助测量工具,另外,本工程并未考虑温度对超声波传感器的影响,所以这一部分内容请自行查阅。
工具:STM32F405(Timer+ I/O)、HC-SR04(4个)
2.2 工程详解
2.2.1 状态机
超声波按序号0-3排序,利用Sonar_ID进行不同的超声波操作,同时基于实际的使用情况,将超声波状态机分为以下5种状态,其中(RCVED_A 、RCVED_B 、RCVED_C 、RCVED_D )为同一种状态,只不过按超声波个数进行了区分。
#define START 1
#define WAIT 2
#define RCVED_A 3
#define RCVED_B 4
#define RCVED_C 5
#define RCVED_D 6
#define END_SINGLE 7
#define END_LOOP 8
START状态:发布Trig信号
WAIT状态:空闲状态
RCVED状态:中断中采集到超声波数据
END_SINGLE状态:完成单个超声波数据采集
END_LOOP状态:完成多路超声波采集循环
2.2.2 引脚定义:
#define TRIPA PCout(0) //A路超声波 PC0接触发引脚 PA0接回波引脚
#define TRIPB PCout(1) //B路超声波 PC1接触发引脚 PA1接回波引脚
#define TRIPC PCout(2) //C路超声波 PC2接触发引脚 PA2接回波引脚
#define TRIPD PCout(3) //D路超声波 PC3接触发引脚 PA3接回波引脚
2.2.3 Timer定时器及IO初始化
利用定时器的CC1、CC2、CC3、CC4及Update溢出中断,通过CC1、CC2、CC3、CC4进行捕获上升沿及下降沿中断,通过Update溢出中断进行距离时间计算,一定要注意该定时器中断的优先级,否则极易造成超声波数据长时间无返回值。
void TIM5_Cap_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM5_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能PORTA时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //使能PORTC时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE); //TIM5时钟使能
/****** ECOH 回响信号******/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; //GPIOA0
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(GPIOA,&GPIO_InitStructure); //初始化PA0
GPIO_PinAFConfig(GPIOA,GPIO_PinSource0,GPIO_AF_TIM5); //PA0复用位定时器5
GPIO_PinAFConfig(GPIOA,GPIO_PinSource1,GPIO_AF_TIM5); //PA1复用位定时器5
GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_TIM5); //PA2复用位定时器5
GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_TIM5); //PA3复用位定时器5
/****** TRIG 触发信号******/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHZ
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOC, &GPIO_InitStructure);
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(TIM5,&TIM_TimeBaseStructure);
//初始化TIM5输入捕获参数
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 选择输入端 IC1映射到TI1上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM5_ICInitStructure.TIM_ICFilter = 0x00; //IC1F=0000 配置输入滤波器 不滤波
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
//初始化TIM5输入捕获参数
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2; //CC1S=02 选择输入端 IC1映射到TI2上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI2上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM5_ICInitStructure.TIM_ICFilter = 0x00; //IC1F=0000 配置输入滤波器 不滤波
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
//初始化TIM5输入捕获参数
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_3; //CC1S=03 选择输入端 IC1映射到TI3上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI3上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM5_ICInitStructure.TIM_ICFilter = 0x00; //IC1F=0000 配置输入滤波器 不滤波
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
//初始化TIM5输入捕获参数
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_4; //CC1S=04 选择输入端 IC1映射到TI4上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI4上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM5_ICInitStructure.TIM_ICFilter = 0x00; //IC1F=0000 配置输入滤波器 不滤波
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE); //允许更新中断
TIM_ITConfig(TIM5,TIM_IT_CC1,ENABLE); //允许CC1IE捕获中断
TIM_ITConfig(TIM5,TIM_IT_CC2,ENABLE); //允许CC2IE捕获中断
TIM_ITConfig(TIM5,TIM_IT_CC3,ENABLE); //允许CC3IE捕获中断
TIM_ITConfig(TIM5,TIM_IT_CC4,ENABLE); //允许CC4IE捕获中断
TIM_Cmd(TIM5,ENABLE ); //使能定时器5
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; //抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
}
2.2.4 中断处理
全部代码贴出来太长了,贴一个ID的给解释一下,其他的照着写就行,最后别忘清除中断标志位。
if(Sonar_ID == 0)
{
if((TIM5CH1_CAPTURE_STA&0x80)==0) //还未成功捕获
{
if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET) //溢出
{
if(TIM5CH1_CAPTURE_STA&0x40) //已经捕获到高电平了
{
if((TIM5CH1_CAPTURE_STA&0x3F)>=0x0C) //高电平太长了
{
TIM5CH1_CAPTURE_STA|=0x80; //标记成功捕获了一次
TIM5CH1_CAPTURE_VAL=0x03E8;
Sonar_Status = RCVED_A;
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
}else
{
TIM5CH1_CAPTURE_STA++;
}
}
}
if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET) //捕获1发生捕获事件
{
if(TIM5CH1_CAPTURE_STA&0x40) //捕获到一个下降沿
{
TIM5CH1_CAPTURE_STA|=0x80; //标记成功捕获到一次高电平脉宽
TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5); //获取当前的捕获值.
Sonar_Status = RCVED_A;
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
}
else //还未开始,第一次捕获上升沿
{
TIM5CH1_CAPTURE_STA=0; //清空
TIM5CH1_CAPTURE_VAL=0;
TIM5CH1_CAPTURE_STA|=0x40; //标记捕获到了上升沿
TIM_Cmd(TIM5,DISABLE); //关闭定时器5
TIM_SetCounter(TIM5,0);
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); //CC1P=1 设置为下降沿捕获
TIM_Cmd(TIM5,ENABLE ); //使能定时器5
}
}
}
}
因为我的超声波状态机大体思路是按照Sonar_ID一路接一路进行采集,所以进中断时也是按照Sonar_ID进行了区分,首先,TIM5CH1_CAPTURE_STA为每路超声波传感器的标志位,主要标记捕获到下降沿,捕获到上升沿,以及溢出次数统计,在完成一次捕获后更新捕获完成bit位,所以多注意下这个参数的使用。
第一步:中断捕获(上升沿/下降沿)
if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET) //捕获1发生捕获事件
第二步:捕获到上升沿标记,同时各种标志位及计数清零,同时将定时器改为捕获下降沿
TIM5CH1_CAPTURE_STA|=0x40; //标记捕获到了上升沿
TIM5CH1_CAPTURE_STA=0; //清空
TIM5CH1_CAPTURE_VAL=0;
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); //CC1P=1 设置为下降沿捕获
第三步:捕获到下降沿标记,更新捕获完成标志位,同时将定时器更改为上升沿捕获
TIM5CH1_CAPTURE_STA|=0x80; //标记成功捕获到一次高电平脉宽
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
另外,我将定时器超时未接收到下降沿也判定为捕获完成,因为距离太远了,所以果断截断,同时将定时器更改为上升沿捕获
2.2.5 状态机处理
全部代码贴出来太长了,贴一个ID的给解释一下
void Sonar_State_Machine(uint8_t status)
{
if(Sonar_ID > 3)
Sonar_ID = 0;
switch(status)
{
case START:
if(Sonar_ID == 0)
{
TRIPA=1; //高电平触发
delay_us(20); //延时
TRIPA=0; //低电平
}
else
Sonar_ID = 0;
Sonar_Status = WAIT;
break;
case WAIT:
OverTime_Count++;
if(OverTime_Count > 2) //假设在等待时间内没有在中断内收到状态返回,则重发Trig
{
Sonar_Status = START;
ReSend_Flag++;
if(ReSend_Flag > 2) //重发Trig3次仍未在中断内收到状态反馈,则判定该超声波存在问题
{
/**可添加故障处理函数,可通过串口/CAN将异常传感器ID进行上报**/
Sonar_ID++;
Sonar_Status = START;
}
}
break;
case RCVED_A:
Distance_A=TIM5CH1_CAPTURE_STA&0X3F;
Distance_A*=1000; //溢出时间总和
Distance_A+=TIM5CH1_CAPTURE_VAL; //得到总的高电平时间
Distance_A=Distance_A*170/1000; //转化成mm为单位,依据声音在空气中传播速度340m/s
TIM5CH1_CAPTURE_STA=0; //开启下一次捕获
Sonar_Status = END_SINGLE;
break;
case END_SINGLE:
Sonar_ID++;
if(Sonar_ID >3)
Sonar_Status = END_LOOP;
case END_LOOP:
Sonar_ID =0;
Sonar_Status = START;
break;
default:
break;
}
}
START状态发布Trig信号,然后状态机转为WAIT,WAIT状态下进行计数统计,如果在超时时间内中断未接收到捕获完成的状态,则自动将该Sonar_ID的Trig信号重发,重发次数超过三次且未接收到数据时,则判定超声波异常,可以做一些异常处理函数,假设在中断中状态机状态变为RCVED_A,则进行这一路超声波的距离计算,计算完成后状态机状态变为END_SINGLE,在该状态记为这一路超声波完成计算,开始进行下一路超声波计算(Sonar_ID++),如果超声波Sonar_ID大于我们需要计算的路数,则将状态机状态变为END_LOOP,标记完成一次循环统计,Sonar_ID赋0,自动开启下一超声波距离检测循环。
以上仅为参考网上诸多资料完成的工程,但是实际测试效果还不错,给大家分享一下,如有错误欢迎指正。
源码地址:https://download.csdn.net/download/weixin_41591545/69503086