STM32CubeMX-输入捕获读取超声波模块数据

Posted JeckXu666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32CubeMX-输入捕获读取超声波模块数据相关的知识,希望对你有一定的参考价值。

STM32CubeMX-输入捕获读取超声波模块数据

本章内容使用STM32CubeMX软件配置STM32F407ZGT6定时器输入捕获功能,读取超声波返回的距离电平信息,转换成实际的距离值

教程包含通用步骤以及专用步骤,其中,通用步骤为STM32CubeMX配置其他外设工程的通用操作,STM32CubeMX系列教程基本通用,专用操作则是针对当前工程进行的配置

一、初始准备

1.硬件平台

主控使用正点原子STM32F4探索者:

超声波模块HC-SR04:

HC-SR04是一款经典的超声波模块,其有四个引脚,分别对应着VCC(电源引脚)、GND(电源引脚),TRIG(触发控制信号输入端)、ECHO(回响信号返回端);使用时我们使用单片机对TRIG引脚输入一个电平,接着模块内部产生连续脉冲发射出去,当发射的超声波撞到障碍物时会被反弹回来,反弹的波再被超声波模块接受到,然后ECHO产生一段高电平,高电平维持的时间就是超声波模块到障碍物一个来回的时间,所以我们用输入捕获来读取电平的持续时间就可以解算出超声波到障碍物的距离,模块电平示意图如下:

想要进一步了解超声波模块的同学可以参考这一篇文章:HC-SR04超声波测距模块的原理介绍与代码实现

2.软件平台

STM32CubeMX软件平台 V6.2.1

Keil5软件平台 V5.32

STM32CubeProgrammer下载平台

3.原理图接线

因为使用到定时器,所以将超声波模块和开发板的引脚按照如下接线,PA7普通引脚输入触发电平,PA6使用定时器3的通道1做输入捕获

二、操作步骤

1.CubeMX生成初始化代码

1.1 建立工程(通用步骤)

  • 芯片选择

打开cube软件,选择从芯片来创建工程,一般开发都是使用这个来开发,有的时候也可能使用另外两个,但不多,第二个基于ST提供的开发板创建工程,针对性高,第三个则选择ST提供的例程来创建工程

F4探索者的主控为STM32F407ZGT6,所以在搜索框找到STM32F407ZG后点击具体芯片,再开始工程

  • 配置时钟源

我们点开SystemCore(系统内核设置),再点击RCC配置HSE和LSE时钟源,这里我都选择使用外部时钟,配置后,我们可以看到右边芯片引脚分配图的两个时钟源引脚点亮,表示时钟配置为外部源

  • 配置时钟树

我们进入ClockConfiguration配置时钟树,使时钟的输入路径和大小符合我们预期,探索者的晶振和时钟倍频如下

一般配置正确时颜色蓝白为主,配置错误时则会出现紫色,提示我们要修改值

具体时钟树的了解可以看我很久之前的文章,有做一些分析

CSDN文章链接-时钟树分析

1.2 开启串口

开启串口通信,方便我们通过上位机发送数据进行调试,不熟悉配置的话可以参考我以前的文章:

STM32CubeMX-串口中断实验

配置后引脚图如下:

1.3 开启输入捕获

通过输入捕获计算超声波模块测量距离的原理很简单,简单来讲就是开启输入捕获和定时器计数,设置上升沿捕获,当上升沿到来时会进入定时器中断,此时我们清空定时器的计数值为0,然后设置设置下降沿捕获,当下降沿到来时,就会再次进入中断,此时再次读取计数值,即可得出高电平维持的时间长度,带入计算得出超声波距离值,配置步骤如下:

点击TIM3,选择内部时钟(Internal Clock),配置通道1为直接输入捕获模式(Input Capture direct mode)

配置输入捕获的详细参数,首先先配置定时器为72MHz分配,即1us计数一次,每次计数值加1,计数上限设为最大65535,配置如下:

然后配置上升沿触发(Rising Edge),不分频(No division),滤波器的值为0不滤波

滤波器就是输入多个边沿才算一次触发,比如设置为4,则输入4次上升沿才会触发中断,一定程度上可以滤除抖动,这里我们设置为0就行

配置完成后别忘了在中断管理(NVIC)里面使能定时器3的中断,配置中断优先级

1.4 配置GPIO

配置PA7为推挽输出,用于产生超声波的触发信号,基本操作,不会配置可参考我之前的文章:STM32CubeMX-流水灯实战,配置后引脚图如下:

1.5 生成代码(通用步骤)

点击进入Project Manager 配置生成工程的名字,存储路径**(不要有中文)**以及编译器,这里我们选MDK-ARM(Keil被收购后改名)

配置生成选项,主要为下面三大块,第一个我们选择只拷贝必要的库,第二个选择为每个外设生成.c和.h文件,保存之前的用户代码,以及删除之前的生成代码,第三个不选择

PS:用户代码段是一下注释之间的代码,只有原始的用户代码段注释才有效,用户自己添加的无效

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

最后点击生成代码

2.编写代码

在文件目录创建Hardware文件夹用于存放模块外设程序,添加hcsr04.c和.h文件

在MDK工程内添加文件夹和文件

别忘了添加Hardware包含路径

到此工程配置好了,下一步我们先将printf重映射到串口1,在uart.c用户代码段插入如下代码

