
CH_SR04
一、简介
1.产品特点
HC_SR04超声波测距模块可提供2cm-400cm的非接触式测距感测功能,测距精度高达3mm;模块包括超声波发射器,接收器与控制电路。
基本工作原理:
(1)采用IO口TRIG触发测距,需要给最少10us的高电平。
(2)模块自动发送8个40kHz的方波,自动检测是否有信号返回。
(3)有信号返回,通过IO口ECHO输出一个高电平,高电平持续时间就是超声波从发射到返回的时间。
距离计算公式:uS/58=厘米,uS/148=英尺,距离=高电平时间*声速(340m/s)/2。
建议测量周期为60ms以上,以防止发射信号对回波信号的影响。
注:此模块不易带电连接,若要带电连接,则需将模块的GND先连接,否则容易接线错误影响正常使用。测距时,被测物体的面积不少于0.5平方米且平面尽量要求平整,否则影响测量结果。
2.实物图
VCC:供5V电源
GND:为地线
TRIG:触发控制信号输入
MCHO:回响信号输出

实物图
3.电气特性
电气参数 | HC-SR04超声波模块 |
工作电压 | DC 5V |
工作电流 | 15mA |
工作频率 | 40kHz |
最远射程 | 4m |
最近射程 | 2cm |
测量角度 | 15° |
输入触发信号 | 10uSTTL脉冲 |
输出回响信号 | 输出TTL电平信号 |
规格尺寸 | 45*20*15mm |
4.时序图

时序图
以上时序图表明只需要提供一个10us以上的脉冲触发信号,该模块内部将发出8个40kHz周期电平并检测回波。一旦检测到有回波信号则输出回响信号,回响信号的脉冲宽度与所测的距离成正比。

