STM32
直播中

斗地主之神

8年用户 804经验值
擅长:嵌入式技术
私信 关注
[问答]

如何通过HC-SR04超声波模块去实现测距功能呢

HC-SR04超声波模块是什么?
如何通过HC-SR04超声波模块去实现测距功能呢?

回帖(1)

李红

2021-12-13 10:22:46
1.所需硬件

正点原子战舰V3开发板 X1
HC-SR04超声波模块 X1
杜邦线 若干
2.HC-SR04超声波模块介绍

上图为HC-SR04超声波模块实物图,从图中可以看到,该模块一共有四个引脚,分别是VCC、GND、Trig、Echo,关于该模块这里不做过多的详细介绍,感兴趣的同学可以自行百度了解,下面将简单介绍该模块的原理及使用方法。根据时序图可知,首先给TRIG引脚至少10us的高电平信号触发测距,模块自动发送8个40KHz的方波,自动检测是否有信号返回,若有信号返回,通过Echo引脚输出一个高电平,高电平的持续时间就是超声波从发射到返回的时间,

时序图如下





该模块使用较简单,难点在于程序的编写。
3.硬件连接

[tr]Vcc3.3/5V[/tr]
GNDGND
TrigPB5
EchoPB6
4.程序编写思路是

1、配置好使用到的GPIO以及定时器;
2、给模块TRIG端口发送大于10us的高电平信号,当收、收到ECHO回响信号是,打开定时器开始定时;
3、当回响信号消失,关闭定时器;
4、通过定时器定时时间来确定距离。
5.程序代码

HCSR04.c源文件

#include "hcsr04.h"
#include "sys.h"
#include "delay.h"


#define HCSR04_PORT     GPIOB
#define HCSR04_CLK      RCC_APB2Periph_GPIOB
#define HCSR04_TRIG     GPIO_Pin_5
#define HCSR04_ECHO     GPIO_Pin_6

#define TRIG_Send  PBout(5)
#define ECHO_Reci  PBin(6)


void Delay_Ms(uint16_t time);
void Delay_Us(uint16_t time);
void hcsr04_NVIC();




u16 msHcCount = 0;      //ms计数




void Hcsr04Init()
{  
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;     //生成用于定时器设置的结构体
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(HCSR04_CLK, ENABLE);
     
        //IO初始化
    GPIO_InitStructure.GPIO_Pin =HCSR04_TRIG;           //发送电平引脚
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    //推挽输出
    GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);
    GPIO_ResetBits(HCSR04_PORT,HCSR04_TRIG);
     
    GPIO_InitStructure.GPIO_Pin =   HCSR04_ECHO;        //返回电平引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   //浮空输入
    GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);  
          GPIO_ResetBits(HCSR04_PORT,HCSR04_ECHO);       

                //定时器初始化 使用基本定时器TIM6
          RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);   //使能对应RCC时钟
                //配置定时器基础结构体
                TIM_DeInit(TIM2);
                TIM_TimeBaseStructure.TIM_Period = (1000-1);      //设置在下一个更新事件装入活动的自动重装载寄存器周期的值         计数到1000为1ms
                TIM_TimeBaseStructure.TIM_Prescaler =(72-1);      //设置用来作为TIMx时钟频率除数的预分频值  1M的计数频率 1US计数
                TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;         //不分频
                TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;   //TIM向上计数模式
                TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);   //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位                 
               
                TIM_ClearFlag(TIM6, TIM_FLAG_Update);       //清除更新中断,免得一打开中断立即产生中断
                TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);    //打开定时器更新中断
                hcsr04_NVIC();
                TIM_Cmd(TIM6,DISABLE);     
}


//tips:static函数的作用域仅限于定义它的源文件内,所以不需要在头文件里声明
static void OpenTimerForHc()        //打开定时器
{
        TIM_SetCounter(TIM6,0);  //清除计数
        msHcCount = 0;
        TIM_Cmd(TIM6, ENABLE);   //使能TIMx外设
}

static void CloseTimerForHc()        //关闭定时器
{
        TIM_Cmd(TIM6, DISABLE);  //使能TIMx外设
}


