首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

HC-SR04超声波测距模块的原理介绍与代码实现

  • 25-03-06 17:02
  • 3470
  • 13062
blog.csdn.net

文章目录

    • 1、超声波模块介绍
          • 1)产品特点
          • 2)基本工作原理
          • 3)实物图
          • 4)电气参数
    • 2、超声波模块原理
          • 1)超声波时序图
          • 2)实现思路
    • 3、参考代码
          • 1)stm8基于TIM1的ch1输入捕获实现
          • 2)51实现测距并使用数码管显示(淘宝提供)

1、超声波模块介绍

超声波模块一般使用的都是HC-SR04来进行测距
在这里插入图片描述

1)产品特点

HC-SR04 超声波测距模块可提供 2cm-400cm 的非接触式距离感测功能,测
距精度可达高到的非接触式距离感测功能,测距精度可达高到 3mm ;模块包括超声波发射器、接收器与控制电路。

2)基本工作原理

(1)采用 IO 口 TRIG 触发测距,给最少 10us 的高电平信呈。
(2)模块自动发送 8 个 40khz 的方波,自动检测是否有信号返回;
(3)有信号返回,通过 IO 口 ECHO 输出一个高电平,高电平持续的时间就是超声
波从发射到返回的时间。测试距离=(高电平时间*声速(340M/S))/2;

3)实物图

在这里插入图片描述
如右图接线,

  • Vcc:+5V电源供电
  • Trig:输入触发信号(可以触发测距)
  • Echo:传出信号回响(可以传回时间差)
  • Gnd:接地
4)电气参数

在这里插入图片描述

2、超声波模块原理

1)超声波时序图

在这里插入图片描述
以上时序图表明你只需要提供一个 10uS 以上脉冲触发信号,该模块内部将发出 8 个 40kHz 周期电平并检测回波。一旦检测到有回波信号则输出回响信号 。回响信号的脉冲宽度与所测的距离成正比。由此通过发射信号到收到的回响信号时间间隔可以计算得到距离。公式:uS/58=厘米或者 uS/148=英寸;或是:距离=高电平时间*声速(340M/S)/2;建议测量周期为 60ms 以上,以防止发射信号对回响信号的影响。
注:
1 、此模块不宜带电连接,若要带电连接,则先让模块的 GND 端先连接,否则会影响模块的正常工作。
2 、测距时,被测物体的面积不少于 0.5 平方米且平面尽量要求平整,否则影响测量的结果

2)实现思路
  • 1.直接给trig高电平,然后读取ECHO引脚是否为高电平,若为高电平,则开启定时器,然后继续检测等待其为低电平的时候,获取计数器值,然后进行计算

  • 2.开启外部中断,先将ECHO配置上升沿中断,当中断来临的时候,在中断函数里面开启定时器,再将其配置为下降沿中断,等待下降沿中断来临的时候,获取计数器值。

其实上面的两种方法,其思路都是通过计算定时器的counter值,来计算距离

  • 3.定时器一路PWM控制触发以及触发周期,超声波返回信号高电平时间用定时器通道捕捉功能获取

定时器输入捕获的使用方法可以参考一下我其他的文章:TIM定时器使用介绍

3、参考代码

1)stm8基于TIM1的ch1输入捕获实现
// 关于超声波测距的宏定义
#define HCSR04_TRIG PC_ODR_ODR0  //PC0为TRIG,输出10us的高电平
#define HCSR04_ECHO PC_IDR_IDR1  //PC1为ECHO,输入一个脉冲信号
#define SYS_CLOCK   16000000     //定义系统当前fmaster频率值15797600UL。

//获取距离的函数
float Hcsr04_getdistance(void)
{
    u16 B_num = 0;  
    u32 Time = 0;
    float Distance = 0;
  
      HCSR04_TRIG = 0; 
//   printf("准备开始测试...\n");
 
    //   TIM1_CCR1H=0x00;//清除捕获/比较寄存器1高8位
 //   TIM1_CCR1L=0x00;//清除捕获/比较寄存器1低8位
    TIM1_SR1&=0xF9;//清除CC1IF标志位与CC2IF标志位
    TIM1_SR2&=0xF9;//清除CC1OF标志位与CC2OF标志位
    TIM1_CCER1|=0x11;//捕获功能使能
 //   printf("捕获功能开启,等待ECHO信号...\n");
   
     //TRIG给最少 10us 的高电平信呈    
      HCSR04_TRIG = 1;
      delay_10us(5);
      HCSR04_TRIG = 0;
   //   overflow_count = 0;
  //    printf("TRIG已发送 10uS 以上脉冲触发信号...\n");
      
     while((TIM1_SR1&0x02)==0);//等待捕获比较1标志位CC1IF变为“1”
  //   TIM1_CR1|=0x01;        //使能TIM1计数器功能“CEN=1”
  //   printf("上升沿信号捕获...\n");
     
    while((TIM1_SR1&0x04)==0);//等待捕获比较2标志位CC2IF变为“1”
 //   printf("下降沿信号捕获...\n");
     
    //取出数据CC2IF位就自动清0
    B_num=(u16)TIM1_CCR2H<<8;//取回捕获/比较寄存器2高8位
    B_num|=TIM1_CCR2L;//取回捕获/比较寄存器2低8位并与高8位拼合
 //   printf("B_num:%d\n",B_num);
   
   // TIM1_SR1&=0xFB;//清除CC2IF标志位
    
     Time = B_num*1000000/SYS_CLOCK; //脉冲长度单位为us
  //   printf("Time:%d\n",Time);
     
     Distance = B_num/16.05*0.017;
 //    printf("Distance:%f cm\n",Distance);
     


     TIM1_CCER1&=0xEE;//捕获功能禁止
    
     return Distance;
}


