当前位置: 首页 > news >正文

【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

#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

#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 EXTINVIC_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 = 1MHzTIM_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,所以每个计数1usdistance = (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

#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

#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

#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

#include "main.h"
#include "hcsr04_hal.h"// 全局变量
HCSR04_TypeDef hcsr04;
TIM_HandleTypeDef htim2;int main(void)
{float distance;HAL_Init();SystemClock_Config();// 配置HC-SR04hcsr04.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配置

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

http://www.dtcms.com/a/502954.html

相关文章:

  • 【STM32笔记】:P03 ISP 一键下载电路详解
  • 江苏省网站建设哪家好php做的购物网站
  • 常见的位运算的总结
  • CSRF(跨站请求伪造)攻击详解:原理、途径与防范
  • 【C++】使用双指针算法习题
  • 全能型 AI 模型新标杆 Qwen2.5-Omni
  • 建设银行佛山分行网站wordpress插件 评分
  • 大数据计算引擎-从源码看Spark AQE对于倾斜的处理
  • 前端基础知识---Ajax
  • 数据结构——多维数组的存储
  • 编译django做的网站网站制作 价格
  • 破解商家客服困局:真人工AI回复如何成为转型核心
  • 【Qt开发】多元素类控件(二)-> QTableWidget
  • 如何建设一个优秀的电商网站自己怎么创建微信公众号
  • 【成长纪实】Flutter中Dart 与Harmony中 ArkTS 异步编程对比:从 Future 到 Promise
  • ARMv7-A 移植 FreeRTOS 栈帧初始化
  • ARMv7-A 移植 FreeRTOS 中断和临界区管理
  • STM32中PB4引脚作普通GPIO使用的一个小问题
  • 网站怎么看哪个公司网站建设海口网站开发制作
  • 【踩坑记录】从“正在还原所需的工具包”说起:一次 .NET 工程包还原失败的完整排查实录
  • 阳山做网站什么网站做视频
  • 虚幻引擎虚拟制片入门教程目录
  • Eclipse 快速修复指南
  • 【从0开始学习Java | 第22篇】反射
  • WEBSTORM前端 —— 第5章:Web APIs —— 第1节:Dom获取属性操作
  • 第 167 场双周赛 / 第 471 场周赛
  • 聊聊 Unity(小白专享、C# 小程序 之 加密存储)
  • 如何推销网站分销商城开发多少钱
  • 大型的营销型网站建设做国外网站翻译中国小说赚钱
  • 论文学习_PalmTree: Learning an Assembly Language Model for Instruction Embedding