超声波模块-HC-SR04
超声波模块-HC-SR04
超声波模块是一种常用的测距工具,一般用在避障小车,水位监控系统中。
模块型号为HC-SR04
两个电源引脚和TRIG,ECHO引脚(这两个引脚分别接我们开发板的P2.0和P2.1)
工作原理
就是起初先给这两个引脚都拉低,然后让TRIG引脚拉高10微秒以上再拉低产生一个脉冲起始信号(程序中我们是拉高20微秒)。
起始信号一开启,TRIG引脚拉高的同时,超声波开始发射,ECHO引脚检测到反射回来的信号时,就会被拉低。
因为声速为340m/s。那么测出的距离就是(ECHO引脚高电平持续的时间*340m/s)/2。
中学学过用声波测海底深度,时间*声速=来回的距离,所以需要除以2才是真正的深度,超声波模块也一样。
该内容主要介绍了一种模块的工作原理及相关参数。具体如下:
- 触发信号要求:只需提供一个 10 微秒以上的脉冲触发信号。
- 模块内部动作:发出 8 个 40kHz 周期电平并检测回波。
- 回响信号输出条件:检测到回波信号时输出回响信号,且回响信号的脉冲宽度与所测距离成正比。
- 距离计算方式:可以通过发射信号到收到回响信号的时间间隔来计算距离,公式为 uS/58 = 厘米或者 uS/148 = 英寸,也可表示为距离 = 高电平时间 * 声速(340M/S)/2。
- 测量周期建议:建议测量周期为 60ms 以上,以防止发射信号对回响信号产生影响。
软件分析
计算ECHO引脚高电平的持续时间就用定时器的计数功能完成,如果定时器计数溢出,证明测的距离太远,超出模块的测距范围(最多4米),我们就在数码管上显示999表示距离太远。在测量范围内我们的数码管就显示测出的厘米数。
比如说定时器的计数为9216,那么高电平的持续时间就是9216*(12/11059200)=0.01s
测出的距离就是0.01*340/2=1.7m
数码管就显示170。
不过为了简化单片机的计算过程,我们可以这样算
“( (X*12)/11059200 )*340*100/2”就是厘米数,化简约为“X/54”,X就是定时器的计数值。
本讲代码需要用到“#include<intrins.h >”的“_nop_();”,这个表示延时1微秒左右,请参考《手把手教你学51单片机》文档14.2节后半段文字。
#include "stm32f10x.h" // Device header
#include "Delay.h" //使用的是b站江科大的延时函数,可以自己用别的替代延时功能
#include "OLED.h" //使用的是b站江科大的OLED驱动代码,用于展示测距结果,可以在相应的地方更换为串口通信展示到电脑的串口助手上
uint8_t flag=0; //用于记录中断信号是上升沿还是下降沿
uint32_t number=0; //记录定时器中断的次数
uint32_t times=0; //记录回响信号的持续时间
int main(void){
OLED_Init();
OLED_ShowString(1,1,"Hello World!!!");
//初始化GPIO口,Trig使用推挽输出,Echo使用浮空输入
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能GPIOA的外设时钟
GPIO_InitTypeDef itd;
itd.GPIO_Mode=GPIO_Mode_Out_PP; //选择推挽输出模式
itd.GPIO_Pin=GPIO_Pin_6; //选择GPIO_Pin_6
itd.GPIO_Speed=GPIO_Speed_50MHz; //默认选择50MHz
GPIO_Init(GPIOA,&itd);
itd.GPIO_Mode=GPIO_Mode_IN_FLOATING; //选择浮空输入模式
itd.GPIO_Pin=GPIO_Pin_7; //选择GPIO_Pin_7
GPIO_Init(GPIOA,&itd);
//AFIO映射中断引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能AFIO的外设时针
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1); //选择外部中断源和中断通道
//EXTI中断配置
EXTI_InitTypeDef itd1;
itd1.EXTI_Line=EXTI_Line7; //echo使用的端口7,因此选择7号中断线
itd1.EXTI_LineCmd=ENABLE;
itd1.EXTI_Mode=EXTI_Mode_Interrupt;
itd1.EXTI_Trigger=EXTI_Trigger_Rising_Falling; //上升沿和下降沿都触发中断
EXTI_Init(&itd1);
//NVIC分配外部中断的中断优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //指定中断分组
NVIC_InitTypeDef itd2;
itd2.NVIC_IRQChannel=EXTI9_5_IRQn; //使用的端口7,因此选择这个参数
itd2.NVIC_IRQChannelCmd=ENABLE;
itd2.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级
itd2.NVIC_IRQChannelSubPriority=2; //响应优先级
NVIC_Init(&itd2);
//配置定时器
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM_TimeBaseInitTypeDef itd3;
itd3.TIM_ClockDivision=TIM_CKD_DIV1; //使用时钟分频1
itd3.TIM_CounterMode=TIM_CounterMode_Up; //向上计数
//72MHz/72/100=1000,每秒定时器计数1000个,因此每个计数为100us
itd3.TIM_Period=72-1; //预分频系数
itd3.TIM_Prescaler=100-1; //自动重装器
itd3.TIM_RepetitionCounter=0; //该参数仅给高级定时器使用
TIM_TimeBaseInit(TIM2,&itd3);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //使能中断输出信号
TIM_InternalClockConfig(TIM2); //选择内部时钟
//NVIC分配定时器的中断优先级
NVIC_InitTypeDef itd4;
itd4.NVIC_IRQChannel=TIM2_IRQn; //指定Tim2的中断通道
itd4.NVIC_IRQChannelCmd=ENABLE;
itd4.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级
itd4.NVIC_IRQChannelSubPriority=1; //响应优先级
NVIC_Init(&itd4);
uint32_t distance;
while(1){
distance=0;
for(int i=0;i<10;++i){ //每次取10次测距数据,取平均值减少误差
GPIO_SetBits(GPIOA,GPIO_Pin_6);
Delay_us(15); //根据说明书,需要提供至少10us的高电平
GPIO_ResetBits(GPIOA,GPIO_Pin_6);
Delay_ms(65); //根据说明书,每个周期至少需要等待60ms
distance+=(times/5.8); //根据说明书提供的公式,获取单位为mm的距离
}
distance/=10;
OLED_ShowNum(2,1,distance,4);
}
}
//定时器中断函数
void TIM2_IRQHandler(void){
if(SET==TIM_GetITStatus(TIM2,TIM_FLAG_Update)){
number++; //每次中断将次数++
TIM_ClearITPendingBit(TIM2,TIM_FLAG_Update);
}
}
//外部中断函数
void EXTI9_5_IRQHandler(void){
if(SET==EXTI_GetITStatus(EXTI_Line7)){
if(flag==0){
//上升沿即回响电平开始,打开计数器
number=0;flag=1;
TIM_SetCounter(TIM2,0);
TIM_Cmd(TIM2,ENABLE);
}else{
//下降沿即回响电平结束,统计高电平持续时长
TIM_Cmd(TIM2,DISABLE);
flag=0;
times=number*100+TIM_GetCounter(TIM2); //得到回响的高电平持续的us
}
EXTI_ClearITPendingBit(EXTI_Line7);
}
}


浙公网安备 33010602011771号