Skip to content

手把手教你玩转超声波传感器(原理+驱动)

我们都知道声音是由物体振动产生的,人能听到的频率在20Hz~20kHz。频率小于20Hz的叫次声波,频率大于20kHz的叫超声波。

超声波可以在空气、液体和固体中传播,可以被物体反射、折射、散射等,并且有频率高、波长短、绕射现象小、方向性好等特点。从而提供了丰富的用途,医学影像、清洁物品、材料检测、非接触测距等等。

本次我们要讲的超声波传感器就是非接触测距,用于测量物体与传感器之间的距离,可用于车辆安全(倒车雷达),安防系统(检测到移动物体并触发警报),工业自动化(物体定位、检测和避障)等等。

1. 源码下载及前置阅读

  • STM32F103C8T6模板工程

链接:https://pan.baidu.com/s/1n7XHCaMYtASWdJH2uA5yDA?pwd=lw59 提取码:lw59

  • 本文的源码

链接:https://pan.baidu.com/s/1VmBE0NfZgBJR4ouXIu7t8Q?pwd=4hry 提取码:4hry

如果你是嵌入式开发小白,那么建议你先读读下面几篇文章。

往期教程,有兴趣的小伙伴可以看看。

2. HC-SR04介绍

超声波传感器有很多的信号:HC-SR04、UC-025、UC-026、UC-015、US-100等等,它们之间大同小异,无非是工作参数有点不一样,像是工作的电压或温度、探测距离或精度有点差别,引脚是一样的,都是4个引脚(US-100 多一个 GND 引脚),引脚顺序和功能也是一样的。

大家在学习和工作中可以自行选择合适的型号,这里我为大家介绍最常见的 HC-SR04 这个型号。

2.1 HC-SR04型号介绍

现在市面上的 HC- SR04 有新版和旧版,我们介绍的是新版。新版性能比老版的精度更高,测距范围更远,可达6米,高于一般超声波测距模块。采用 CS-100A 超声波测距 SOC 芯片,高性能,工业级,宽电压,价格在4块钱左右。

2.2 HC-SR04工作参数及引脚介绍

HC-SR04 工作参数:

  • 探测距离:2~600cm
  • 探测精度:0.1cm±1%
  • 感应角度:<15°
  • 输出方式:GPIO
  • 工作电压:DC 3~5.5V
  • 工作电流:5.3mA
  • 工作温度:-40~85℃

接线如下:

HC-SR04STM32备注
VCC3.3/5V外接直流电源
Trig任意一个GPIO口输入端
Echo任意一个GPIO口输出端
GNDGND接地

3. HC-SR04工作原理

3.1 原理简述

超声波测距的工作原理其实很简单,传感器发送超声波,超声波碰到障碍物反弹回来,被传感器接收到。芯片算出发送和接收的时间间隔,再利用公式:s = v × t,看下面示意图,所以实际距离 = 测量距离 / 2 = 速度 × 时间 / 2。

顺便一提,超声波在空气中的传播速度大概是 343m/s,传播速度受到环境条件的影响,如温度、湿度和气压等。

超声波模块上的两个超声波探头,一个是发送端,负责发送超声波;一个是接收端,负责接收超声波。

3.2 原理详述

接下来我们详细的介绍下超声波模块的工作时序,明白了时序以后才知道怎么写代码。

  • 正常测距时序图:

  1. 单片机给超声波模块发送大于 10us 的高电平的触发信号;
  2. 超声波模块收到触发信号后 Trig 端发送 8个40kHz 的超声波脉冲;
  3. Echo 端由低电平转为高电平,同时开始发送超声波;
  4. 超声波模块检测到返回信号,Echo 端由高电平转为低电平;
  5. Echo 端高电平宽度即为超声波传播时间。

如果觉得太生涩了,我给大家准备了趣味描述:

  • 超出测距范围时序图:

当测量距离超过 HC-SR04 的测量范围时,Echo 任会输出高电平,宽度约为66ms,后转为低电平。

4. 编程实战

4.1 硬件接线

本教程使用的硬件如下:

  • 单片机:STM32F103C8T6

  • 超声波传感器:HC-SR04

  • 串口:USB 转 TTL

  • 烧录器:ST-LINK V2

HC-SR04STM32USB 转 TTL
VCC3.3/5V
TrigB6
EchoB7
GNDG
A10TX
A9RX
GGND

烧录的时候接线如下表,如果不会烧录的话可以看我之前的文章【STM32下载程序的五种方法】。

ST-Link V2STM32
SWCLKSWCLK
SWDIOSWDIO
GNDGND
3.3V3V3

接好如下图:

4.2 初始化引脚

我们将 Trig 引脚设置为推挽式输出,Echo 引脚设置为浮空输入。为什么这样设置呢?大家可以对照下表的 GPIO 的八种工作模式看看。

模式名称性质特征
浮空输入数字输入可读取引脚电平,若引脚悬空,则电平不确定
上拉输入数字输入可读取引脚电平,内部连接上拉电阻,悬空时默认高电平
下拉输入数字输入可读取引脚电平,内部连接下拉电阻,悬空时默认低电平
模拟输入模拟输入GPIO 无效,引脚直接接入内部 ADC
开漏输出数字输入可输出引脚电平,高电平为高阻态,低电平接 VSS
推挽输出数字输入可输出引脚电平,高电平接 VDD,低电平接 VSS
复用开漏输出数字输入由片上外设控制,高电平为高阻态,低电平接VSS
复用推挽输出数字输入由片上外设控制,高电平接VDD,低电平接VSS

