一、前言
HC-SR04超声波测距模块是嵌入式开发中最常用的测距传感器之一。今天我们一起驱动这个模块,完成距离测量。
驱动过程采用定时器输入捕获 方式,并结合DWT精确延时,使驱动具备良好的一致性和实时性。最终提供一个简单的测试函数,只需调用即可循环打印距离。

二、HC-SR04测距原理
HC-SR04通过超声波往返时间计算距离:
-
Trig:触发引脚,输入大于10µs的高电平,模块发射8个40kHz超声波脉冲。
-
Echo:回响引脚,输出高电平,高电平持续时间 = 超声波往返时间。
-
距离公式:
距离(cm)=高电平时间(µs)58距离(cm)=58高电平时间(µs)
或等价于 距离(cm)=高电平时间(µs)×0.017距离(cm)=高电平时间(µs)×0.017。
这里用示波器抓了一下Echo脚,其实也就是距离决定输出的脉宽

三、硬件连接
| HC-SR04 | STM32F103 |
|---|---|
| VCC | 5V |
| GND | GND |
| Trig | PA1 |
| Echo | PA0 |
注意:Echo引脚输出5V高电平,建议串联1kΩ电阻后再接入PA0(PA0为5V容忍引脚)。
四、STM32CubeMX配置
-
时钟:配置系统时钟为72MHz(使用HSE或HSI),确保定时器计数准确。
-
TIM2:
-
时钟源:Internal Clock
-
通道1:Input Capture direct mode
-
Prescaler:71(72MHz / 72 = 1MHz,计数周期1µs)
-
Counter Period:65535
-
NVIC:使能TIM2全局中断
-
-
GPIO:
-
PA0:自动配置为TIM2_CH1(复用功能)
-
PA1:GPIO_Output,用户标签设为Trig
-
-
生成代码:勾选"Generate peripheral initialization as a pair of '.c/.h' files per peripheral"。
五、驱动代码(关键部分)
5.1 SR04.h -- 宏定义与接口
#ifndef __SR04_H
#define __SR04_H
#include "main.h"
#include "tim.h"
// 延时方式选择(推荐使用DWT)
#define DWT_DELAY
// 定时器及通道(与CubeMX一致)
#define SR04_TIM TIM2
#define SR04_TIMER &htim2
#define SR04_CHANNEL TIM_CHANNEL_1
// 引脚定义(若CubeMX已定义则无需重复)
#ifndef Trig_Pin
#define Trig_Pin GPIO_PIN_1
#define Trig_GPIO_Port GPIOA
#endif
#ifndef Echo_Pin
#define Echo_Pin GPIO_PIN_0
#define Echo_GPIO_Port GPIOA
#endif
// 超声波数据结构体
typedef struct {
uint8_t status; // 0:等待上升沿, 1:等待下降沿
uint16_t IC_Time[2]; // 上升沿/下降沿捕获值
float distance; // 距离(cm)
} SR04_Data;
void SR04_Init(void);
void SR04_Update(void);
float Get_SR04_Distance(void);
void SR04_Test(void); // 测试函数
#endif
5.2 SR04.c -- 驱动实现(核心部分)
#include "SR04.h"
#include <stdio.h>
SR04_Data SR04_t = {0, {0, 0}, -1.0f};
// 微秒延时(DWT方式)
static void SR04_Delay_us(uint32_t us)
{
#ifdef DWT_DELAY
uint32_t start = DWT->CYCCNT;
uint32_t cycles = us * (SystemCoreClock / 1000000);
while ((DWT->CYCCNT - start) < cycles);
#else
// 循环延时备用
uint32_t delayCycles = (SystemCoreClock / 1000000) * us / 4;
while (delayCycles--);
#endif
}
// 发送Trig脉冲并启动捕获
static void SR04_Trig(void)
{
HAL_GPIO_WritePin(Trig_GPIO_Port, Trig_Pin, GPIO_PIN_SET);
SR04_Delay_us(12); // 12µs高电平
HAL_GPIO_WritePin(Trig_GPIO_Port, Trig_Pin, GPIO_PIN_RESET);
HAL_TIM_IC_Start_IT(SR04_TIMER, SR04_CHANNEL);
__HAL_TIM_SET_COUNTER(SR04_TIMER, 0);
__HAL_TIM_SET_CAPTUREPOLARITY(SR04_TIMER, SR04_CHANNEL, TIM_ICPOLARITY_RISING);
SR04_t.status = 0;
}
// 对外接口:触发一次测量
void SR04_Update(void)
{
SR04_Trig();
}
// 获取最近一次测量结果
float Get_SR04_Distance(void)
{
return SR04_t.distance;
}
// 定时器输入捕获中断回调
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == SR04_TIM && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
uint32_t tick = 0;
switch (SR04_t.status)
{
case 0: // 上升沿
SR04_t.IC_Time[0] = HAL_TIM_ReadCapturedValue(SR04_TIMER, SR04_CHANNEL);
__HAL_TIM_SET_CAPTUREPOLARITY(SR04_TIMER, SR04_CHANNEL, TIM_ICPOLARITY_FALLING);
SR04_t.status = 1;
break;
case 1: // 下降沿
SR04_t.IC_Time[1] = HAL_TIM_ReadCapturedValue(SR04_TIMER, SR04_CHANNEL);
HAL_TIM_IC_Stop_IT(SR04_TIMER, SR04_CHANNEL);
SR04_t.status = 0;
// 计算脉宽(处理溢出)
if (SR04_t.IC_Time[1] >= SR04_t.IC_Time[0])
tick = SR04_t.IC_Time[1] - SR04_t.IC_Time[0];
else
tick = (0xFFFF - SR04_t.IC_Time[0]) + SR04_t.IC_Time[1];
// 距离 = 脉宽(us) / 58
SR04_t.distance = (float)tick / 58.0f;
break;
}
}
}
// 初始化(DWT、状态)
void SR04_Init(void)
{
#ifdef DWT_DELAY
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
DWT->CYCCNT = 0;
#endif
SR04_t.status = 0;
SR04_t.distance = -1.0f;
}
// 测试函数:循环测距并串口打印
void SR04_Test(void)
{
SR04_Init();
printf("HC-SR04 Test Start\r\n");
while (1)
{
SR04_Update();
// 等待测量完成(最长50ms)
uint32_t timeout = 50000;
while (SR04_t.status != 0 && timeout-- > 0)
SR04_Delay_us(1);
float dist = Get_SR04_Distance();
if (dist > 0 && dist < 500)
printf("Distance: %.2f cm\r\n", dist);
else
printf("Measurement failed\r\n");
HAL_Delay(200);
}
}
六、主函数调用
#include "main.h"
#include "SR04.h"
int main(void)
{
HAL_Init();
SystemClock_Config(); // 配置72MHz
MX_GPIO_Init();
MX_TIM2_Init(); // 定时器初始化
SR04_Test(); // 开始测试
while (1);
}
七、测试结果
将程序烧录至STM32F103,打开串口助手(波特率与printf重定向一致),我们使用尺子进行测量,感觉精度还行吧。


八、参考代码
通过网盘分享的文件:HC_SR04_STM32_Driver.zip
链接: https://pan.baidu.com/s/1WpOrqhMW7YBHxyDGLWwigQ?pwd=ygbe 提取码: ygbe
--来自百度网盘超级会员v8的分享