视频演示
以上对HC_SR04超声波测距模块的介绍在说明书中都有介绍。以下开始对HC_SR04进行配置。
二、程序设计
1.准备
硬件:
(1)单片机最小系统(笔者用的是 STM32F103ZET6 开发板)
(2)HC-SR超声波测距模块
(3)0.96寸OLED屏
(4)杜邦线
主芯片相关外设:
(1)RCC时钟
(2)GPIO
(3)TIM定时器
(4)EXTI外部中断
(5)Systick系统滴答定时器
开发环境: KEIL_5
2.硬件连接
HC-SR04超声波模块 | |
TRIG | PD_0 |
MCHO | PD_1 |
3.驱动编写
编程思路:
使能GPIO,AFIO,TIM,EXTI。
①TRIG为主机发送触发信号端,可将对应io配置为输出模式,并拉低电平。触发信号:io口输出高电平持续10us以上再输出低电平。
②MCHO为从机输出回响信号端,由主机接收信号,可将对应io配置为输入模式。配置外部中断,检测io电平变化。
③使用TIM6,在MCHO上升沿时启动定时器,记录MCHO高电平持续时间;在下降沿时关闭定时器,并清空计数器,保存高电平持续时间。
④使用TIM7,定时发送触发信号。
⑤配置中断,设置中断优先级,编写中断服务函数。
⑥main函数负责向oled输出实测距离。
(1)GPIO引脚和外部中断配置
void HC_SR04_PinInit(void)
{//1.配置GPIO模式GPIO_InitTypeDef hc_sr04_gpioInit;hc_sr04_gpioInit.GPIO_Pin = GPIO_Pin_1;hc_sr04_gpioInit.GPIO_Mode = GPIO_Mode_IPD; //下拉输入模式hc_sr04_gpioInit.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOD,&hc_sr04_gpioInit);hc_sr04_gpioInit.GPIO_Pin = GPIO_Pin_0;hc_sr04_gpioInit.GPIO_Mode = GPIO_Mode_Out_PP; //通用推挽输出GPIO_Init(GPIOD,&hc_sr04_gpioInit);HC_SR04_TRIG_0;//拉低电平//2.配置外部中断EXTI_InitTypeDef hc_sr04_extiInit;hc_sr04_extiInit.EXTI_Line = EXTI_Line1;hc_sr04_extiInit.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式hc_sr04_extiInit.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//双边沿对齐hc_sr04_extiInit.EXTI_LineCmd = ENABLE;//使能EXTI_Init(&hc_sr04_extiInit);//3.选择外部中断线GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource1);//选择PD1作为外部中断线//EXTI_GenerateSWInterrupt(EXTI_Line1);//产生一个软件中断EXTI_ClearFlag(EXTI_Line1);//清除挂起标志位
}
(2)TIM配置
/*\brief: 基本TIM初始化\param: TIMx: where x can be 1 to 17 to select the TIM peripheral.psc:预分频系数arr:重装载值\retval: none
*/
static void TIMx_Init(TIM_TypeDef* TIMx,uint16_t psc,uint16_t arr)
{//1.复位TIMTIM_DeInit(TIMx);//2.配置TIMTIM_TimeBaseInitTypeDef TIMx_timeBaseInit;TIMx_timeBaseInit.TIM_Prescaler = psc-1; //设置预分频系数为pscTIMx_timeBaseInit.TIM_CounterMode = TIM_CounterMode_Up; //向上计数TIMx_timeBaseInit.TIM_RepetitionCounter = 0; //重复计数值TIMx_timeBaseInit.TIM_Period = arr; //重装载值TIMx_timeBaseInit.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInit(TIMx,&TIMx_timeBaseInit);//3.中断配置TIM_ITConfig(TIMx,TIM_IT_Update,ENABLE); //使能更新中断TIM_ClearFlag(TIMx,TIM_FLAG_Update); //清除挂起标志//4.失能定时器TIM_Cmd(TIMx,DISABLE);
}
(2)配置中断优先级
/*\brief: 配置中断优先级\param: NVIC_IRQChannel:中断号PreemptionPriority:抢占优先级SubPriority:子优先级\retval: none
*/
static void nvic_InitConfig(uint8_t IRQChannel,uint8_t PreemptionPriority,uint8_t SubPriority)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);//配置优先级分组NVIC_InitTypeDef ch_sr04_NVIC_init;ch_sr04_NVIC_init.NVIC_IRQChannel = IRQChannel;ch_sr04_NVIC_init.NVIC_IRQChannelPreemptionPriority = PreemptionPriority;ch_sr04_NVIC_init.NVIC_IRQChannelSubPriority = SubPriority;ch_sr04_NVIC_init.NVIC_IRQChannelCmd = ENABLE; //使能中断NVIC_Init(&ch_sr04_NVIC_init);
}
(3)初始化HC_SR04
使用外部中断需要开启AFIO时钟。
触发信号发送周期建议大于60ms,此处为200ms定时发送触发信号。
/*\brief: CH_SR04超声测距模块初始化配置\retval: none
*/
void HC_SR04_Init(void)
{//1.打开外设时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);//使能TIM6RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE);//使能TIM7RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);//使能GPIODRCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO//2.GPIO初始化HC_SR04_PinInit();//3.TIM初始化TIMx_Init(TIM6,72,0xFFFF); //72分频 1us 用于记录MCHO高电平时间TIMx_Init(TIM7,720,20000); //200ms发一次脉冲TIM_Cmd(TIM7,ENABLE);//4.配置中断优先级nvic_InitConfig(EXTI1_IRQn,1,0);nvic_InitConfig(TIM6_IRQn,2,0);nvic_InitConfig(TIM7_IRQn,3,0);
}
//触发信号
void HC_SR04_TriggerSignal(void)
{HC_SR04_TRIG_1;Delay_us(20); //延时10us以上HC_SR04_TRIG_0;
}
(4)中断服务函数
为了使用us计数,将TIM6进行72分频,最大计数值为65535us,而MCHO高电平最大持续时间为400*58=23200us,所以将TIM6的重装载寄存器的值设为0xFFFF,完全够用于记录MCHO的高电平持续时间。如果TIM一次计数到溢出所用的时间小于23200us,可定义一个变量记录一次回响信号TIM6进中断的次数,再进行时间计算。
本文中的tim6_IT_count就显得有些多余。
uint16_t tim6_IT_count=0;
//ECHO 中断线服务函数
void EXTI1_IRQHandler(void)
{if(RESET != EXTI_GetITStatus(EXTI_Line1)){EXTI_ClearITPendingBit(EXTI_Line1);//清除中断标志位if(HC_SR04_ECHO()) //上升沿{TIM_Cmd(TIM6,ENABLE); //启动定时器} else //下降沿{TIM_Cmd(TIM6,DISABLE); //关闭定时器Dist_cm = (tim6_IT_count*0xFFFF+TIM_GetCounter(TIM6))/58.0;//计算距离TIM_SetCounter(TIM6,0x00); //清空计数器tim6_IT_count=0; //计数数清零}}
}
//基本定时器TIM6 辅助MCHO高电平期间计数
void TIM6_IRQHandler(void)
{if(RESET != TIM_GetITStatus(TIM6,TIM_IT_Update)){TIM_ClearITPendingBit(TIM6,TIM_IT_Update);//清除标志位tim6_IT_count++;}
}
//基本定时器TIM7 定时发送CH_SR04载波发送
void TIM7_IRQHandler(void)
{if(RESET != TIM_GetITStatus(TIM7,TIM_IT_Update)){TIM_ClearITPendingBit(TIM7,TIM_IT_Update);//清除标志位HC_SR04_TriggerSignal();//触发信号}
}
三、实验结果

测量值不会很准确,当测量距离小于2cm时,或者测量斜面时,数值会跳动很大。如果需要增加精准度,可通过排序取中间值或其他方式减小误差。
四、代码
hc_sr04.c
#include "hc_sr04.h"/*配置GPIO\pin: ECHO - PD1 触发控制信号TRIG - PD0 回响信号
*/
#define HC_SR04_ECHO() GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_1)//读取输入电平
#define HC_SR04_TRIG_0 GPIO_ResetBits(GPIOD,GPIO_Pin_0) //写0
#define HC_SR04_TRIG_1 GPIO_SetBits(GPIOD,GPIO_Pin_0) //写1float Dist_cm;//测量距离void HC_SR04_PinInit(void)
{//1.配置GPIO模式GPIO_InitTypeDef hc_sr04_gpioInit;hc_sr04_gpioInit.GPIO_Pin = GPIO_Pin_1;hc_sr04_gpioInit.GPIO_Mode = GPIO_Mode_IPD; //下拉输入模式hc_sr04_gpioInit.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOD,&hc_sr04_gpioInit);hc_sr04_gpioInit.GPIO_Pin = GPIO_Pin_0;hc_sr04_gpioInit.GPIO_Mode = GPIO_Mode_Out_PP; //通用推挽输出GPIO_Init(GPIOD,&hc_sr04_gpioInit);HC_SR04_TRIG_0;//拉低电平//2.配置外部中断EXTI_InitTypeDef hc_sr04_extiInit;hc_sr04_extiInit.EXTI_Line = EXTI_Line1;hc_sr04_extiInit.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式hc_sr04_extiInit.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//双边沿对齐hc_sr04_extiInit.EXTI_LineCmd = ENABLE;//使能EXTI_Init(&hc_sr04_extiInit);//3.选择外部中断线GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource1);//选择PD1作为外部中断线//EXTI_GenerateSWInterrupt(EXTI_Line1);//产生一个软件中断EXTI_ClearFlag(EXTI_Line1);//清除挂起标志位
}
/*\brief: 基本TIM初始化\param: TIMx: where x can be 1 to 17 to select the TIM peripheral.psc:预分频系数arr:重装载值\retval: none
*/
static void TIMx_Init(TIM_TypeDef* TIMx,uint16_t psc,uint16_t arr)
{//1.复位TIMTIM_DeInit(TIMx);//2.配置TIMTIM_TimeBaseInitTypeDef TIMx_timeBaseInit;TIMx_timeBaseInit.TIM_Prescaler = psc-1; //设置预分频系数为pscTIMx_timeBaseInit.TIM_CounterMode = TIM_CounterMode_Up; //向上计数TIMx_timeBaseInit.TIM_RepetitionCounter = 0; //重复计数值TIMx_timeBaseInit.TIM_Period = arr; //重装载值TIMx_timeBaseInit.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInit(TIMx,&TIMx_timeBaseInit);//3.中断配置TIM_ITConfig(TIMx,TIM_IT_Update,ENABLE); //使能更新中断TIM_ClearFlag(TIMx,TIM_FLAG_Update); //清除挂起标志//4.失能定时器TIM_Cmd(TIMx,DISABLE);
}
/*\brief: 配置中断优先级\param: NVIC_IRQChannel:中断号PreemptionPriority:抢占优先级SubPriority:子优先级\retval: none
*/
static void nvic_InitConfig(uint8_t IRQChannel,uint8_t PreemptionPriority,uint8_t SubPriority)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);//配置优先级分组NVIC_InitTypeDef ch_sr04_NVIC_init;ch_sr04_NVIC_init.NVIC_IRQChannel = IRQChannel;ch_sr04_NVIC_init.NVIC_IRQChannelPreemptionPriority = PreemptionPriority;ch_sr04_NVIC_init.NVIC_IRQChannelSubPriority = SubPriority;ch_sr04_NVIC_init.NVIC_IRQChannelCmd = ENABLE; //使能中断NVIC_Init(&ch_sr04_NVIC_init);
}
/*\brief: CH_SR04超声测距模块初始化配置\retval: none
*/
void HC_SR04_Init(void)
{//1.打开外设时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);//使能TIM6RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE);//使能TIM7RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);//使能GPIODRCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO//2.GPIO初始化HC_SR04_PinInit();//3.TIM初始化TIMx_Init(TIM6,72,0xFFFF); //72分频 1us 用于记录MCHO高电平时间TIMx_Init(TIM7,720,20000); //200ms发一次脉冲TIM_Cmd(TIM7,ENABLE);//4.配置中断优先级nvic_InitConfig(EXTI1_IRQn,1,0);nvic_InitConfig(TIM6_IRQn,2,0);nvic_InitConfig(TIM7_IRQn,3,0);
}
//触发信号
void HC_SR04_TriggerSignal(void)
{HC_SR04_TRIG_1;Delay_us(20); //延时10us以上HC_SR04_TRIG_0;
}
uint16_t tim6_IT_count=0;
//ECHO 中断线服务函数
void EXTI1_IRQHandler(void)
{if(RESET != EXTI_GetITStatus(EXTI_Line1)){EXTI_ClearITPendingBit(EXTI_Line1);//清除中断标志位if(HC_SR04_ECHO()) //上升沿{TIM_Cmd(TIM6,ENABLE); //启动定时器} else //下降沿{TIM_Cmd(TIM6,DISABLE); //关闭定时器Dist_cm = (tim6_IT_count*0xFFFF+TIM_GetCounter(TIM6))/58.0;//计算距离TIM_SetCounter(TIM6,0x00); //清空计数器tim6_IT_count=0; //计数数清零}}
}
//基本定时器TIM6 辅助MCHO高电平期间计数
void TIM6_IRQHandler(void)
{if(RESET != TIM_GetITStatus(TIM6,TIM_IT_Update)){TIM_ClearITPendingBit(TIM6,TIM_IT_Update);//清除标志位tim6_IT_count++;}
}
//基本定时器TIM7 定时发送CH_SR04载波发送
void TIM7_IRQHandler(void)
{if(RESET != TIM_GetITStatus(TIM7,TIM_IT_Update)){TIM_ClearITPendingBit(TIM7,TIM_IT_Update);//清除标志位HC_SR04_TriggerSignal();//触发信号}
}
hc_sr04.h
#ifndef _HC_SR04_H_
#define _HC_SR04_H_#include "stm32f10x.h"
#include "systick.h"extern float Dist_cm;//测量距离 cmvoid HC_SR04_Init(void);#endif
main.c
#include "stm32f10x.h"
#include "hc_sr04.h"
#include "systick.h"
#include "oled.h"
#include <stdio.h>
#include <string.h>
/*超声测距实验
*/int main(void)
{/* 相关外设初始化 */HC_SR04_Init(); //hc_sr04初始化systick_config(); //系统滴答OLED_Init(); //oled初始化uint8_t buff[10];while(1){snprintf((char *)buff,10,"%0.1f%s",Dist_cm,"cm"); //拼接字符串OLED_Display_String(20,16,12,24,buff); //oled显示字符串OLED_Refresh(); //刷新函数OLED_GRAM_Init(); //初始化oled缓存memset(buff,0,10); //清零buff}
}
其他代码:略。
源码下载:
链接:https://pan.baidu.com/s/1mfZma1C0xWdlEr6bNz4KCg?pwd=1234
提取码:1234
文章如有错误,还望在评论区指正!
2023/1/13