STM32-HAL库 HC-SR04超声波测距 — 2024.10.26
一、驱动简介
此驱动基于STM32 HAL库开发,可移植到任何HAL工程里,适配任何型号的STM32单片机。本驱动不需要使用单片机的通用定时器,且测量精度很高(毫米级精度),这样做的优点是不需要占用定时器资源,工程配置简单,且驱动的可移植性更高。
本驱动在 2024.11.10 以前免费,有需求的朋友请帮忙点个关注、点个赞。若在移植的过程中遇到无法解决的困难,请在评论区留言。
若还需要其他类型的传感器驱动,可在评论区留言,或者添加微姓:able078
二、HC-SR04简介
(一)概述
HC-SR04 是一种流行的超声波距离传感器,广泛应用于各种电子项目,如机器人、测距设备等。它通过发射和接收超声波信号来测量与障碍物之间的距离。HC-SR04 具有成本低、易于使用和高精度等优点。
(二)引脚说明
HC-SR04 通常有 4 个引脚,功能如下:
(三)工作原理
- 触发信号:通过将 Trig 引脚拉高至少 10 微秒,传感器开始发射超声波信号。
- 发射超声波:传感器内的发射器会发出 8 个 40 kHz 的超声波脉冲。
- 接收回波:超声波遇到障碍物反射回传感器,接收器感知到这个回波信号。
- 持续时间测量:Echo 引脚输出高电平,时间与超声波从传感器发出到返回所用时间成正比。时间越长,距离越远。
- 距离计算:利用公式计算距离:Distance = (Time*343) / 2
三、CubeMX生成基础工程
1、芯片选型:这里选择STM32f103c8t6
2、配置Debug:SW模式
3、配置外部时钟
4、配置TRIG引脚:引脚可根据实际情况任意选择,但必须重命名为:TRIG
5、配置ECHO引脚:引脚可根据实际情况任意选择,但必须重命名为:ECHO
6、配置串口,输出数据
7、输出基础工程文件
四、Keil5编写工程
(一)hcsr04.c
新建 hcsr04.c 文件,将下面代码块中的代码全部复制到 hcsr04.c 文件中
/* 这个代码块为 hcsr04.c 中的文件 */
#include "hcsr04.h"
/**************************************************
* @brief 获取系统时钟(us级时钟)
**************************************************
*/
uint64_t GetTime_us(void)
{
uint32_t time_ms;
uint32_t time_us;
uint64_t time;
time_ms=HAL_GetTick();
time_us=( ( (SysTick->LOAD+1-SysTick->VAL) * 1.0) /(SysTick->LOAD+1) )* 1000;
time=time_us+time_ms*1000;
return time;
}
/**
* 微妙延时函数
* 全系列通用,只需要将宏定义CPU_FREQUENCY_MHZ根据时钟主频修改即可。
* 系统滴答定时器是HAL库初始化的,且必须有HAL库初始化。
*/
#define CPU_FREQUENCY_MHZ (int)(HAL_RCC_GetHCLKFreq()/1000000) // 自动获取STM32时钟主频
void delay_us(__IO uint32_t delay)
{
int last, curr, val;
int temp;
while (delay != 0)
{
temp = delay > 900 ? 900 : delay;
last = SysTick->VAL;
curr = last - CPU_FREQUENCY_MHZ * temp;
if (curr >= 0)
{
do
{
val = SysTick->VAL;
}
while ((val < last) && (val >= curr));
}
else
{
curr += CPU_FREQUENCY_MHZ * 1000;
do
{
val = SysTick->VAL;
}
while ((val <= last) || (val > curr));
}
delay -= temp;
}
}
void SR04_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = TRIG_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(TRIG_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = ECHO_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(ECHO_GPIO_Port, &GPIO_InitStruct);
}
float SR04_Get_Distance(void) {
SR04_GPIO_Init();
float total_distance = 0; // 用于存储总距离
const int measurements = 5; // 测量次数
for (int i = 0; i < measurements; i++) {
uint64_t startTime = 0;
uint32_t travelTime = 0;
// 发送 10 us 的高电平信号到 TRIG 引脚
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET);
delay_us(20); // 确保 TRIG 信号维持至少 10 us
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET);
// 等待 ECHO 引脚变为高电平,开始计时
while (HAL_GPIO_ReadPin(ECHO_GPIO_Port, ECHO_Pin) == GPIO_PIN_RESET) {
// 等待 Echo 引脚高电平开始
}
startTime = GetTime_us(); // 记录开始时间
// 等待 ECHO 引脚变为低电平,计时结束
while (HAL_GPIO_ReadPin(ECHO_GPIO_Port, ECHO_Pin) == GPIO_PIN_SET) {
// 等待 Echo 引脚低电平结束
}
travelTime = GetTime_us() - startTime; // 计算往返时间
// 计算距离,Speed of Sound ~ 343 m/s => Distance (cm) = (Time (us) * 343) / 2
float distance = ((travelTime * 343.0) / 2.0) / 10000; // 返回距离(厘米)
total_distance += distance; // 累加当前测量的距离
if(measurements > 1) HAL_Delay(10);
}
// 返回 5 次测量的平均值
return total_distance / measurements;
}
将该.c文件保存到:工程路径>Core>Src
(二)hcsr04.h
新建 hcsr04.h 文件,将下面代码块中的代码全部复制到 hcsr04.h 文件中
/* 这里的代码复制到hcsr04.h中 */
#ifndef __SR04_H
#define __SR04_H
#include "main.h"
void SR04_GPIO_Init(void);
float SR04_Get_Distance(void);
#endif
并将该.h文件保存到:工程路径>Core>Inc
(三) 添加源文件
(四)调用驱动
注意:
- 在使用该驱动的地方,需要在合适的位置写:#include "hcsr04.h"
- 该驱动不需要初始化,在需要测距的地方直接调用函数即可。
- 使用之前需要一个浮点型的变量来缓存距离数据。
- 该传感器的最快调用频率不可超过100Hz,也就是每次调用间隔至少10ms。
下面是我在 main.c 中调用驱动函数的程序,读者可参考使用。
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "hcsr04.h"
#include <string.h>
#include "stdio.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
float SR04_Distance = 0.0;
char Tx_Buff[20] ;
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
SR04_Distance = SR04_Get_Distance();
sprintf(Tx_Buff,"距离:%0.1f\r\n",SR04_Distance);
HAL_UART_Transmit(&huart1,(uint8_t*)Tx_Buff,strlen(Tx_Buff),100);
HAL_Delay(500);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
五、效果展示
因为录屏原因,不好记录传感器的移动过程,所以只录了串口调试助手的数据。但可以保证测量精度非常高,放在一个地方不动传感器,数据几乎没有波动。
作者:智控工作室