/* USER CODE BEGIN 0 */
#define USE_PRINT
#ifdef USE_PRINT
//编译器不使用MicroLib库
#pragma import(__use_no_semihosting)
//定义 _sys_exit() 避免使用半主机
void _sys_exit(int x)
{
    x = x;
}
struct __FILE
{
    int handle;
};
FILE __stdout;
//重映射fputc
int fputc(int ch, FILE *stream)
{
    //判断串口是否发送完成 
	  //不同芯片的串口标志位不一定相同,具体查手册
    while((USART1->SR & 0X40) == 0);
    //如果串口已经发送完成,发送下一个字符
    USART1->DR = (uint8_t) ch;
    return ch;
}
#endif
/* USER CODE END 0 */

然后在usart.h加入printf的库

#include <stdio.h>

我们在hcsr04.h文件插入如下代码:代码注释都写在了里面

#ifndef _HCSR04_H
#define _HCSR04_H

#include "tim.h"
//配置使用的定时器和捕获通道
#define HCSR04_TIM 		 		  TIM3
#define HCSR04_TIM_HAL 			htim3
#define HCSR04_TIM_CHANNEL  TIM_CHANNEL_1
//声明一个状态枚举常量
typedef enum Run_State
{
	TRIG_WAIT = 0,
	RISING,
	FALLING,
	OVER
}Run_State;
//声明结构体方便传递使用数据
typedef struct 
{
	Run_State STATE;
	int buf[2];
	float len;
}HCSR04;
//声明一下回调函数
extern void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);
#endif

然后我们在hcsr04.c文件插入如下代码

#include "hcsr04.h"
//声明一个模块的数据结构体
HCSR04 hcsr04;
//重新编写输入捕获回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	
	if(HCSR04_TIM == htim->Instance)
	{
		//如果上升沿检测
		if(hcsr04.STATE == RISING)
		{
			//设置定时器CNT为0
			__HAL_TIM_SET_COUNTER(&HCSR04_TIM_HAL,0);
		  //读取上升沿时的CNT值到buf
			hcsr04.buf[0] = __HAL_TIM_GetCounter(&HCSR04_TIM_HAL);
			//设置下一个捕获为下降沿
			__HAL_TIM_SET_CAPTUREPOLARITY(&HCSR04_TIM_HAL,HCSR04_TIM_CHANNEL,TIM_ICPOLARITY_FALLING); 
			//改变运行模式
			hcsr04.STATE=FALLING;
		}else if(hcsr04.STATE == FALLING)//如果下降沿检测
		{
			//获取当前的CNT到buf2,这样高电平维持的时间长度就记录了
			hcsr04.buf[1] = __HAL_TIM_GetCounter(&HCSR04_TIM_HAL);
			//将运行标志设置为完成
			hcsr04.STATE=OVER;  
		}
	}
	
}

主函数循环体插入如下代码

  /* USER CODE BEGIN WHILE */
	printf("System Init Ok! jeck666!\\r\\n"); //串口发送数据
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,0);
	while (1)
	{
			/* USER CODE END WHILE */

			/* USER CODE BEGIN 3 */
		if(hcsr04.STATE == TRIG_WAIT)
		{
			//给HCSR04一个1ms触发电平
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,1);
			HAL_Delay(1);
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,0);
			//启动上升沿捕获
			__HAL_TIM_SET_CAPTUREPOLARITY(&HCSR04_TIM_HAL, HCSR04_TIM_CHANNEL, TIM_INPUTCHANNELPOLARITY_RISING);
			//启动输入捕获
			HAL_TIM_IC_Start_IT(&HCSR04_TIM_HAL, HCSR04_TIM_CHANNEL);	
			//设置上升沿捕获
			hcsr04.STATE = RISING;
		}
		
		if(hcsr04.STATE == OVER)//判断电平捕获结束
		{
			//计算高电平时间差值代表时间维持的长度,因为定时器设置1us自加一次,所以时长单位为us
			//乘以0.017的是因为声波速度为340m/s,而1s=1000000us,且技术一次距离是一个来回
			//要除以2,此处单位为cm
			hcsr04.len = (float)(hcsr04.buf[1]- hcsr04.buf[0])*0.017; 
			//刷新状态,使可以进行下一次触发电平
			hcsr04.STATE = TRIG_WAIT;
			//发送数据到上位机
			printf("HCSR04_Len: %.3f cm \\r\\n",hcsr04.len);
			//延时1s
			HAL_Delay(1000);
		}
	}
/* USER CODE END 3 */

当然main.c文件头部别忘了添加头文件和变量声明

/* USER CODE BEGIN Includes */
#include "hcsr04.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
extern HCSR04 hcsr04;
/* USER CODE END PTD */

到此代码编写完毕

3.程序下载(通用步骤)

程序下载我一般用两种方式:

第一种是使用MDK自带的下载环境下载程序,我们给单片机连接ST-Link后配置下载,点击魔术棒,选择debug

选择ST-link后,点击setting

添加对应F4的Flash

keil界面点击下载

第二种是使用Stm32Programmer下载软件,该下载软件下载方式多,下载快,下面我使用st-link下载

打开软件,点击connect左边选择stlink后再点击connect连接下载器

点击open file,找到工程路径下MDK文件夹下工程生成的hex文件

之后点击downlod下载,下载结果如下

三、实验现象

可以看到串口接受到的距离在变化(因为我在移动超声波模块)

以上是关于STM32CubeMX-输入捕获读取超声波模块数据的主要内容,如果未能解决你的问题,请参考以下文章

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

HAL实现多个超声波测距(输入捕获实现)

HAL实现多个超声波测距(输入捕获实现)

(STM32CubeMX)超声波模块测距传感器学习笔记

智能车 有来有往 单收单发超声波模组 STM32CubeMx HAL库

基于STM32F103C8T6(HAL库)的HC-SR501红外人体传感及HC-SR04超声波测距