2025年5月11日更新:新增标准库代码,添加初始化修改引脚直接可以使用

2025年6月7日第二次更新:新增定时器方案,获取距离不再堵塞,不用卡程序,用定时器状态机的方式(推荐)(旧的不删,新的是在原来的基础上弄出来的,看懂原来的才好看懂新的)

前言

在51单片机和32的标准库的时候使用过这个模块,但是到后面的时候,太久不用忘记怎末使用了

然后超声波模块测距的原理,好像也没有那么简单理解,为什么高脉冲的持续时间,就是从发送到接收的时间呢?

然后在HAL库又怎末使用呢?

本篇文章提供.c.  h代码,只需要修改.h文件的TR和HC引脚,以及配置的10us的定时器中断TIM编号,就可以使用了

然后不用外部中断,只需要一个定时器中断就可以实现超声波测距的功能了

超声波测距模块简介

这个是我从产品说明书中获取的简介

超声波测距模块驱动过程

1.初始化

#define HC_TR_TRIG_PIN   GPIO_PIN_0
#define HC_TR_TRIG_PORT  GPIOA
#define HC_TR_ECHO_PIN   GPIO_PIN_1
#define HC_TR_ECHO_PORT  GPIOA
#define HC_TR_TIME  htim2

 只需要在Cubemx配置定时器中断,时间为10us就欧克

记得开启定时器中断,我这里是以主频为80Hz,PA0为TR,PA1为HC为例

2.脉冲发射

        给TR口一个至少10us的高电平,就会开始发射8个40Hz的脉冲

        因为是HAL库,HAL_Delay()是以1ms为单位的,直接给1ms就好

void TR_SEND()
{
    HAL_GPIO_WritePin(HC_TR_TRIG_PORT, HC_TR_TRIG_PIN, GPIO_PIN_SET);
   HAL_Delay(1); 
    HAL_GPIO_WritePin(HC_TR_TRIG_PORT, HC_TR_TRIG_PIN, GPIO_PIN_RESET); 
    GetEchoTime(2); 
}

3.脉冲接收过程

        

根据这张图可以发现,发送完后HC还是低电平,所以我们要等到高电平

然后高电平的持续时间,就是从发送到接收的时长,我也想不明白,为什么会是这个时间

可能是发送芯片发送完后

要给接收芯片一个已发送收完的信号

接收芯片在收到发送完的信号后,才开始设置为高电平

直到接收到8个40KHz的脉冲后,才变成低电平

所以单片机在发送完后的一小段时间里面,HC还是低电平的,然后才变高,最后变低

高电平持续时间,就是发送和接收的时长

代码就在下面

其中计算的公式是

float distance = (float)GetEchoTime(0)/100000*340/2*100; 

因为我定时器的时间为10us

假设期间经过了x个10us,那么就是

Time=x*10 us

=10x us

=10x /1000 ms

=10x /1000 000 s

=x/ 100 000s

然后路程等于速度乘时间

x=s*t= 340m/s  *  x/100 000s  (m)

因为路程是距离的两倍

所以

实际测距得的距离为 

L=x/2 =   340m/s  *  x/100 000s  /2(m)

转化成cm,厘米为单位,那么就是乘100

L=x/2 =   340m/s  *  x/100 000s  /2 *100(cm)

所以简化就是

L=x*170/1000(cm)

L=x*0.17 (cm)

如果发现数值错乱,那就是供电不稳的原因

历程源码-直接复制改.h文件引脚就可以使用了

记得要调用初始化函数

HC_TR.c

#include "HC_TR.h"
#include "tim.h"
#include "gpio.h"
#include "usart.h"
#include "stdio.h"
#include "string.h"

void TR_SE_init()
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
 __HAL_RCC_GPIOB_CLK_ENABLE();
	__HAL_RCC_GPIOC_CLK_ENABLE();
	__HAL_RCC_GPIOD_CLK_ENABLE();
	__HAL_RCC_GPIOE_CLK_ENABLE();



  /*Configure GPIO pin : PA0 */
  GPIO_InitStruct.Pin = HC_TR_TRIG_PIN ;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init( HC_TR_TRIG_PORT, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = HC_TR_ECHO_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(HC_TR_ECHO_PORT, &GPIO_InitStruct);
  
  HAL_TIM_Base_Start_IT(&HC_TR_TIME);

  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
}
void TR_SEND()
{
    HAL_GPIO_WritePin(HC_TR_TRIG_PORT, HC_TR_TRIG_PIN, GPIO_PIN_SET);
   HAL_Delay(1); 
    HAL_GPIO_WritePin(HC_TR_TRIG_PORT, HC_TR_TRIG_PIN, GPIO_PIN_RESET); 
    GetEchoTime(2); 
}

