【Timer】超声波测距
下载例程代码:
HC-SR04 超声波模块简介
HC-SR04 工作原理
模块有2个超声波换能器(如图所示),一个发出声波,另一个接收物体反射回来的声波,这中间所经过的时间即声波传播的时间,再结合声速就能计算出:
- 距离 = 声速 * 时间 ÷ 2
如何使用HC-SR04模块
模块具有4个引脚,除了电源外,有TRIG、ECHO两个引脚需要操作:
- 首先,向TRIG引脚发送一个高电平脉冲,来触发模块输出声波
- 记录ECHO引脚输出高电平的时间,即声波的飞行时间
- 距离 = 声速(340m/s) * 声波的飞行时间 ÷ 2
如何使用例程
下载程序,并连接硬件,即可看到效果
硬件连接
- 使用配套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
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 else14 {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); // 未连接显示NULL7OLED_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);