STM32CubeMX | 39-使用硬件定时器获取超声波模块数据(HC-SR04)

本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的通用定时器外设,捕获超声波模块的距离信号。

本系列教程所编写的驱动源码:https://github.com/Mculover666/HAL_Driver_Lib,好用的话,记得点个Star呀!

1. 准备工作

硬件准备

首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):
STM32CubeMX | 39-使用硬件定时器获取超声波模块数据(HC-SR04)_第1张图片
超声波模块使用HC-SR04,如图:
STM32CubeMX | 39-使用硬件定时器获取超声波模块数据(HC-SR04)_第2张图片
该模块的四个引脚说明如下表:

引脚名 引脚作用
Vcc 5V供电
Trig 触发控制信号输入
Echo 回响信号输出
Gnd

超声波模块测距原理

如图,超声波模块发出超声波信号,如果前方碰到障碍物,则被障碍物反射回来,模块收到回来的超声波后,即可计算出与障碍物的距离:
STM32CubeMX | 39-使用硬件定时器获取超声波模块数据(HC-SR04)_第3张图片

HR-SC04模块工作原理

HR-SC04模块的时序图如下:
STM32CubeMX | 39-使用硬件定时器获取超声波模块数据(HC-SR04)_第4张图片
根据该时序图,可以看出一次测距的流程如下:

1.给超声波模块接入电源和地。
2.给脉冲触发引脚(trig)输入一个长为20us的高电平方波

  3.输入方波后,模块会自动发射8个40KHz的声波,与此同时回波引脚(echo)端的电平会由0变为1;(此时应该启动定时器计时)
  4.当超声波返回被模块接收到时,回波引 脚端的电平会由1变为0;(此时应该停止定时器计数),定时器记下的这个时间即为超声波由发射到返回的总时长。
  5.根据声音在空气中的速度为344米/秒,即可计算出所测的距离。
  • 触发信号由Trig引脚输入,10us的高电平即可触发一次测距
  • 触发后,距离信息由Echo引脚输出,该输出信号的高电平时间与检测距离成比例;

在整个测距过程中,最重要的是Echo引脚输出信号高电平的时间,可以使用STM32硬件定时器得到时长,这个时间为超声波从发射到接收的时长,由声速计算出传播距离,再取1/2,计算出当前距离障碍物的距离。

软件准备

  • 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码。

2.生成MDK工程

选择芯片型号

打开STM32CubeMX,打开MCU选择器:
mark

搜索并选中芯片STM32L431RCT6:
mark

配置时钟源

  • 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;
  • 如果使用默认内部时钟(HSI),这一步可以略过;

这里我都使用外部时钟:
mark

配置GPIO

配置两个普通的GPIO,一个配置为输出模式,用于连接模块的Trig引脚,触发一次测距,这里我配置为PB8,另一个配置为输入模式(浮空),用于连接模块的Echo引脚,接收echo信号,这里我配置为PB9。
STM32CubeMX | 39-使用硬件定时器获取超声波模块数据(HC-SR04)_第5张图片

配置通用定时器TIM2

知识小卡片——STM32L431的定时器

STM32L431xx 系列有 1 个高级定时器(TIM1), 3 个通用定时器(TIM2、TIM15、TIM16),两个基本定时器(TIM6、TIM7),还有两个低功耗定时器(LPTIM1、LPTIM2)。

STM32L431 的通用 TIMx (TIM2、TIM15、TIM16)定时器功能包括:

  • 16 位(TIM15,TIM16)/32 位(TIM2)向上、向下、向上/向下自动装载计数器,注意:
    TIM15、TIM16 只支持向上(递增)计数方式;

  • 16 位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为 1~65535 之间的任
    意数值。

  • 4 个独立通道(TIMx_CH1~4, 其中 TIM15 最多 2 个通道, TIM16 最多 1 个
    通道),这些通道可以用来作为:

    • 输入捕获
    • 输出比较
    • PWM 生成(边缘或中间对齐模式)
    • 单脉冲模式输出
  • 可使用外部信号控制定时器和定时器互连的同步电路。

  • 如下事件发生时产生中断/DMA:

    • 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
    • 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
    • 输入捕获
    • 输出比较

知识小卡片结束啦~

接下来开始配置TIM2,首先选择TIM2,时钟源选择内部时钟:
mark

接下来是对TIM2的参数设置,参照数据手册中的RCC时钟树,TIM2内部时钟来源是PCLK1 = 80Mhz,我们的目的是每1us产生一次中断(1Mhz),所以预分频系数设置为8,自动重载值为10,得到的计时器更新中断频率即为80000000/8/10=1000000Hz=1MHz
STM32CubeMX | 39-使用硬件定时器获取超声波模块数据(HC-SR04)_第6张图片
其余的一些设置保持默认即可,最后开启TIM2中断:
STM32CubeMX | 39-使用硬件定时器获取超声波模块数据(HC-SR04)_第7张图片

配置串口

开发板板载了一个CH340换串口,连接到USART1。

接下来开始配置USART1

配置时钟树

STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz即可:
mark

生成工程设置

STM32CubeMX | 39-使用硬件定时器获取超声波模块数据(HC-SR04)_第8张图片

代码生成设置

最后设置生成独立的初始化文件:
mark

生成代码

点击GENERATE CODE即可生成MDK-V5工程:
mark

3. 在MDK中编写、编译、下载用户代码

1. printf重定向

STM32CubeMX_09 | 重定向printf函数到串口输出的多种方法

