【mqtt参数上云和小程序开发】【第5期】hcsr04超声波测距模块调试完成

好的,我来为你提供一个完整的STM32超声波测距模块(HC-SR04)驱动代码,包含标准库HAL库两个版本。


HC-SR04工作原理

基本参数

  • 工作电压:5V
  • 测量范围:2cm - 400cm
  • 测量精度:3mm
  • 测量角度:15度

工作时序

  1. 触发信号:给Trig引脚至少10us的高电平
  2. 模块响应:模块自动发送8个40kHz超声波脉冲
  3. 回波信号:Echo引脚输出高电平,高电平持续时间与距离成正比
  4. 距离计算:距离 = (高电平时间 × 声速) / 2

方法一:STM32标准库驱动

1. 头文件 hcsr04.h

c 复制代码
#ifndef __HCSR04_H
#define __HCSR04_H

#include "stm32f10x.h"

// HC-SR04引脚定义 - 根据实际连接修改
#define HCSR04_TRIG_PORT    GPIOA
#define HCSR04_TRIG_PIN     GPIO_Pin_0
#define HCSR04_ECHO_PORT    GPIOA  
#define HCSR04_ECHO_PIN     GPIO_Pin_1

// 定时器定义
#define HCSR04_TIM          TIM2

// 函数声明
void HCSR04_Init(void);
float HCSR04_GetDistance(void);
void HCSR04_StartMeasurement(void);
uint8_t HCSR04_IsMeasurementComplete(void);

// 外部变量
extern volatile uint32_t HCSR04_Echo_Start;
extern volatile uint32_t HCSR04_Echo_End;
extern volatile uint8_t HCSR04_Measurement_Complete;

#endif

2. 源文件 hcsr04.c

c 复制代码
#include "hcsr04.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_exti.h"
#include "misc.h"
#include "delay.h"  // 需要实现微秒延时

// 全局变量
volatile uint32_t HCSR04_Echo_Start = 0;
volatile uint32_t HCSR04_Echo_End = 0;
volatile uint8_t HCSR04_Measurement_Complete = 0;

/**
  * @brief  HC-SR04初始化
  */
void HCSR04_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
    
    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    
    // 初始化Trig引脚(输出)
    GPIO_InitStructure.GPIO_Pin = HCSR04_TRIG_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(HCSR04_TRIG_PORT, &GPIO_InitStructure);
    
    // 初始化Echo引脚(输入)
    GPIO_InitStructure.GPIO_Pin = HCSR04_ECHO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;  // 下拉输入
    GPIO_Init(HCSR04_ECHO_PORT, &GPIO_InitStructure);
    
    // 配置EXTI用于Echo引脚(双边沿触发)
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);
    
    EXTI_InitStructure.EXTI_Line = EXTI_Line1;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;  // 双边沿触发
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
    
    // 配置NVIC for EXTI
    NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    // 配置定时器TIM2用于测量时间(1MHz计数频率)
    TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF;  // 最大计数值
    TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1;   // 72MHz/72 = 1MHz
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(HCSR04_TIM, &TIM_TimeBaseStructure);
    
    // 启动定时器
    TIM_Cmd(HCSR04_TIM, ENABLE);
}

/**
  * @brief  启动一次测量
  */
void HCSR04_StartMeasurement(void)
{
    // 确保Echo为低电平
    while(GPIO_ReadInputDataBit(HCSR04_ECHO_PORT, HCSR04_ECHO_PIN));
    
    // 拉高Trig引脚10us以上
    GPIO_SetBits(HCSR04_TRIG_PORT, HCSR04_TRIG_PIN);
    Delay_us(20);  // 20us脉冲
    GPIO_ResetBits(HCSR04_TRIG_PORT, HCSR04_TRIG_PIN);
    
    // 重置测量状态
    HCSR04_Measurement_Complete = 0;
}

/**
  * @brief  检查测量是否完成
  */
uint8_t HCSR04_IsMeasurementComplete(void)
{
    return HCSR04_Measurement_Complete;
}

/**
  * @brief  获取距离(厘米)
  * @retval 距离值(厘米),-1表示测量失败或超时
  */
float HCSR04_GetDistance(void)
{
    float distance = -1;
    
    if (HCSR04_Measurement_Complete) {
        if (HCSR04_Echo_End > HCSR04_Echo_Start) {
            uint32_t pulse_width = HCSR04_Echo_End - HCSR04_Echo_Start;
            
            // 距离 = (高电平时间 * 声速) / 2
            // 声速约340m/s = 34000cm/s
            // 定时器计数频率1MHz,所以每个计数1us
            distance = (pulse_width * 0.034) / 2;  // 单位:厘米
            
            // 限制在有效范围内
            if (distance < 2) distance = -1;
            if (distance > 400) distance = -1;
        }
        HCSR04_Measurement_Complete = 0;
    }
    
    return distance;
}

