STM32-HAL库 HC-SR04超声波测距 — 2024.10.26

一、驱动简介

        此驱动基于STM32 HAL库开发,可移植到任何HAL工程里,适配任何型号的STM32单片机。本驱动不需要使用单片机的通用定时器,且测量精度很高(毫米级精度),这样做的优点是不需要占用定时器资源,工程配置简单,且驱动的可移植性更高。

        本驱动在 2024.11.10 以前免费,有需求的朋友请帮忙点个关注、点个赞。若在移植的过程中遇到无法解决的困难,请在评论区留言。

        若还需要其他类型的传感器驱动,可在评论区留言,或者添加微姓:able078

二、HC-SR04简介

(一)概述

        HC-SR04 是一种流行的超声波距离传感器,广泛应用于各种电子项目,如机器人、测距设备等。它通过发射和接收超声波信号来测量与障碍物之间的距离。HC-SR04 具有成本低、易于使用和高精度等优点。

(二)引脚说明

HC-SR04 通常有 4 个引脚,功能如下:

  • VCC:供电引脚,通常接 5V 电源。
  • Trig(触发引脚):输入引脚,发送高电平信号来触发传感器发射超声波。
  • Echo(回声引脚):输出引脚,传递接收到的回波持续时间,以计算距离。
  • GND:接地引脚。
  • (三)工作原理

    1. 触发信号:通过将 Trig 引脚拉高至少 10 微秒,传感器开始发射超声波信号。
    2. 发射超声波:传感器内的发射器会发出 8 个 40 kHz 的超声波脉冲。
    3. 接收回波:超声波遇到障碍物反射回传感器,接收器感知到这个回波信号。
    4. 持续时间测量:Echo 引脚输出高电平,时间与超声波从传感器发出到返回所用时间成正比。时间越长,距离越远。
    5. 距离计算:利用公式计算距离: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  

    (三) 添加源文件

     (四)调用驱动

    注意:

    1. 在使用该驱动的地方,需要在合适的位置写:#include "hcsr04.h" 
    2. 该驱动不需要初始化,在需要测距的地方直接调用函数即可。
    3. 使用之前需要一个浮点型的变量来缓存距离数据。
    4. 该传感器的最快调用频率不可超过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 */
    }

    五、效果展示

            因为录屏原因,不好记录传感器的移动过程,所以只录了串口调试助手的数据。但可以保证测量精度非常高,放在一个地方不动传感器,数据几乎没有波动。

    作者:智控工作室

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32-HAL库 HC-SR04超声波测距 — 2024.10.26

    发表回复