LOADING

加载过慢请开启缓存 浏览器默认开启

HC-SR04模块

2023/1/5 单片机

Abstract: 超声波测距模块HC-SR04模块的代码,提供两种思路


目录:


模块介绍

懒得写,看这里

代码部分

思路一

  • 用 15us 的高电平启动模块
  • 使用间断计时的方法,用每隔一段时间(这里是 10us)触发一次的中断进行计时
  • 用计时的值和声速算出距离

HCSR04.h

#ifndef __HCSR04_H
#define __HCSR04_H

void HCSR04_Init(void);
uint8_t HCSR04_GetDistance(void);

#endif

HCSR04.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

#define Trig_Pin GPIO_Pin_7 //PB7
#define Echo_Pin GPIO_Pin_6 //PB6,TIM4_CH1

uint16_t Count=0; //计数值
uint64_t Time=0;
uint64_t Time_end=0;


/**
  * @brief  初始化函数
  * @param  无
  * @retval 无
  */
void HCSR04_Init(void)
{	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = Trig_Pin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Pin = Echo_Pin; //TIM4_CH1
     GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    GPIO_ResetBits(GPIOB, Trig_Pin); //Trig拉低
    
    TIM_InternalClockConfig(TIM4);
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //时基单元设置
    TIM_TimeBaseInitStructure.TIM_Prescaler = (72-1); //1us
    TIM_TimeBaseInitStructure.TIM_Period = (10-1); //10us溢出
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
    
    TIM_ClearFlag(TIM4, TIM_FLAG_Update);			//清除更新中断标志位
    TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);		//开启更新中断
        
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;				//指定中断通道
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//中断使能
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//设置抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;			//设置响应优先级
    NVIC_Init(&NVIC_InitStructure);
        
    TIM_Cmd(TIM4,ENABLE); //TIM4使能
}

/**
  * @brief  产生触发信号
  * @param  无
  * @retval 无
  */
void HCSR04_Start(void)
{
    GPIO_SetBits(GPIOB, Trig_Pin);
    Delay_us(15);
    GPIO_ResetBits(GPIOB, Trig_Pin);
}

/**
  * @brief  启动HC-SR04模块并测量距离
  * @param  无
  * @retval uint16_t Distance 测量结果
  */
uint16_t HCSR04_GetDistance(void)
{
    HCSR04_Start();
    uint32_t Distance = 0;
    while(GPIO_ReadInputDataBit(GPIOB,Echo_Pin)==0); //等待低电平结束
    Time=0; //计时清零
    while(GPIO_ReadInputDataBit(GPIOB,Echo_Pin)==1); //等待高电平结束
    Time_end=Time; //记录结束时的时间
    Distance=(Time_end/2)*3.46;
    return Distance;
}

/**
  * @brief  TIM4中断函数,每10ms Time加1
  * @param  无
  * @retval 无
  */
void TIM4_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) == SET)
    {
        Time++;
        TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
    }
}

思路二

  • 用 15us 的高电平启动模块
  • 使用不间断计时的方法,当检测到模块的返回信号时开始计时,直到返回信号结束,读取出计数器的值
  • 用计数的值和声速算出距离

HCSR04.h

#ifndef __HCSR04_H
#define __HCSR04_H

void HCSR04_Init(void);
uint8_t HCSR04_GetDistance(void);

#endif

HCSR04.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

#define Trig_Pin GPIO_Pin_7 //PB7
#define Echo_Pin GPIO_Pin_6 //PB6,TIM4_CH1

uint16_t Count=0; //计数值


/**
  * @brief  初始化函数
  * @param  无
  * @retval 无
  */
void HCSR04_Init(void)
{	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = Trig_Pin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Pin = Echo_Pin; //TIM4_CH1
     GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    GPIO_ResetBits(GPIOB, Trig_Pin); //Trig拉低
    
    TIM_InternalClockConfig(TIM4);
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //时基单元设置
    TIM_TimeBaseInitStructure.TIM_Prescaler = (72-1); //1us
    TIM_TimeBaseInitStructure.TIM_Period = (50000-1); //50ms溢出
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
}

/**
  * @brief  产生触发信号
  * @param  无
  * @retval 无
  */
void HCSR04_Start(void)
{
    GPIO_SetBits(GPIOB, Trig_Pin);
    Delay_us(15);
    GPIO_ResetBits(GPIOB, Trig_Pin);
}

/**
  * @brief  启动HC-SR04模块并测量距离
  * @param  无
  * @retval uint16_t Distance 测量结果
  */
uint16_t HCSR04_GetDistance(void)
{
    HCSR04_Start();
    uint32_t Distance = 0;
    TIM_SetCounter(TIM4,0);
    while(GPIO_ReadInputDataBit(GPIOB,Echo_Pin)==0); //等待低电平结束
    TIM_Cmd(TIM4,ENABLE);
    while(GPIO_ReadInputDataBit(GPIOB,Echo_Pin)==1); //等待高电平结束
    Count=TIM_GetCounter(TIM4);
    TIM_Cmd(TIM4,DISABLE);
    Distance=(Count/2)*0.346;
    return Distance;
}

值得注意的点

  • HC-SR04模块使用 DC 5V 供电,但是也有其他电压的产品

  • 模块检测距离时需要注意反射超声波的物体的大小和面倾角,若物体太小或面倾角过大将不能得到准确的结果

  • 考虑到 STM32 GPIO 口的电压耐受,建议使用 FT (即 5V 耐受)的引脚,例如这里使用的PB6、PB7引脚,也有人使用普通引脚,这里不做尝试

  • 这里两种思路的代码,从精度上看几乎一样,但是频繁进中断不太优雅,所以更偏向于思路二

  • 思路一的代码需要额外配置中断优先级组,如在主函数中加上 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

  • 也可以使用定时器的输入捕获实现功能,但是精度不理想且受外部影响较大,这里就不考虑了