2. 添加us级延时函数

  • 一种Cortex-M内核中的精确延时方法

3. 编写驱动

HC_SR04.h

/**
 * @Copyright   (c) 2021,mculover666 All rights reserved
 * @filename    HC_SR04.c
 * @breif       Drive HC_SR04 based on stm32 tim peripheral
 * @changelog   v1.0    mculover666     2021/5/9
 */

#ifndef _HC_SR04_H_
#define _HC_SR04_H_

#include "stm32l4xx.h"
#include "core_delay/core_delay.h"

/* HC-SR04模块控制引脚 */
#define HC_SR04_TRIG_PORT       GPIOB
#define HC_SR04_TRIG_PIN        GPIO_PIN_8
#define HC_SR04_ECHO_PORT       GPIOB
#define HC_SR04_ECHO_PIN        GPIO_PIN_9

/* us级延时函数 */
#define HC_SR04_Delay_Us(n)     CPU_TS_Tmr_Delay_US(n)

/* us级硬件定时器 */
#define HC_SR04_TIM     htim2

void HC_SR04_Init(void);
int HC_SR04_Measure(double *distance_cm);

#endif /* _HC_SR04_H_ */

HC_SR04.c

/**
 * @Copyright   (c) 2021,mculover666 All rights reserved
 * @filename    HC_SR04.c
 * @breif       Drive HC_SR04 based on stm32 tim peripheral
 * @changelog   v1.0    mculover666     2021/5/9
 */

#include "HC_SR04.h"
#include 

extern TIM_HandleTypeDef HC_SR04_TIM;
static uint32_t high_level_time_us;

/**
 * @brief   GPIO and TIM initialization.
 * @param   none
 * @return  none
*/
void HC_SR04_Init(void)
{
     
    // it is initialized in main function
}

/**
 * @brief   Send trig signal.
 * @param   none
 * @return  none
*/
static void HC_SR04_Start(void)
{
     
    /* output high level */
    HAL_GPIO_WritePin(HC_SR04_TRIG_PORT, HC_SR04_TRIG_PIN, GPIO_PIN_SET);
    
    /* maintain high level at least 10us */
    HC_SR04_Delay_Us(10);
    
    /* resume low level */
    HAL_GPIO_WritePin(HC_SR04_TRIG_PORT, HC_SR04_TRIG_PIN, GPIO_PIN_RESET);
}

/**
 * @brief   Measure the high level time of the echo signal.
 * @param   none
 * @return  errcode
 * @retval  0 success
 * @retval -1 fail
*/
int HC_SR04_Measure(double *distance_cm)
{
     
    uint32_t timeout_start, timeout_cur;
    
    HC_SR04_Start();
    
    /* waitting for start of the high level through echo pin */
    timeout_start = HAL_GetTick();
    while (HAL_GPIO_ReadPin(HC_SR04_ECHO_PORT, HC_SR04_ECHO_PIN) == GPIO_PIN_RESET) {
     
        // timeout check code to avoid blocking
        timeout_cur = HAL_GetTick();
        if (timeout_cur - timeout_start > 100) {
     
            //100ms timeout
            return -1;
        }
    }

    /* start the tim and enable the interrupt */
    high_level_time_us = 0;
    HAL_TIM_Base_Start_IT(&HC_SR04_TIM);
    
    /* waitting for end of the high level through echo pin */
    timeout_start = HAL_GetTick();
    while (HAL_GPIO_ReadPin(HC_SR04_ECHO_PORT, HC_SR04_ECHO_PIN) == GPIO_PIN_SET)
    {
     
        // timeout check code to avoid blocking
        timeout_cur = HAL_GetTick();
        if (timeout_cur - timeout_start > 100) {
     
            //100ms timeout
            return -1;
        }
    }
    
    /* stop the tim */
    HAL_TIM_Base_Stop(&HC_SR04_TIM);
    
    /* calc distance in unit cm */
    *distance_cm = (double)(high_level_time_us/1000000.0) * 340.0 / 2.0 *100.0;
    
     return 0;
}

/**
 * @brief   Realize the callback function of TIM.
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* tim_baseHandle)
{
     
	if(tim_baseHandle->Instance == HC_SR04_TIM.Instance)
    {
     
        high_level_time_us++;
    }
}

测试结果

在main.c中包含驱动头文件:

#include 
#include "core_delay.h"
#include "HC_SR04.h"

在main函数中创建变量:

/* USER CODE BEGIN 1 */
double distance_cm;

/* USER CODE END 1 */

编写测试代码:

/* USER CODE BEGIN 2 */
 printf("HC-SR04 Module Test By Mculover666\r\n");
 HC_SR04_Init();

 /* USER CODE END 2 */

 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
     
   /* USER CODE END WHILE */

   /* USER CODE BEGIN 3 */
   if (HC_SR04_Measure(&distance_cm) == 0)
   {
     
       printf("distance:%.2f cm\r\n", distance_cm);
   }
   else
   {
     
       printf("measure fail\r\n");
   }
   
   HAL_Delay(1000);
 }
 /* USER CODE END 3 */

测试结果为:
STM32CubeMX | 39-使用硬件定时器获取超声波模块数据(HC-SR04)_第9张图片
更多精彩文章及资源,请关注我的微信公众号:『mculover666』。

mark

你可能感兴趣的:(#,STM32CubeMX,实战,STM32Cubemx,HAL,超声波,HC_SR04)