【STM32小案例02】用STM32F103ZET6使用HC-SR04超声波模块测距

本文将简单介绍如何通过HC-SR04超声波模块实现测距功能,希望对大家的学习有所帮助。

1.所需硬件

正点原子战舰V3开发板 X1

HC-SR04超声波模块 X1

杜邦线 若干

2.HC-SR04超声波模块介绍

在这里插入图片描述

上图为HC-SR04超声波模块实物图,从图中可以看到,该模块一共有四个引脚,分别是VCC、GND、Trig、Echo,关于该模块这里不做过多的详细介绍,感兴趣的同学可以自行百度了解,下面将简单介绍该模块的原理及使用方法。根据时序图可知,首先给TRIG引脚至少10us的高电平信号触发测距,模块自动发送8个40KHz的方波,自动检测是否有信号返回,若有信号返回,通过Echo引脚输出一个高电平,高电平的持续时间就是超声波从发射到返回的时间,
测 量 距 离 = ( 高 电 平 的 持 续 时 间 ∗ 声 速 ( 340 m / s ) ) / 2 测量距离 =(高电平的持续时间 * 声速(340m/s))/ 2 =340m/s/2
时序图如下
在这里插入图片描述

该模块使用较简单,难点在于程序的编写。

3.硬件连接

Vcc3.3/5V
GNDGND
TrigPB5
EchoPB6

4.程序编写思路是

1、配置好使用到的GPIO以及定时器;
2、给模块TRIG端口发送大于10us的高电平信号,当收、收到ECHO回响信号是,打开定时器开始定时;
3、当回响信号消失,关闭定时器;
4、通过定时器定时时间来确定距离。

5.程序代码

HCSR04.c源文件

#include "hcsr04.h"
#include "sys.h"
#include "delay.h"

#define HCSR04_PORT     GPIOB
#define HCSR04_CLK      RCC_APB2Periph_GPIOB
#define HCSR04_TRIG     GPIO_Pin_5
#define HCSR04_ECHO     GPIO_Pin_6
 
#define TRIG_Send  PBout(5) 
#define ECHO_Reci  PBin(6)

void Delay_Ms(uint16_t time);
void Delay_Us(uint16_t time);
void hcsr04_NVIC();


u16 msHcCount = 0;      //ms计数


void Hcsr04Init()
{  
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;     //生成用于定时器设置的结构体
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(HCSR04_CLK, ENABLE);
     
        //IO初始化
    GPIO_InitStructure.GPIO_Pin =HCSR04_TRIG;           //发送电平引脚
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    //推挽输出
    GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);
    GPIO_ResetBits(HCSR04_PORT,HCSR04_TRIG);
     
    GPIO_InitStructure.GPIO_Pin =   HCSR04_ECHO;        //返回电平引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   //浮空输入
    GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);  
	  GPIO_ResetBits(HCSR04_PORT,HCSR04_ECHO);	
 
		//定时器初始化 使用基本定时器TIM6
	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);   //使能对应RCC时钟
		//配置定时器基础结构体
		TIM_DeInit(TIM2);
		TIM_TimeBaseStructure.TIM_Period = (1000-1);      //设置在下一个更新事件装入活动的自动重装载寄存器周期的值         计数到1000为1ms
		TIM_TimeBaseStructure.TIM_Prescaler =(72-1);      //设置用来作为TIMx时钟频率除数的预分频值  1M的计数频率 1US计数
		TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;         //不分频
		TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;   //TIM向上计数模式
		TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);   //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位		 
		
		TIM_ClearFlag(TIM6, TIM_FLAG_Update);       //清除更新中断,免得一打开中断立即产生中断
		TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);    //打开定时器更新中断
		hcsr04_NVIC();
		TIM_Cmd(TIM6,DISABLE);     
}
 
 
//tips:static函数的作用域仅限于定义它的源文件内,所以不需要在头文件里声明
static void OpenTimerForHc()        //打开定时器
{
	TIM_SetCounter(TIM6,0);  //清除计数
	msHcCount = 0;
	TIM_Cmd(TIM6, ENABLE);   //使能TIMx外设
}
 
