

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