uint32_t GetEchoTime(uint8_t cmd)
{
    static uint32_t echo_time = 0;
    
    switch (cmd)
    {
    case 0:
        return echo_time;
    case 1:
        echo_time ++;
        break;
    case 2:
        echo_time = 0;
        break;
    }
}

// 获取距离函数
float HC_TR_GetDistance(void)
{
    // 发送触发信号
   TR_SEND();
    GetEchoTime(2);
        while(HAL_GPIO_ReadPin(HC_TR_ECHO_PORT, HC_TR_ECHO_PIN)==0)
        {
          if(GetEchoTime(0) > 1000)
            {
                printf("wate time \r\n");
                break;  
            }
        }
//超时检测
    GetEchoTime(2);
        while (HAL_GPIO_ReadPin(HC_TR_ECHO_PORT, HC_TR_ECHO_PIN))
        {
            if(GetEchoTime(0) > 1000)
            {
                printf("one time \r\n");
                break;  
            }
      }
    //超时检测
    //float distance = (float)GetEchoTime(0)/100000*340/2*100; 
    float distance = (float)GetEchoTime(0)*0.17; 
   // printf("distance= %.1fcm\r\n",distance);
        
    return distance;
}

// 定时器2中断处理函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim==&HC_TR_TIME)
    {
        GetEchoTime(1);
    }
}

HC_TR.h

#ifndef HC_TR_H
#define HC_TR_H

#include "main.h"

// 定义超声波传感器的引脚
#define HC_TR_TRIG_PIN   GPIO_PIN_0
#define HC_TR_TRIG_PORT  GPIOA
#define HC_TR_ECHO_PIN   GPIO_PIN_1
#define HC_TR_ECHO_PORT  GPIOA
#define HC_TR_TIME  htim2
void TR_SE_init();
float HC_TR_GetDistance(void);
void TR_SEND();
uint32_t GetEchoTime(uint8_t cmd);
#endif /* HC_TR_H */

2025年5月11日更新内容

标准库

HC_TR.c

#include "stm32f10x.h"                  // Device header
#include "HC_TR.h"
#include "Delay.h"
void Timer_Init(void)//要修改成自己对应的引脚
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM2, ENABLE);
}

void TR_SE_init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_Initstructure;
	GPIO_Initstructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Initstructure.GPIO_Pin = HC_TR_TRIG_PIN;
	GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(HC_TR_TRIG_PORT,&GPIO_Initstructure);
	
	GPIO_Initstructure.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_Initstructure.GPIO_Pin = HC_TR_ECHO_PIN;
	GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(HC_TR_ECHO_PORT,&GPIO_Initstructure);
	Timer_Init();
	
}

void TR_SEND()
{
   GPIO_WriteBit(HC_TR_TRIG_PORT, HC_TR_TRIG_PIN, 1);
   Delay_us(100); 
    GPIO_WriteBit(HC_TR_TRIG_PORT, HC_TR_TRIG_PIN, 0); 
    GetEchoTime(2); 
}
 
uint32_t GetEchoTime(uint8_t cmd)
{
    static uint32_t echo_time = 0;
    
    switch (cmd)
    {
    case 0:
        return echo_time;
    case 1:
        echo_time ++;
        break;
    case 2:
        echo_time = 0;
        break;
    }
}
 
// 获取距离函数
float HC_TR_GetDistance(void)
{
    // 发送触发信号
   TR_SEND();
    GetEchoTime(2);
        while(GPIO_ReadInputDataBit(HC_TR_ECHO_PORT, HC_TR_ECHO_PIN)==0)
        {
          if(GetEchoTime(0) > 1000)
            {
                break;  
            }
        }
//超时检测
    GetEchoTime(2);
        while (GPIO_ReadInputDataBit(HC_TR_ECHO_PORT, HC_TR_ECHO_PIN))
        {
            if(GetEchoTime(0) > 1000)
            {
                break;  
            }
      }
    //超时检测
    //float distance = (float)GetEchoTime(0)/100000*340/2*100; 
    float distance = (float)GetEchoTime(0)*0.17; 
   // printf("distance= %.1fcm\r\n",distance);
        
    return distance;
}
 
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		  GetEchoTime(1);
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

