IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 学习并使用HC-SR04超声波测距模块+STM32 -> 正文阅读

[嵌入式]学习并使用HC-SR04超声波测距模块+STM32

**

超声波测距原理

**
参考博客:
https://blog.csdn.net/qq_40734815/article/details/105992801
https://blog.csdn.net/qq_43743762/article/details/103217402

利用HC-SR04超声波测距模块可以实现比较精确的直线测距,其测距原理图如下:在这里插入图片描述
HC-SR04的一端发出超声波,接触到反射物后反射,被另一个端口接收到,所以只要知道发射和接收的时间差,就可以根据声波传播的速率算出HC-SR04和反射物直接的距离。
所以实现超声波测距就需要俩个条件:

(1) 发射和接收的时间差

(2) 超声波传输的速率

HC-SR04工作原理

HC-SR04模块的电气参数如示:

在这里插入图片描述

HC-SR04模块的实物图

在这里插入图片描述

通过了解超声波测距原理和工作原理可以得知测距原理
(1)超声波模块的TRIG引脚给最少10us高电平信号,触发测距。
(2)模块自动发送8个40khz的方波,自动检测是否有信号返回;
(3)有信号返回, 通过ECHO口输出一个高电平, 高电平持续的时间就是超声波从发射到返回的时间。
测试距离=(高电平时间*声速(340M/S))/2。
因此只要检测ECHO连接的单片机引脚高电平持续时间 便可以得到超声波从发射到返回的时间。
在这里插入图片描述

检测时间方法

这里使用的是定时器输入捕获功能,网上还有其他方法,参考方法在参考博客中有写到。
采用定时器的输入捕获功能,直接将ECHO引脚接到STM32具有输入捕获功能的管脚,配置好定时器输入捕获功能,就可以读取到高电平时间。

(1)超声波模块的TRIG引脚给最少10us高电平信号,触发测距。

void HCSR04_TRIG_Send(void)
{
    TRIG = 1;
    delay_us(20);     //延时20US
    TRIG = 0;
}

void HCSR04_TRIG_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;           //PC1接TRIG
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    //设为推挽输出模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                 
    GPIO_Init(GPIOC, &GPIO_InitStructure);              //初始化外设GPIO
}

(2)模块自动发送8个40khz的方波,自动检测是否有信号返回;

(3)有信号返回, 通过ECHO口输出一个高电平, 高电平持续的时间就是超声波从发射到返回的时间。

float Get_Distance(void)
{
    float distance = 0;u32 temp = 0;
    HCSR04_TRIG_Send();
    if(TIM5CH1_CAPTURE_STA&0X80)//成功捕获到了一次上升沿
    {
        temp=TIM5CH1_CAPTURE_STA&0X3F;
        temp*=65536;//溢出时间总和
        temp+=TIM5CH1_CAPTURE_VAL;//得到总的高电平时间
        distance = (float)temp * 170 / 10000;   
        TIM5CH1_CAPTURE_STA=0;//开启下一次捕获
    }
    return distance;
}

定时器timer.c配置

#include "timer.h"
#include "usart.h"
TIM_ICInitTypeDef  TIM5_ICInitStructure;

void TIM5_Cap_Init(u16 arr,u16 psc)
{	 
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);	//使能TIM5时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA时钟
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;     //PA0 清除之前设置  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;  //PA0 下拉输入  
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);	
	
	//初始化定时器5 TIM5	 
	TIM_TimeBaseStructure.TIM_Period = arr;     //设定计数器自动重装值 
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 	//预分频器   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  
	//初始化TIM5输入捕获参数
	TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //通道1
  TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
  TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
  TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置输入分频,不分频 
  TIM5_ICInitStructure.TIM_ICFilter = 12;//滤波系数为5
  TIM_ICInit(TIM5, &TIM5_ICInitStructure);
	
	//中断分组初始化
	NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;  //TIM5中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级2级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级0级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 
	
	TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断 ,允许CC1IE捕获中断	
	
  TIM_Cmd(TIM5,ENABLE ); 	//使能定时器5

}




u8  TIM5CH1_CAPTURE_STA=0;	//输入捕获状态		    				
u16	TIM5CH1_CAPTURE_VAL;	//输入捕获值
 
