引言
HC-SR04超声波测距模块因其成本低、精度高、使用简单,被广泛应用于机器人避障、液位检测等领域。本文将结合STM32F103C8T6的驱动代码,详细解析HC-SR04的工作原理及实现方法,帮助开发者快速上手。
源码链接:https://share.weiyun.com/RJG4fPjy
HC-SR04模块工作原理
-
触发信号:向
Trig
引脚发送至少10μs的高电平脉冲,触发模块发射8个40kHz超声波。 -
回波接收:模块自动检测回波信号,
Echo
引脚输出高电平的时间,其持续时间与距离成正比。 -
编程思路:先向
Trig引脚发送
高电平,后延时至少10μs,然后拉低发送向Trig引脚
低电平结束驱动信号,HC-SR04模块开始发射8个40kHz超声波,然后检测Echo
引脚输出高电平的时间,测量距离=(声速343m/s ✖Echo
引脚高电平的时间/s)÷2 -
距离计算:
距离=高电平时间×340 m/s2距离=2高电平时间×340m/s
公式:简化后为:
距离(cm)=高电平持续的时间(μs)58距离(cm)=58高电平时间(μs)
驱动代码解析
1. 外设TIM定时器的初始化timer.c源文件
#include "delay.h"
#include "timer.h"
#include "adcx.h"
#include "led.h"
#include "string.h"
#include "ultrasonic.h"
extern u16 msHcCount;
void TIM4_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能
//定时器TIM4初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ClearFlag(TIM4, TIM_FLAG_Update);
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM4中断,允许更新中断
//中断优先级NVIC设置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级0级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
TIM_Cmd(TIM4,DISABLE); //禁用TIM4
}
//定时器4中断服务程序
void TIM4_IRQHandler(void) //TIM4中断
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update );
msHcCount++;
}
}
外设TIM定时器的初始化timer.h头文件
#ifndef _TIMER_H
#define _TIMER_H
#include "sys.h"
void TIM4_Int_Init(u16 arr,u16 psc); //定时器4初始化
#endif
HC-SR04超声波模块初始化ultrasonic.c源文件
#include "ultrasonic.h"
#include "timer.h"
/*****************************************
项目: HC-SR04超声波传感器实验
MCU: STM32F103C8T6
关注优快云作者: 单片机阿伟
时间: 2025-1-20
*****************************************/
//超声波计数
u16 msHcCount;
void Ultrasonic_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd (ULTRASONIC_GPIO_CLK, ENABLE ); // 打开连接 超声波传感器 的单片机引脚端口时钟
GPIO_InitStructure.GPIO_Pin = ULTRASONIC_TRIG_GPIO_PIN; // 配置连接 传感器TRIG 的单片机引脚模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 设置为推挽输出
GPIO_Init(ULTRASONIC_GPIO_PORT, &GPIO_InitStructure); // 初始化
GPIO_InitStructure.GPIO_Pin = ULTRASONIC_ECHO_GPIO_PIN; // 配置连接 传感器ECHO 的单片机引脚模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 设置为浮空输入输入
GPIO_Init(ULTRASONIC_GPIO_PORT, &GPIO_InitStructure); // 初始化
TIM4_Int_Init(1000-1,72-1); //定时器溢出一次为t=1ms, t=(arr+1)*(psc+1)/72M=1ms
}
//打开定时器4
static void OpenTimerForHc()
{
TIM_SetCounter(TIM4,0); //设置CNT的值,默认从0开始向上计数,当CNT的值大于arr则溢出
msHcCount = 0;
TIM_Cmd(TIM4, ENABLE); //打开定时器4
}
//关闭定时器4
static void CloseTimerForHc()
{
TIM_Cmd(TIM4, DISABLE);
}
//获取定时器4计数器值
u32 GetEchoTimer(void)
{
u32 t = 0;
t = msHcCount*1000; //转换成us
t += TIM_GetCounter(TIM4); //CNT计数值每1us加1,所以总的时间要加上计数器的值
TIM4->CNT = 0; //计数器的值置0
delay_ms(50);
return t;
}
//通过定时器4计数器值推算距离
/**************************
原理:Echo高电平持续的时间就是T
计算距离distance=(声波的速度(343M/S)*Echo从0到1的时间T/ms)/2
换算
熟读=(34300cm/1000000us)/2/1≈58cm/us
distance_cm=(1000Tus)/58cm
**************************/
float UltrasonicGetLength(void)
{
u32 t = 0;
int i = 0;
float lengthTemp = 0; //临时存储测得距离的变量
float sum = 0;
while(i!=5) //取5次测量的距离
{
TRIG_Send = 1; //先发送20us的高电平,告诉HC-SR04要开始测量
delay_us(20);
TRIG_Send = 0; //低电平释放
while(ECHO_Reci == 0); //while循环等待Echo为高电平
OpenTimerForHc(); //一旦Echo为高电平,打开定时器开始计时
i = i + 1;
while(ECHO_Reci == 1); //while循环等待Echo为低电平
CloseTimerForHc(); //若为低电平,则结束一次时间的计时
t = GetEchoTimer(); //获取时间
lengthTemp = ((float)t/58.0);//计算一次测量的距离cm
sum = lengthTemp + sum ; //求和
}
lengthTemp = sum/5.0; //求5次的平均值
return lengthTemp; //返回距离
}
HC-SR04超声波模块初始化ultrasonic.h头文件
#ifndef __ULTRASONIC_H
#define __ULTRASONIC_H
#include "stm32f10x.h"
#include "adcx.h"
#include "delay.h"
#include "math.h"
/*****************************************
项目: HC-SR04超声波传感器实验
MCU: STM32F103C8T6
关注优快云作者: 单片机阿伟
时间: 2025-1-20
*****************************************/
/***************根据自己需求更改****************/
// ULTRASONIC GPIO宏定义
#define ULTRASONIC_GPIO_CLK RCC_APB2Periph_GPIOA
#define ULTRASONIC_GPIO_PORT GPIOA
#define ULTRASONIC_TRIG_GPIO_PIN GPIO_Pin_0
#define ULTRASONIC_ECHO_GPIO_PIN GPIO_Pin_1
#define TRIG_Send PAout(0)
#define ECHO_Reci PAin(1)
/*********************END**********************/
void Ultrasonic_Init(void);
float UltrasonicGetLength(void);
void OpenTimerForHc(void);
void CloseTimerForHc(void);
u32 GetEchoTimer(void);
#endif /* __ADC_H */
主函数:main.c
#include "stm32f10x.h"
#include "led.h"
#include "usart.h"
#include "delay.h"
#include "oled.h"
#include "ultrasonic.h"
#include "timer.h"
/*****************************************
项目: HC-SR04超声波传感器实验
MCU: STM32F103C8T6
关注优快云作者: 单片机阿伟
时间: 2025-1-20
*****************************************/
float distance;
int main(void)
{
SystemInit();//配置系统时钟为72M
delay_init(72);
LED_Init();
LED_On();
Ultrasonic_Init();
USART1_Config();//串口初始化
// OLED_Init();
printf("Start \n");
delay_ms(1000);
while(1)
{
distance = UltrasonicGetLength();
printf("\r\n距离:%f cm\n",distance);
LED_Toggle();
delay_ms(50); //延时50ms
}
}
-
关键点:
-
Trig
引脚需输出高电平触发信号,Echo
引脚需检测回波高电平。 -
定时器4配置为1ms溢出周期,用于精确计时。
-
2. 时间测量函数
GetEchoTimer.c
u32 GetEchoTimer(void) { u32 t = msHcCount * 1000; // 溢出次数转微秒 t += TIM_GetCounter(TIM4); // 加上当前计数值 TIM4->CNT = 0; // 重置计数器 delay_ms(50); // 防止硬件干扰 return t; }
-
原理:
-
msHcCount
记录定时器溢出次数,每次溢出对应1ms。 -
TIM_GetCounter(TIM4)
获取当前周期内的计数值(1μs/计数)。 -
总时间 = 溢出时间 + 当前计数值。
-
-
注意:
delay_ms(50)
用于等待模块稳定,避免连续触发。
3. 距离计算函数
c
复制
float UltrasonicGetLength(void) { float sum = 0; for (int i=0; i<5; i++) { // 取5次测量平均值 TRIG_Send = 1; // 触发信号 delay_us(20); TRIG_Send = 0; while (ECHO_Reci == 0); // 等待回波高电平 OpenTimerForHc(); // 启动定时器 while (ECHO_Reci == 1); // 等待回波低电平 CloseTimerForHc(); // 停止定时器 sum += GetEchoTimer() / 58.0; // 计算单次距离 } return sum / 5.0; // 返回平均值 }
最后运行看效果
-
优化:
-
多次采样:取5次测量平均值,减少噪声干扰。
-
触发时序:严格遵循10μs以上触发信号(代码中为20μs)。
-
常见问题及解决方法
-
测量结果不稳定
-
原因:环境噪声或被测物体表面不平。
-
解决:增加软件滤波(如中值滤波)或硬件屏蔽。
-
-
超出最大测距范围
-
原因:默认R3电阻为392,最大测距4.5m。
-
解决:更换R3为472电阻,并调整探测角度(需硬件修改)。
-
-
回波信号无响应
-
检查点:
-
模块供电是否为5V。
-
Echo
引脚是否接触不良。
-
-
代码优化建议
-
中断优化
-
使用输入捕获功能(如TIM的IC模式)替代轮询
Echo
引脚,减少CPU占用。
// 示例:配置TIM输入捕获 TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿触发 TIM_ICInit(TIM4, &TIM_ICInitStructure);
-
结语
本文从硬件配置、代码实现到优化方向,详细解析了HC-SR04超声波模块的驱动方法。开发者可根据实际需求调整代码参数,并结合硬件优化进一步提升测距稳定性。完整代码及工程文件可通过文章开头处获取链接。