HC_TR.h

#ifndef HC_TR_H
#define HC_TR_H
#include "stm32f10x.h"   
#define HC_TR_TRIG_PIN   1<<0
#define HC_TR_TRIG_PORT  GPIOA      //我这里是PA0
#define HC_TR_ECHO_PIN   1<<1
#define HC_TR_ECHO_PORT  GPIOA      //PA1

void Timer_Init(void);
void TR_SE_init();
float HC_TR_GetDistance(void);
void TR_SEND();
uint32_t GetEchoTime(uint8_t cmd);

#endif

2025年6月7日第二次更新

用法一样,都是初始化然后直接调用

不过函数有些小变化,下面讲用法

修改引脚编号和定时器编号

核心就是创建一个10us的定时器中断,然后把这个函数放到中断

然后初始化,直接调用就可以了

如图,其实可以返回小数,因为是浮点型

hc_tr.c

#include "HC_TR.h"
#include "tim.h"
#include "gpio.h"
#include <stdint.h>
 
void TR_SE_init()
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
 
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
 __HAL_RCC_GPIOB_CLK_ENABLE();
	__HAL_RCC_GPIOC_CLK_ENABLE();
	__HAL_RCC_GPIOD_CLK_ENABLE();
	__HAL_RCC_GPIOE_CLK_ENABLE();
	
  GPIO_InitStruct.Pin = HC_TR_TRIG_PIN ;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init( HC_TR_TRIG_PORT, &GPIO_InitStruct);
 
  GPIO_InitStruct.Pin = HC_TR_ECHO_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(HC_TR_ECHO_PORT, &GPIO_InitStruct);
    TR_station(0);
 HAL_TIM_Base_Start_IT(HC_TR_TIM);
}
 
uint32_t GetEchoTime(uint8_t cmd)
{
    static uint32_t echo_time = 0;
    
    switch (cmd)
    {
    case 0:
        return echo_time;
    case 1:
        echo_time ++;
        break;
    case 2:
        echo_time = 0;
        break;
    }
}

float distance_change(uint8_t cmd,float distance)
{
    static float distance1=0;
    if(cmd==0)
    {
        return distance1;
    }
    else if(cmd==1)
    {
        distance1=distance*0.17; 
    }
}

float distance_return()
{
    float distance = distance_change(0,0);
}
 
void HC_TR_TIME()
{
    static  uint32_t time=0;
    static  uint8_t flag=0;

    switch (flag)
    {
    case 0:
    {
        GetEchoTime(2);
        TR_station(1);
        flag=1; 
        break;
    }
    
    case 1:
    {
        if(GetEchoTime(0)>50)
        flag=2;
        break;
    }
    
    case 2:
    {
       GetEchoTime(2);
            TR_station(0);
        flag=3;
        break;
    }

    case 3:
    {
         if(EC_station_read())
        {
            GetEchoTime(2);
            flag=4; 
        }
        if(GetEchoTime(0)>10000)
        {
            flag=0;
        }
        break;
    }

    case 4:
    {
       if(!EC_station_read())
        {
            flag=5; 
        }
        if(GetEchoTime(0)>10000)
        {
            flag=0;
        }
        break;
    }

    case 5:
    {
         distance_change(1,GetEchoTime(0));
        flag=0;
       break;
    }
  
}
 GetEchoTime(1);
}
 

hc_tr.h

#ifndef HC_TR_H
#define HC_TR_H
 
#include "main.h"
 
// 定义超声波传感器的引脚
#define HC_TR_TRIG_PIN   1<<2
#define HC_TR_TRIG_PORT  GPIOE
#define HC_TR_ECHO_PIN   1<<3
#define HC_TR_ECHO_PORT  GPIOE
#define HC_TR_TIM  &htim2

#define TR_station(x)     HAL_GPIO_WritePin(HC_TR_TRIG_PORT, HC_TR_TRIG_PIN, x)
#define EC_station_read()   HAL_GPIO_ReadPin(HC_TR_ECHO_PORT, HC_TR_ECHO_PIN)


void TR_SE_init();
float distance_return();
uint32_t GetEchoTime(uint8_t cmd);
void HC_TR_TIME();
float distance_change(uint8_t cmd,float distance);
#endif /* HC_TR_H */

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