//NVIC配置
void hcsr04_NVIC()
{
        NVIC_InitTypeDef NVIC_InitStructure;
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);


        NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;             //选择串口1中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;   //抢占式中断优先级设置为1
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;          //响应式中断优先级设置为1
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //使能中断
        NVIC_Init(&NVIC_InitStructure);
}


//定时器6中断服务程序
void TIM6_IRQHandler(void)   //TIM3中断
{
        if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
        {
                        TIM_ClearITPendingBit(TIM6, TIM_IT_Update  );   //清除TIMx更新中断标志
                        msHcCount++;
        }
}


//获取定时器时间
u32 GetEchoTimer(void)
{
        u32 t = 0;
        t = msHcCount*1000;    //得到MS
        t += TIM_GetCounter(TIM6);    //得到US
          TIM6->CNT = 0;     //将TIM2计数寄存器的计数值清零
                        Delay_Ms(50);
        return t;
}


//一次获取超声波测距数据 两次测距之间需要相隔一段时间,隔断回响信号
//为了消除余震的影响,取五次数据的平均值进行加权滤波。
float Hcsr04GetLength(void )
{
        u32 t = 0;
        int i = 0;
        float lengthTemp = 0;
        float sum = 0;
        while(i!=5)
        {
        TRIG_Send = 1;      //发送口高电平输出
        Delay_Us(20);
        TRIG_Send = 0;
        while(ECHO_Reci == 0);      //等待接收口高电平输出
                OpenTimerForHc();        //打开定时器
                i = i + 1;
                while(ECHO_Reci == 1);
                CloseTimerForHc();        //关闭定时器
                t = GetEchoTimer();        //获取时间,分辨率为1US
                lengthTemp = ((float)t/58.0);//cm
                sum = lengthTemp + sum ;
       
        }
        lengthTemp = sum/5.0;
        return lengthTemp;
}


/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
** 函数名称: Delay_Ms_Ms
** 功能描述: 延时1MS (可通过仿真来判断他的准确度)                       
** 参数描述:time (ms) 注意time<65535
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
void Delay_Ms(uint16_t time)  //延时函数
{
        uint16_t i,j;
        for(i=0;i                   for(j=0;j<10260;j++);
}
/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
** 函数名称: Delay_Ms_Us
** 功能描述: 延时1us (可通过仿真来判断他的准确度)
** 参数描述:time (us) 注意time<65535                                 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
void Delay_Us(uint16_t time)  //延时函数
{
        uint16_t i,j;
        for(i=0;i                   for(j=0;j<9;j++);
}
main.c源文件

#include "hcsr04.h"
#include "sys.h"
#include "delay.h"


#define HCSR04_PORT     GPIOB
#define HCSR04_CLK      RCC_APB2Periph_GPIOB
#define HCSR04_TRIG     GPIO_Pin_5
#define HCSR04_ECHO     GPIO_Pin_6

#define TRIG_Send  PBout(5)
#define ECHO_Reci  PBin(6)


void Delay_Ms(uint16_t time);
void Delay_Us(uint16_t time);
void hcsr04_NVIC();




u16 msHcCount = 0;      //ms计数




void Hcsr04Init()
{  
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;     //生成用于定时器设置的结构体
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(HCSR04_CLK, ENABLE);
     
        //IO初始化
    GPIO_InitStructure.GPIO_Pin =HCSR04_TRIG;           //发送电平引脚
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    //推挽输出
    GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);
    GPIO_ResetBits(HCSR04_PORT,HCSR04_TRIG);
     
    GPIO_InitStructure.GPIO_Pin =   HCSR04_ECHO;        //返回电平引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   //浮空输入
    GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);  
          GPIO_ResetBits(HCSR04_PORT,HCSR04_ECHO);       

                //定时器初始化 使用基本定时器TIM6
          RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);   //使能对应RCC时钟
                //配置定时器基础结构体
                TIM_DeInit(TIM2);
                TIM_TimeBaseStructure.TIM_Period = (1000-1);      //设置在下一个更新事件装入活动的自动重装载寄存器周期的值         计数到1000为1ms
                TIM_TimeBaseStructure.TIM_Prescaler =(72-1);      //设置用来作为TIMx时钟频率除数的预分频值  1M的计数频率 1US计数
                TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;         //不分频
                TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;   //TIM向上计数模式
                TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);   //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位                 
               
                TIM_ClearFlag(TIM6, TIM_FLAG_Update);       //清除更新中断,免得一打开中断立即产生中断
                TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);    //打开定时器更新中断
                hcsr04_NVIC();
                TIM_Cmd(TIM6,DISABLE);     
}