/**
  * @brief  EXTI1中断服务函数(Echo引脚)
  */
void EXTI1_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line1) != RESET) {
        if (GPIO_ReadInputDataBit(HCSR04_ECHO_PORT, HCSR04_ECHO_PIN)) {
            // 上升沿:Echo开始,记录开始时间
            HCSR04_Echo_Start = TIM_GetCounter(HCSR04_TIM);
        } else {
            // 下降沿:Echo结束,记录结束时间
            HCSR04_Echo_End = TIM_GetCounter(HCSR04_TIM);
            HCSR04_Measurement_Complete = 1;
        }
        
        // 清除中断标志位
        EXTI_ClearITPendingBit(EXTI_Line1);
    }
}

方法二:STM32 HAL库驱动

1. 头文件 hcsr04_hal.h

c 复制代码
#ifndef __HCSR04_HAL_H
#define __HCSR04_HAL_H

#include "main.h"

// HC-SR04结构体
typedef struct {
    GPIO_TypeDef *Trig_GPIOx;
    uint16_t Trig_GPIO_Pin;
    GPIO_TypeDef *Echo_GPIOx;
    uint16_t Echo_GPIO_Pin;
    TIM_HandleTypeDef *htim;
    uint32_t Echo_Start;
    uint32_t Echo_End;
    uint8_t Measurement_Complete;
} HCSR04_TypeDef;

// 函数声明
void HCSR04_Init(HCSR04_TypeDef *hcsr04);
void HCSR04_StartMeasurement(HCSR04_TypeDef *hcsr04);
float HCSR04_GetDistance(HCSR04_TypeDef *hcsr04);
uint8_t HCSR04_IsMeasurementComplete(HCSR04_TypeDef *hcsr04);
void HCSR04_Echo_EXTI_Callback(HCSR04_TypeDef *hcsr04);

#endif

2. 源文件 hcsr04_hal.c

c 复制代码
#include "hcsr04_hal.h"

/**
  * @brief  HC-SR04初始化
  */
void HCSR04_Init(HCSR04_TypeDef *hcsr04)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 初始化Trig引脚(输出)
    GPIO_InitStruct.Pin = hcsr04->Trig_GPIO_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(hcsr04->Trig_GPIOx, &GPIO_InitStruct);
    
    // 初始化Echo引脚(输入)
    GPIO_InitStruct.Pin = hcsr04->Echo_GPIO_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;  // 双边沿中断
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(hcsr04->Echo_GPIOx, &GPIO_InitStruct);
    
    // 初始化变量
    hcsr04->Measurement_Complete = 0;
    hcsr04->Echo_Start = 0;
    hcsr04->Echo_End = 0;
    
    // 启动定时器
    HAL_TIM_Base_Start(hcsr04->htim);
}

/**
  * @brief  启动一次测量
  */
void HCSR04_StartMeasurement(HCSR04_TypeDef *hcsr04)
{
    // 确保Echo为低电平
    while(HAL_GPIO_ReadPin(hcsr04->Echo_GPIOx, hcsr04->Echo_GPIO_Pin));
    
    // 拉高Trig引脚10us以上
    HAL_GPIO_WritePin(hcsr04->Trig_GPIOx, hcsr04->Trig_GPIO_Pin, GPIO_PIN_SET);
    HAL_Delay(1);  // 1ms延时,确保足够宽度
    HAL_GPIO_WritePin(hcsr04->Trig_GPIOx, hcsr04->Trig_GPIO_Pin, GPIO_PIN_RESET);
    
    // 重置测量状态
    hcsr04->Measurement_Complete = 0;
}

/**
  * @brief  获取距离(厘米)
  */
float HCSR04_GetDistance(HCSR04_TypeDef *hcsr04)
{
    float distance = -1;
    
    if (hcsr04->Measurement_Complete) {
        if (hcsr04->Echo_End > hcsr04->Echo_Start) {
            uint32_t pulse_width = hcsr04->Echo_End - hcsr04->Echo_Start;
            
            // 计算距离(厘米)
            distance = (pulse_width * 0.034) / 2;
            
            // 限制在有效范围内
            if (distance < 2) distance = -1;
            if (distance > 400) distance = -1;
        }
        hcsr04->Measurement_Complete = 0;
    }
    
    return distance;
}

/**
  * @brief  检查测量是否完成
  */
uint8_t HCSR04_IsMeasurementComplete(HCSR04_TypeDef *hcsr04)
{
    return hcsr04->Measurement_Complete;
}

/**
  * @brief  Echo引脚中断回调函数
  */
void HCSR04_Echo_EXTI_Callback(HCSR04_TypeDef *hcsr04)
{
    if (HAL_GPIO_ReadPin(hcsr04->Echo_GPIOx, hcsr04->Echo_GPIO_Pin)) {
        // 上升沿:记录开始时间
        hcsr04->Echo_Start = __HAL_TIM_GET_COUNTER(hcsr04->htim);
    } else {
        // 下降沿:记录结束时间
        hcsr04->Echo_End = __HAL_TIM_GET_COUNTER(hcsr04->htim);
        hcsr04->Measurement_Complete = 1;
    }
}

