STM32驱动HC-SR04超声波测距模块

一、前言

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配置

  1. 时钟:配置系统时钟为72MHz(使用HSE或HSI),确保定时器计数准确。

  2. TIM2

    • 时钟源:Internal Clock

    • 通道1:Input Capture direct mode

    • Prescaler:71(72MHz / 72 = 1MHz,计数周期1µs)

    • Counter Period:65535

    • NVIC:使能TIM2全局中断

  3. GPIO

    • PA0:自动配置为TIM2_CH1(复用功能)

    • PA1:GPIO_Output,用户标签设为Trig

  4. 生成代码:勾选"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的分享

相关推荐
飞凌嵌入式2 小时前
飞凌嵌入式RK3506J核心板通过OpenHarmony 5.1兼容性认证
嵌入式硬件·开源·鸿蒙
李永奉3 小时前
杰理芯片SDK-更改芯片产品蓝牙名功能
单片机·嵌入式硬件·mcu·物联网·语音识别
坤坤藤椒牛肉面3 小时前
ARM中断设置--定时器中断
单片机·嵌入式硬件
普中科技3 小时前
【普中 51-Ai8051 开发攻略】-- 第 6 章 LED 实验
单片机·嵌入式硬件·开发板·led·普中科技·ai8051u·aicube
蓝凌y3 小时前
51单片机点亮LED
单片机·嵌入式硬件·51单片机
玻璃杯中水3 小时前
硬件知识总结梳理-5(二极管)
单片机·嵌入式硬件·学习
电子科技圈3 小时前
SmartDV展示汽车IP解决方案以赋能智驾创芯并加速规模化普及
嵌入式硬件·设计模式·硬件架构·软件工程·软件构建·设计规范
weiyvyy4 小时前
常用嵌入式硬件接口原理与开发方法-GPIO接口
单片机·嵌入式硬件·gpio接口硬件设计·gpio接口原理·gpio接口驱动开发·gpio接口调试常见问题
fengfuyao9854 小时前
STM32智能桌面宠物-AI机器狗设计与实现
人工智能·stm32·宠物