波特律动
课程中心/STM32基础教程/【Timer】超声波测距

【Timer】超声波测距

下载例程代码: 

注意

CubeIDE:请按照 例程使用方法🔗 导入例程,否则下载的可能不是例程而是其他工程。

Keil:请使用 ArmCC V6 编译,否则可能会出现编译错误。点击此处查看切换编译器方法🔗

HC-SR04 超声波模块简介

HC-SR04 工作原理

模块有2个超声波换能器(如图所示),一个发出声波,另一个接收物体反射回来的声波,这中间所经过的时间即声波传播的时间,再结合声速就能计算出:

  • 距离 = 声速 * 时间 ÷ 2

如何使用HC-SR04模块

模块具有4个引脚,除了电源外,有TRIG、ECHO两个引脚需要操作:

  • 首先,向TRIG引脚发送一个高电平脉冲,来触发模块输出声波
  • 记录ECHO引脚输出高电平的时间,即声波的飞行时间
  • 距离 = 声速(340m/s) * 声波的飞行时间 ÷ 2
信息

更多资料请查看 资料包 -> 配套模块资料 -> 超声波测距HC-SR04-P

如何使用例程

下载程序,并连接硬件,即可看到效果

硬件连接

  • 使用配套TYPE-C数据线,将学习板连接到计算机
  • 需要使用:4P杜邦线、超声波模块
  • 连接模块时请核对好线序

循迹模块

学习板

VCC

VCC

TRIG

A11

ECHO

A10

GND

GND

程序效果

  • 直接观察oled即可看到测距结果
  • 打开 波特律动 串口助手 (baud-dance.com) 在线串口调试助手,点击“选择串口”,选择USB Single Serial
  • 将超声波模块对准一个平面(量程 2 - 400 cm),可以在串口助手看到测距结果

例程讲解

下面介绍了如何自己实现该例程的功能

1、工程配置

  • 开启外部晶振:在Pinout&Configuration -> System Core -> RCC 页面,将 High Speed Clock (HSE) 配置为 Crystal/Ceramic Resonator
  • 配置时钟频率:在Clock Configuration 页面,将PLL Source 选择为 HSE,将System Clock Mux 选择为 PLLCLK,然后在HCLK (MHz) 输入72并回车,将HCLK频率配置为 72 MHz
  • 分配引脚:在Pinout&Configuration页面,将PA11、PA10分别配置为GPIO_Output、TIM1_CH3,并将PA11命名为TRIG
  • 配置TIM1:在Pinout&Configuration -> Timers -> TIM1
  • 打开I²C外设:Pinout&Configuration -> Connectivity -> I2C1,将I2C模式选择为I2C
  • 打开串口2外设:Pinout&Configuration -> Connectivity -> USART2,将Mode选择为Asynchronous
  • 启用float打印:在cubeIDE菜单栏中,Project Properties -> C/C++ Build -> Settings -> Tool Settings -> MCU Settings,勾选Use float with printf ... -nano
注意

默认情况下,sprintf 函数不能打印小数。因此我们需要配置一下编译器,使其能够打印小数

2、代码

  • 引用头文件
  • 因为需要打印输出变量,所以应该引用几个头文件:
C语言
1#include "stdio.h"
2#include "string.h"
3#include "oled.h"
  • 定义变量
C语言
1uint8_t echo_flag = 0; // 捕获标志位
2uint16_t rising_cnt = 0, falling_cnt = 0; // 上升沿、下降沿数值
3char buf_uart[32] = {0}; // 串口发送字符串
4char buf_oled[30] = {0}; // oled显示字符串
  • 初始化oled
C语言
1HAL_Delay(20);
2OLED_Init();
  • 触发测量
  • 将TRIG引脚拉高至少10us后拉低,触发测量
C语言
1// 触发
2HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, 1);
3HAL_Delay(5);
4HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, 0);
  • 打开脉冲捕获
  • 先清零变量
  • __HAL_TIM_SET_CAPTUREPOLARITY 配置为上升沿捕获
  • HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_3) 开始捕获
C语言
1// 清零
2rising_cnt = 0;
3falling_cnt = 0;
4echo_flag = 0;
5__HAL_TIM_SET_COUNTER(&htim1, 0);
6// 开始捕获
7__HAL_TIM_SET_CAPTUREPOLARITY(&htim1, TIM_CHANNEL_3, TIM_INPUTCHANNELPOLARITY_RISING);
8HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_3);
  • 重定义输入捕获中断函数
    • 捕获到边沿时,Timer会自动记录当前的计数值,通过__HAL_TIM_SET_CAPTUREPOLARITY函数即可获取
    • 捕获到上升沿时,立即配置成下降沿捕获,以捕获下降沿
    • 将上升沿、下降沿的值分别保存到变量rising_cnt、falling_cnt
C语言
1void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
2{
3 if (htim == &htim1 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_3)
4 {
5 // 捕获到上升沿
6 if (!echo_flag)
7 {
8 rising_cnt = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_3);
9 echo_flag = 1;
10 __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_3, TIM_INPUTCHANNELPOLARITY_FALLING);
11 }
12 // 捕获到下降沿
13 else
14 {
15 falling_cnt = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_3);
16 echo_flag = 0;
17 __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_3, TIM_INPUTCHANNELPOLARITY_RISING);
18 }
19 }
20}
  • 计算距离
    • 当rising_cnt、falling_cnt都捕获完成时,计算距离
C语言
1// 等待完成
2for (uint16_t i = 0; i < 150; i++)
3{
4 if (rising_cnt != 0 && falling_cnt != 0)
5 {
6 // 计算距离
7 // 定时器每1us计数1次,因此 距离=计数*0.34/2(毫米)
8 float distance = (falling_cnt - rising_cnt) * 0.017;
9 // 构造字符串
10 sprintf(buf_uart, "Distance: %.2f cm\r\n", distance);
11 sprintf(buf_oled, "%.2f cm", distance);
12 break;
13 }
14 HAL_Delay(1);
15}
  • 显示数据
    • 通过oled显示以及串口发送结果
C语言
1// 串口发送数据
2HAL_UART_Transmit(&huart2, (uint8_t *)buf_uart, strlen(buf_uart), 1000);
3// oled显示数据
4OLED_NewFrame();
5OLED_PrintString(32, 0, "Distance", &font16x16, OLED_COLOR_NORMAL);
6OLED_PrintString(32, 26, "NULL", &font16x16, OLED_COLOR_NORMAL); // 未连接显示NULL
7OLED_PrintString(30, 26, buf_oled, &font16x16, OLED_COLOR_NORMAL);
8OLED_ShowFrame();
  • 停止捕获
C语言
1// 停止捕获
2HAL_TIM_IC_Stop_IT(&htim1, TIM_CHANNEL_3);
3HAL_Delay(500);