在主函数中使用

标准库版本 main.c

c 复制代码
#include "stm32f10x.h"
#include "hcsr04.h"
#include "delay.h"
#include "stdio.h"  // 如果使用串口打印

int main(void)
{
    float distance;
    
    // 系统初始化
    SystemInit();
    Delay_init();
    HCSR04_Init();
    
    // 初始化串口(用于打印结果)
    // USART1_Init();
    
    while(1) {
        // 启动测量
        HCSR04_StartMeasurement();
        
        // 等待测量完成(带超时)
        uint32_t timeout = 100000;  // 超时计数
        while(!HCSR04_IsMeasurementComplete() && timeout--);
        
        // 获取距离
        distance = HCSR04_GetDistance();
        
        if(distance > 0) {
            printf("距离: %.2f cm\r\n", distance);
        } else {
            printf("测量失败或超出范围\r\n");
        }
        
        // 延时1秒后再次测量
        Delay_ms(1000);
    }
}

HAL库版本 main.c

c 复制代码
#include "main.h"
#include "hcsr04_hal.h"

// 全局变量
HCSR04_TypeDef hcsr04;
TIM_HandleTypeDef htim2;

int main(void)
{
    float distance;
    
    HAL_Init();
    SystemClock_Config();
    
    // 配置HC-SR04
    hcsr04.Trig_GPIOx = GPIOA;
    hcsr04.Trig_GPIO_Pin = GPIO_PIN_0;
    hcsr04.Echo_GPIOx = GPIOA;
    hcsr04.Echo_GPIO_Pin = GPIO_PIN_1;
    hcsr04.htim = &htim2;
    
    HCSR04_Init(&hcsr04);
    
    while (1) {
        // 启动测量
        HCSR04_StartMeasurement(&hcsr04);
        
        // 等待测量完成
        HAL_Delay(100);  // 等待100ms
        
        // 获取距离
        distance = HCSR04_GetDistance(&hcsr04);
        
        if(distance > 0) {
            printf("距离: %.2f cm\r\n", distance);
        } else {
            printf("测量失败\r\n");
        }
        
        HAL_Delay(1000);
    }
}

// Echo引脚中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_1) {
        HCSR04_Echo_EXTI_Callback(&hcsr04);
    }
}

硬件连接

复制代码
HC-SR04          STM32
VCC    ---->     5V
Trig   ---->     PA0 (或其他GPIO)
Echo   ---->     PA1 (或其他GPIO)  
GND    ---->     GND

注意:HC-SR04需要5V供电,但Echo引脚输出是5V电平,STM32 GPIO只能承受3.3V。建议:

  1. 使用电阻分压(1kΩ + 2kΩ)
  2. 使用电平转换芯片

关键点说明

  1. 定时器配置:使用1MHz计数频率,每个计数代表1us
  2. 中断使用:Echo引脚使用双边沿触发中断
  3. 超时处理:添加超时机制防止死等
  4. 范围限制:将距离限制在2-400cm有效范围内
  5. 单位换算:距离 = (时间 × 34000) / 2 / 1000000 (厘米)

常见问题排查

  1. 测量值不准确:检查声速常数、定时器分频设置
  2. 无响应:检查Trig信号、电源电压、硬件连接
  3. 数值跳动:多次测量取平均值,添加软件滤波
  4. 中断不触发:检查GPIO配置、NVIC设置、EXTI配置

这个驱动代码提供了完整的功能,你可以根据实际需求进行调整和优化。

相关推荐
点灯小铭4 小时前
基于单片机的PWM三基色LED灯控制器设计与无线调色系统
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
Vae_Mars12 小时前
单片机中的TVS管
单片机·嵌入式硬件
hazy1k14 小时前
51单片机基础-直流电机控制
stm32·单片机·嵌入式硬件·51单片机
小莞尔16 小时前
【51单片机】【protues仿真】基于51单片机智能窗帘系统
c语言·stm32·单片机·嵌入式硬件·物联网·51单片机
我先去打把游戏先18 小时前
ESP32学习笔记(基于IDF):IOT应用——WIFI连接
笔记·单片机·嵌入式硬件·mcu·物联网·学习·esp32
清风66666620 小时前
基于单片机的简易智能衣架控制系统设计
单片机·嵌入式硬件·毕业设计·课程设计
酷飞飞21 小时前
I2C软实现基于GD32F407VE的天空星的配置
单片机·嵌入式硬件
充哥单片机设计21 小时前
【STM32项目开源】基于STM32的人体健康监测系统
stm32·单片机·嵌入式硬件
hazy1k1 天前
51单片机基础-独立按键
stm32·单片机·嵌入式硬件·51单片机