/****************************************************************/
//TIM1功能初始化函数TIM1_init(),无形参,无返回值
/****************************************************************/
void TIM1_init(void)
{
  //1.CC1通道被配置为输入,IC1映射在TI1FP1上“CC1S[1:0]=01”
  // 0x000000001 : CC1通道被配置为输入,IC1映射在TI1FP1上;
  TIM1_CCMR1|=0x01;
  
  //2.配置TI1FP1信号边沿极性为上升沿“CC1P=0”
  // 0x11111101 : 捕获发生在TI1F或TI2F的上升沿;
  TIM1_CCER1&=0xFD;
  
  //3.CC2通道被配置为输入,IC2映射在TI1FP2上“CC2S[1:0]=10”
  // 0x00000010 : CC2通道被配置为输入,IC1映射在TI2FP2上;
  TIM1_CCMR2|=0x02;
  
  //4.配置TI1FP2信号边沿极性为下降沿“CC2P=1”
  // 0x00100000 : 1:捕获发生在TI1F或TI2F的下降沿
  TIM1_CCER1|=0x20; 
  
  //5.配置触发输入信号为TI1FP1,“TS[2:0]=101”
  // 0x01010000 : 选择用于选择同步计数器的触发输入,滤波后的定时器输入1(TI1FP1)
  TIM1_SMCR|=0x50;
  
  //6.配置触发模式为复位触发,“SMS[2:0]=100”
  // 0x00000100 : 复位模式 – 在选中的触发输入(TRGI)的上升沿时重新初始化计数器,并且产生一个更新寄存器的信号
  TIM1_SMCR|=0x04;
  
  //7.使能TIM1计数器功能“CEN=1”
  TIM1_CR1|=0x01;
  
  //没有设置在外部触发寄存器(TIM1_ETR)中的采样频率
}

//初始化
void HCSR04_Init(void)
{
  //PC1为ECHO,PC0为TRIG
    //设置TRIG引脚为PC0,TRIG输出一个10us的高电平触发
    PC_DDR_DDR0 = 1;  
    PC_CR1_C10 = 1;   
    PC_CR2_C20 = 0;   
    
    //设置ECHO引脚为PC1,ECHO输入一个脉冲信号,需要用定时器测出持续时间
  
    PC_DDR_DDR1 = 0;  //设置为PC1为输入
    PC_CR1_C11 = 1;   //设置诶上拉输入
    PC_CR2_C21 = 0;   //带中断
   
   
 /*   
    PC_DDR_DDR1 = 0;  //设置为PD2为输入
    PC_CR1_C11 = 1;   //设置诶上拉输入
    PC_CR2_C21 = 1;   //带中断
  */
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
2)51实现测距并使用数码管显示(淘宝提供)
//超声波测距
//晶振=8M
//MCU=STC10F04XE
//P0.0-P0.6共阳数码管引脚
//Trig  = P1^0
//Echo  = P3^2
#include      //包括一个52标准内核的头文件
#define uchar unsigned char //定义一下方便使用
#define uint  unsigned int
#define ulong unsigned long
//***********************************************
sfr  CLK_DIV = 0x97; //为STC单片机定义,系统时钟分频
                     //为STC单片机的IO口设置地址定义
sfr   P0M1   = 0X93;
sfr   P0M0   = 0X94;
sfr   P1M1   = 0X91;
sfr   P1M0   = 0X92;
sfr	P2M1   = 0X95;
sfr	P2M0   = 0X96;
//***********************************************
sbit Trig  = P1^0; //产生脉冲引脚
sbit Echo  = P3^2; //回波引脚
sbit test  = P1^1; //测试用引脚

uchar code SEG7[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};//数码管0-9
uint distance[4];  //测距接收缓冲区
uchar ge,shi,bai,temp,flag,outcomeH,outcomeL,i;  //自定义寄存器
bit succeed_flag;  //测量成功标志
//********函数声明
void conversion(uint temp_data);
void delay_20us();
//void pai_xu();