static void CloseTimerForHc()        //关闭定时器
{
	TIM_Cmd(TIM6, DISABLE);  //使能TIMx外设
}
 
 
//NVIC配置
void hcsr04_NVIC()
{
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

	NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;             //选择串口1中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;   //抢占式中断优先级设置为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;          //响应式中断优先级设置为1
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //使能中断
	NVIC_Init(&NVIC_InitStructure);
}
 
 
//定时器6中断服务程序
void TIM6_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
	{
			TIM_ClearITPendingBit(TIM6, TIM_IT_Update  );   //清除TIMx更新中断标志 
			msHcCount++;
	}
}
 
 
//获取定时器时间
u32 GetEchoTimer(void)
{
	u32 t = 0;
	t = msHcCount*1000;    //得到MS
	t += TIM_GetCounter(TIM6);    //得到US
	  TIM6->CNT = 0;     //将TIM2计数寄存器的计数值清零
			Delay_Ms(50);
	return t;
}
 
 
//一次获取超声波测距数据 两次测距之间需要相隔一段时间,隔断回响信号
//为了消除余震的影响,取五次数据的平均值进行加权滤波。
float Hcsr04GetLength(void )
{
	u32 t = 0;
	int i = 0;
	float lengthTemp = 0;
	float sum = 0;
	while(i!=5)
	{
	TRIG_Send = 1;      //发送口高电平输出
	Delay_Us(20);
	TRIG_Send = 0;
	while(ECHO_Reci == 0);      //等待接收口高电平输出
		OpenTimerForHc();        //打开定时器
		i = i + 1;
		while(ECHO_Reci == 1);
		CloseTimerForHc();        //关闭定时器
		t = GetEchoTimer();        //获取时间,分辨率为1US
		lengthTemp = ((float)t/58.0);//cm
		sum = lengthTemp + sum ;
	
	}
	lengthTemp = sum/5.0;
	return lengthTemp;
}
 
 
/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
** 函数名称: Delay_Ms_Ms
** 功能描述: 延时1MS (可通过仿真来判断他的准确度)			
** 参数描述:time (ms) 注意time<65535
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
void Delay_Ms(uint16_t time)  //延时函数
{ 
	uint16_t i,j;
	for(i=0;i<time;i++)
  		for(j=0;j<10260;j++);
}
/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
** 函数名称: Delay_Ms_Us
** 功能描述: 延时1us (可通过仿真来判断他的准确度)
** 参数描述:time (us) 注意time<65535				 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
void Delay_Us(uint16_t time)  //延时函数
{ 
	uint16_t i,j;
	for(i=0;i<time;i++)
  		for(j=0;j<9;j++);
}

main.c源文件

#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "led.h"

int main()
{		
			float length;
			delay_init();	    	 //延时函数初始化	  
			NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
			uart_init(115200);	 //串口初始化为115200

	
			while(1)
			{
					delay_ms(1000);
					printf("串口初始化成功!\r\n");

					Hcsr04Init();	
					printf("超声波初始化成功!\r\n");      //测试程序是否卡在下面两句上面

					length = Hcsr04GetLength();
					printf("距离为:%.3f\r\n",length);
			}
			return 0;
}	

5.总结

通过本次的项目,我们对STM32的定时器的应用有一定了解,在学习STM32单片机的过程中自己可以边学边做项目,这样会提高自己的动手能力,也会巩固理论知识,本篇博客简单介绍了利用超声波模块测距,如需进一步了解可自行求助度娘,希望对大家的学习有所帮助!

程序源码已上传,如有需要请自行下载

如有错误,欢迎指正!
部分内容来源于网络,如有侵权请告知!

步骤如下: 1.连接硬件:将HC-SR04的VCC接到STM32F103ZET6的5V,GND接到GND,Trig接到STM32F103ZET6的PB10,Echo接到STM32F103ZET6的PB11。 2.配置STM32F103ZET6的GPIO:将PB10配置为输出模式,PB11配置为输入模式。 3.编写程序:使用定时器来生成10us的高电平,然后将Trig引脚拉高,等待10us后拉低。然后等待Echo引脚变高,开始计时,直到Echo引脚变低,停止计时。根据计时器的值计算距离并输出到串口。 下面是代码示例: ``` #include "stm32f10x.h" #include "stdio.h" #define TRIG_PIN GPIO_Pin_10 #define ECHO_PIN GPIO_Pin_11 #define TRIG_PORT GPIOB #define ECHO_PORT GPIOB void TIM3_init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); TIM_TimeBaseInitStruct.TIM_Prescaler = (SystemCoreClock/1000000) - 1; //1us per tick TIM_TimeBaseInitStruct.TIM_Period = 0xFFFF; TIM_TimeBaseInitStruct.TIM_ClockDivision = 0; TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct); TIM_Cmd(TIM3, ENABLE); } void delay_us(uint32_t us) { TIM_SetCounter(TIM3, 0); while(TIM_GetCounter(TIM3) < us); } float get_distance(void) { uint32_t time_start, time_end; float distance; GPIO_ResetBits(TRIG_PORT, TRIG_PIN); delay_us(2); GPIO_SetBits(TRIG_PORT, TRIG_PIN); delay_us(10); GPIO_ResetBits(TRIG_PORT, TRIG_PIN); while(GPIO_ReadInputDataBit(ECHO_PORT, ECHO_PIN) == RESET); time_start = TIM_GetCounter(TIM3); while(GPIO_ReadInputDataBit(ECHO_PORT, ECHO_PIN) == SET); time_end = TIM_GetCounter(TIM3); if(time_end > time_start) { distance = (float)(time_end - time_start) * 0.017; //calculate distance in cm } else { distance = 0; } return distance; } int main(void) { USART_InitTypeDef USART_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO | RCC_APB2Periph_USART1, ENABLE); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = TRIG_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(TRIG_PORT, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = ECHO_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(ECHO_PORT, &GPIO_InitStruct); USART_InitStruct.USART_BaudRate = 115200; USART_InitStruct.USART_WordLength = USART_WordLength_8b; USART_InitStruct.USART_StopBits = USART_StopBits_1; USART_InitStruct.USART_Parity = USART_Parity_No; USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStruct.USART_Mode = USART_Mode_Tx; USART_Init(USART1, &USART_InitStruct); USART_Cmd(USART1, ENABLE); TIM3_init(); while(1) { float distance = get_distance(); printf("Distance: %.2f cm\n", distance); delay_us(500000); } } ```
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值