引脚初始化代码如下:

c
 void HCSR04_GPIO_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    Trig_GPIO_CLK_ENABLE();                                 /* Trig引脚使能 */
    Echo_GPIO_CLK_ENABLE();                                 /* Echo引脚使能 */

    /* Trig低电平 */
    HAL_GPIO_WritePin(Trig_GPIO_PORT, Trig_GPIO_PIN, GPIO_PIN_RESET);

    GPIO_InitStruct.Pin = Trig_GPIO_PIN;                   /* Trig引脚 */
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */
    GPIO_InitStruct.Pull = GPIO_NOPULL;                    /* 浮空 */
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;           /* 低速 */
    HAL_GPIO_Init(Trig_GPIO_PORT, &GPIO_InitStruct);       /* 初始化Trig引脚 */

    GPIO_InitStruct.Pin = Echo_GPIO_PIN;                   /* Echo引脚 */
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;                /* 输入 */
    GPIO_InitStruct.Pull = GPIO_NOPULL;                    /* 浮空 */
    HAL_GPIO_Init(Echo_GPIO_PORT, &GPIO_InitStruct);       /* 初始化Echo引脚 */
}

4.3 初始化定时器

需要初始化一个定时器,用于测量 Echo 高电平宽度,这里我们初始化了通用定时器2。

c
void TIM2_Init(void)
{
    TIM_ClockConfigTypeDef sClockSourceConfig = {0};            /* 定时器设置结构体 */
    TIM_MasterConfigTypeDef sMasterConfig = {0};

    htim2.Instance = TIM2;                                      /* 通用定时器2 */
    htim2.Init.Prescaler = 71;                                  /* 预分频系数 */
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;                /* 递增计数模式 */
    htim2.Init.Period = 65535;                                  /* 自动装载值 */
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
    HAL_TIM_Base_Init(&htim2);

    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);

    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);
}

4.4 测量距离

按照超声波的工作时序,

  1. 单片机给超声波模块发送大于 10us 的高电平的触发信号;
  2. Trig 的 8个40kHz 的超声波脉冲,不用管,我们不需要;
  3. Echo 端由低电平转为高电平,开启定时器;
  4. 超声波模块检测到返回信号,Echo 端由高电平转为低电平,关闭定时器;
  5. 得到超声波来回的总距离,进行计算,得到实际测量距离。
c
void HCSR04_Get_Length (void)  
{  
    int total_time=0;           //超声波来回的总时间
    float distance=0;           //实际测量距离

    HCSR04_GPIO_init();
    HAL_GPIO_WritePin(Trig_GPIO_PORT,Trig_GPIO_PIN,GPIO_PIN_SET);           //拉高
    __HAL_TIM_SetCounter(&htim2, 0);                                        //定时器归零
    delay_us(15);
    HAL_GPIO_WritePin(Trig_GPIO_PORT,Trig_GPIO_PIN,GPIO_PIN_RESET);         //拉低

    while(HAL_GPIO_ReadPin(Echo_GPIO_PORT,Echo_GPIO_PIN)==GPIO_PIN_RESET);  //Echo转到高电平
    HAL_TIM_Base_Start(&htim2);                                             //启动定时器

    while(HAL_GPIO_ReadPin(Echo_GPIO_PORT,Echo_GPIO_PIN)==GPIO_PIN_SET);    //Echo转回低电平
    HAL_TIM_Base_Stop(&htim2);                                              //停止定时器

    total_time = __HAL_TIM_GetCounter(&htim2);                              //得到高电平持续时间

    distance = total_time * 0.01715; //                                     //算出测量距离(343*0.000001*100/2 = 0.01715)
    printf("dis : %.2f cm\r\n",distance);
}

有的同学可能会好奇,这个“ * 0.01715 ”是什么,因为

实际距离 = 测量距离 / 2

​ = 速度 × 总时间 / 2。

​ = 343(m/s) * total_time(us)/ 2

​ = 343(m/s) * total_time(us) * 0.000001(1s=1000000) * 100(1m=100cm)/2

​ = 343 * 0.000001 * 100 / 2

​ = 0.01715

所以我们就直接写 0.01715 啦,减轻一点计算负担,虽然本身也没多少。

.h文件内容如下:

c
#ifndef __HCSR04_H__
#define __HCSR04_H__

#include "stdio.h"
#include "stm32f1xx.h"

/* 引脚定义 */
#define Trig_GPIO_PORT                  GPIOB
#define Trig_GPIO_PIN                   GPIO_PIN_6
#define Trig_GPIO_CLK_ENABLE()          do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)             /* PB口时钟使能 */

#define Echo_GPIO_PORT                  GPIOB
#define Echo_GPIO_PIN                   GPIO_PIN_7
#define Echo_GPIO_CLK_ENABLE()          do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)             /* PB口时钟使能 */



void HCSR04_GPIO_init(void);
void TIM2_Init(void);
void HCSR04_Get_Length (void);

#endif

4.5 最终效果

串口输出如下,这是我用本子在超声波前来回移动的数据。记得给板子上电哦,光 STLink 供电可不够。

5. 小结

通过本文的学习与实践,相信大家已经了解并掌握 HC-SR04 的特性和使用,能够更好地应用于嵌入式开发。希望 HC-SR04 可以成为您的得力助手,让我们一起玩转 HC-SR04,peace and love!