//tips:static函数的作用域仅限于定义它的源文件内,所以不需要在头文件里声明
static void OpenTimerForHc()        //打开定时器
{
        TIM_SetCounter(TIM6,0);  //清除计数
        msHcCount = 0;
        TIM_Cmd(TIM6, ENABLE);   //使能TIMx外设
}

static void CloseTimerForHc()        //关闭定时器
{
        TIM_Cmd(TIM6, DISABLE);  //使能TIMx外设
}


//NVIC配置
void hcsr04_NVIC()
{
        NVIC_InitTypeDef NVIC_InitStructure;
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);


        NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;             //选择串口1中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;   //抢占式中断优先级设置为1
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;          //响应式中断优先级设置为1
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //使能中断
        NVIC_Init(&NVIC_InitStructure);
}


//定时器6中断服务程序
void TIM6_IRQHandler(void)   //TIM3中断
{
        if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
        {
                        TIM_ClearITPendingBit(TIM6, TIM_IT_Update  );   //清除TIMx更新中断标志
                        msHcCount++;
        }
}


//获取定时器时间
u32 GetEchoTimer(void)
{
        u32 t = 0;
        t = msHcCount*1000;    //得到MS
        t += TIM_GetCounter(TIM6);    //得到US
          TIM6->CNT = 0;     //将TIM2计数寄存器的计数值清零
                        Delay_Ms(50);
        return t;
}


//一次获取超声波测距数据 两次测距之间需要相隔一段时间,隔断回响信号
//为了消除余震的影响,取五次数据的平均值进行加权滤波。
float Hcsr04GetLength(void )
{
        u32 t = 0;
        int i = 0;
        float lengthTemp = 0;
        float sum = 0;
        while(i!=5)
        {
        TRIG_Send = 1;      //发送口高电平输出
        Delay_Us(20);
        TRIG_Send = 0;
        while(ECHO_Reci == 0);      //等待接收口高电平输出
                OpenTimerForHc();        //打开定时器
                i = i + 1;
                while(ECHO_Reci == 1);
                CloseTimerForHc();        //关闭定时器
                t = GetEchoTimer();        //获取时间,分辨率为1US
                lengthTemp = ((float)t/58.0);//cm
                sum = lengthTemp + sum ;
       
        }
        lengthTemp = sum/5.0;
        return lengthTemp;
}


/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
** 函数名称: Delay_Ms_Ms
** 功能描述: 延时1MS (可通过仿真来判断他的准确度)                       
** 参数描述:time (ms) 注意time<65535
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
void Delay_Ms(uint16_t time)  //延时函数
{
        uint16_t i,j;
        for(i=0;i                   for(j=0;j<10260;j++);
}
/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
** 函数名称: Delay_Ms_Us
** 功能描述: 延时1us (可通过仿真来判断他的准确度)
** 参数描述:time (us) 注意time<65535                                 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
void Delay_Us(uint16_t time)  //延时函数
{
        uint16_t i,j;
        for(i=0;i                   for(j=0;j<9;j++);
}
      

5.总结

通过本次的项目,我们对STM32的定时器的应用有一定了解,在学习STM32单片机的过程中自己可以边学边做项目,这样会提高自己的动手能力,也会巩固理论知识,本篇博客简单介绍了利用超声波模块测距,如需进一步了解可自行求助度娘,希望对大家的学习有所帮助!
举报

更多回帖

发帖
×
20
完善资料,
赚取积分