void main(void)   // 主程序
{  uint distance_data,a,b;
   uchar CONT_1;   
   CLK_DIV=0X03; //系统时钟为1/8晶振(pdf-45页) 
     P0M1 = 0;   //将io口设置为推挽输出
     P1M1 = 0;
     P2M1 = 0;
     P0M0 = 0XFF;
     P1M0 = 0XFF;
     P2M0 = 0XFF;
   i=0;
   flag=0;
	test =0;
	Trig=0;       //首先拉低脉冲输入引脚
	TMOD=0x11;    //定时器0,定时器1,16位工作方式
	TR0=1;	     //启动定时器0
   IT0=0;        //由高电平变低电平,触发外部中断
	ET0=1;        //打开定时器0中断
 //ET1=1;        //打开定时器1中断
	EX0=0;        //关闭外部中断
	EA=1;         //打开总中断0	
  
	
while(1)         //程序循环
	{
  EA=0;
	     Trig=1;
        delay_20us();
        Trig=0;         //产生一个20us的脉冲,在Trig引脚  
        while(Echo==0); //等待Echo回波引脚变高电平
	     succeed_flag=0; //清测量成功标志
	     EX0=1;          //打开外部中断
	 	  TH1=0;          //定时器1清零
        TL1=0;          //定时器1清零
	     TF1=0;          //
        TR1=1;          //启动定时器1
   EA=1;

      while(TH1 < 30);//等待测量的结果,周期65.535毫秒(可用中断实现)  
		  TR1=0;          //关闭定时器1
        EX0=0;          //关闭外部中断

    if(succeed_flag==1)
	     { 	
		   distance_data=outcomeH;                //测量结果的高8位
           distance_data<<=8;                   //放入16位的高8位
		     distance_data=distance_data|outcomeL;//与低8位合并成为16位结果数据
            distance_data*=12;                  //因为定时器默认为12分频
           distance_data/=58;                   //微秒的单位除以58等于厘米
         }                                      //为什么除以58等于厘米,  Y米=(X秒*344)/2
			                                       // X秒=( 2*Y米)/344 ==》X秒=0.0058*Y米 ==》厘米=微秒/58 
    if(succeed_flag==0)
		   {
            distance_data=0;                    //没有回波则清零
		   	test = !test;                       //测试灯变化
           }

     ///       distance[i]=distance_data; //将测量结果的数据放入缓冲区
     ///        i++;
  	  ///	 if(i==3)
	  ///	     {
	  ///	       distance_data=(distance[0]+distance[1]+distance[2]+distance[3])/4;
     ///        pai_xu();
     ///        distance_data=distance[1];

      
	   a=distance_data;
       if(b==a) CONT_1=0;
       if(b!=a) CONT_1++;
       if(CONT_1>=3)
		   { CONT_1=0;
			  b=a;
			  conversion(b);
			}       
	  ///		 i=0;
 	  ///		}	     
	 }
}
//***************************************************************
//外部中断0,用做判断回波电平
INTO_()  interrupt 0   // 外部中断是0号
 {    
     outcomeH =TH1;    //取出定时器的值
     outcomeL =TL1;    //取出定时器的值
     succeed_flag=1;   //至成功测量的标志
     EX0=0;            //关闭外部中断
  }
//****************************************************************
//定时器0中断,用做显示
timer0() interrupt 1  // 定时器0中断是1号
   {
 	 TH0=0xfd; //写入定时器0初始值
	 TL0=0x77;	 	
	 switch(flag)   
      {case 0x00:P0=ge; P2=0xfd;flag++;break;
	    case 0x01:P0=shi;P2=0xfe;flag++;break;
	    case 0x02:P0=bai;P2=0xfb;flag=0;break;
      }
   }
//*****************************************************************
/*
//定时器1中断,用做超声波测距计时
timer1() interrupt 3  // 定时器0中断是1号
    {
TH1=0;
TL1=0;
     }
*/
//******************************************************************
//显示数据转换程序
void conversion(uint temp_data)  
 {  
    uchar ge_data,shi_data,bai_data ;
    bai_data=temp_data/100 ;
    temp_data=temp_data%100;   //取余运算
    shi_data=temp_data/10 ;
    temp_data=temp_data%10;   //取余运算
    ge_data=temp_data;

    bai_data=SEG7[bai_data];
    shi_data=SEG7[shi_data];
    ge_data =SEG7[ge_data];

    EA=0;
    bai = bai_data;
    shi = shi_data;
    ge  = ge_data ; 
	 EA=1;
 }
//******************************************************************
void delay_20us()
 {  uchar bt ;
    for(bt=0;bt<100;bt++);
 }
/*
void pai_xu()
  {  uint t;
  if (distance[0]>distance[1])
    {t=distance[0];distance[0]=distance[1];distance[1]=t;} /*交换值
  if(distance[0]>distance[2])
    {t=distance[2];distance[2]=distance[0];distance[0]=t;} /*交换值
  if(distance[1]>distance[2])
    {t=distance[1];distance[1]=distance[2];distance[2]=t;} /*交换值	 
    }
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
注:本文转载自blog.csdn.net的Clichong的文章"https://blog.csdn.net/weixin_44751294/article/details/111568926"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2491) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top