//定时器5中断服务程序	 
void TIM5_IRQHandler(void)
{ 

 	if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获	
	{	  
		if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
		 
		{	    
			if(TIM5CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
			{
				if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
				{
					TIM5CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
					TIM5CH1_CAPTURE_VAL=0XFFFF;
				}else TIM5CH1_CAPTURE_STA++;
			}	 
		}
	if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//捕获1发生捕获事件
		{	
			if(TIM5CH1_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
			{	  			
				TIM5CH1_CAPTURE_STA|=0X80;		//标记成功捕获到一次上升沿
				TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5);
		   		TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
			}else  								//还未开始,第一次捕获上升沿
			{
				TIM5CH1_CAPTURE_STA=0;			//清空
				TIM5CH1_CAPTURE_VAL=0;
	 			TIM_SetCounter(TIM5,0);
				TIM5CH1_CAPTURE_STA|=0X40;		//标记捕获到了上升沿
		   		TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);		//CC1P=1 设置为下降沿捕获
			}		    
		}			     	    					   
 	}
 
    TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
 
}

定时器timer.h配置

#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"

void TIM5_Cap_Init(u16 arr,u16 psc);
#endif

hcsr04.c配置

#include "hcsr04.h"


extern u8  TIM5CH1_CAPTURE_STA;		//输入捕获状态		    				
extern u16	TIM5CH1_CAPTURE_VAL;	//输入捕获值	

//超声波模块的TRIG引脚给最少10us高电平信号,触发测距。
//模块自动发送8个40khz的方波,自动检测是否有信号返回;
//有信号返回, 通过ECHO口输出一个高电平, 高电平持续的时间就是超声波从发射到返回的时间。
//(PC1)-->TRIG
//(PA0)-->ECHO

void HCSR04_TRIG_Send(void)
{
    TRIG = 1;
    delay_us(20);     //延时20US
    TRIG = 0;
}

void HCSR04_TRIG_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;           //PC1接TRIG
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    //设为推挽输出模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                 
    GPIO_Init(GPIOC, &GPIO_InitStructure);              //初始化外设GPIO
}

float Get_Distance(void)
{
    float distance = 0;u32 temp = 0;
    HCSR04_TRIG_Send();
    if(TIM5CH1_CAPTURE_STA&0X80)//成功捕获到了一次上升沿
    {
        temp=TIM5CH1_CAPTURE_STA&0X3F;
        temp*=65536;//溢出时间总和
        temp+=TIM5CH1_CAPTURE_VAL;//得到总的高电平时间
        distance = (float)temp * 170 / 10000;   
        TIM5CH1_CAPTURE_STA=0;//开启下一次捕获
    }
    return distance;
}

hcsr04.h配置

#ifndef __HCSR04_H
#define __HCSR04_H
#include "sys.h"
#include "delay.h"

#define TRIG  PCout(1) 
//#define ECHO  PAin(0)

void HCSR04_TRIG_Send(void);
void HCSR04_TRIG_Init(void);
float Get_Distance(void);


#endif

main.c配置

#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
#include "hcsr04.h"
float distance = 0;
int main(void)
{		
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	delay_init();	    	 //延时函数初始化
	HCSR04_TRIG_Init();  //HCSR04超声波模块初始化
	uart1_init(115200);	 //串口1初始化为115200
	TIM5_Cap_Init(0XFFFF,72-1);	//以1Mhz的频率计数 
	printf("初始化成功");
  while(1)
	{
         delay_ms(500);
         distance = Get_Distance();
         printf("distance: %.2f cm\r\n",distance);
	}	 
} 

其中遇到的问题:

超声波测距接线,串口打印数据为0.00cm
解决:检查接线是否存在问题,(PC1)–>TRIG,(PA0)–>ECHO,确认无误,检查stm32,超声波模块供电是否存在问题,都没问题最后检查代码是否存在问题,全部确认完了,仍然检测不出距离,最后换了杜邦线重新接线就好了。检测精度还是可以的。
在这里插入图片描述

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-07-24 11:39:31  更:2021-07-24 11:41:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 18:57:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码