【mqtt参数上云和小程序开发】【第5期】hcsr04超声波测距模块调试完成
好的,我来为你提供一个完整的STM32超声波测距模块(HC-SR04)驱动代码,包含标准库和HAL库两个版本。
HC-SR04工作原理
基本参数
- 工作电压:5V
- 测量范围:2cm - 400cm
- 测量精度:3mm
- 测量角度:15度
工作时序
- 触发信号:给Trig引脚至少10us的高电平
- 模块响应:模块自动发送8个40kHz超声波脉冲
- 回波信号:Echo引脚输出高电平,高电平持续时间与距离成正比
- 距离计算:距离 = (高电平时间 × 声速) / 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。建议:
- 使用电阻分压(1kΩ + 2kΩ)
- 使用电平转换芯片
关键点说明
- 定时器配置:使用1MHz计数频率,每个计数代表1us
- 中断使用:Echo引脚使用双边沿触发中断
- 超时处理:添加超时机制防止死等
- 范围限制:将距离限制在2-400cm有效范围内
- 单位换算:距离 = (时间 × 34000) / 2 / 1000000 (厘米)
常见问题排查
- 测量值不准确:检查声速常数、定时器分频设置
- 无响应:检查Trig信号、电源电压、硬件连接
- 数值跳动:多次测量取平均值,添加软件滤波
- 中断不触发:检查GPIO配置、NVIC设置、EXTI配置
这个驱动代码提供了完整的功能,你可以根据实际需求进行调整和优化。