STM32_超声波测距

超声波测距

  • 超声波测距原理
  • 超声波模块说明书
  • 超声波注意事项
  • HMI串口屏
  • 代码解析
  • 测距结果

超声波测距原理

利用声音测距,声音在空气中的速度是340m/s(15℃)
当声音传播时,若遇到障碍物时,就会被反弹回来,通过计时反弹回来的时间就可以计算出从发射端到障碍物的距离

S=v*(t/2)

超声波模块说明书

在单片机中借助超声波模块实现测距功能,通过串口调试助手或者显示屏进行显示反馈

STM32_超声波测距_第1张图片
HC-SR04超声波模块测量范围为2cm-400cm,精度可达3mm。

引脚功能:
VCC引脚:5V电源。
TRIG引脚:触发信号引脚,单片机给超声波模块一个信号,超声波模块就会工作。
ECHO引脚:回响信号引脚,当超声波模块已经测量距离成功后,通过该引脚告诉单片机当前超声波传输的时间。
GND:信号地。

模块工作原理:
STM32_超声波测距_第2张图片
1、起初Trig和Echo两个引脚都处于低电平状态;
2、然后将TRIG引脚拉高至少10微秒以上再拉低,让超声波模块开始工作;
3、此时模块内部会自动发送8个40KHz的方波作为起始信号;
4、在起始信号发射的同时ECHO引脚就会被拉高,如果有信号返回,ECHO引脚会自动检测到反射回来的信号,并被拉低,那么ECHO引脚的高电平持续时间就是波从发射到返回的时间。
测出的距离即为:
S=(ECHO引脚高电平持续的时间*340m/s)/2
S=t x 170
因为单片机的定时器一般使用us进行计时,所以公式可以转换为
S= t x 170 x 10-6=t/58 (cm)

超声波注意事项

1、不同的温度,声音的传播速度是不一样的,有以下公式:
STM32_超声波测距_第3张图片
一般取15℃时,v=340m/s,可以在测量距离时加入温湿度传感器测量环境温度,带入公式,动态计算声速值,使测量更加精确;
2、测距时,要求被测物体尽量平整,且面积不小于0.5平方米,否则会影响测量结果;
2、超声波的测量误差为3mm,可根据3mm的测距求出测量时间,求算如下:

STM32_超声波测距_第4张图片
这里使用这个公式测量距离,当延时9us,即ECHO持续高电平9us,测得的距离为3mm,这里只是近似值。

在这里插入图片描述

HMI串口屏

本实验使用的串口屏来自淘晶驰商家
型号为 :TJC3224K024_011R
320x240分辨率
_011:硬件版本号
K0系列
2.4寸屏幕(斜对角线的距离)
触摸屏说明:N=无触摸 R=电阻触摸屏 C=电容触摸屏
STM32_超声波测距_第5张图片
相对LCD(液晶屏)和OLED(有机发光显示器),串口屏幕更加简单,只有4根线,利用串口通信,而且屏幕丰富多彩,可以彩屏显示。

LCD:32根引脚,使用IIC或者SPI通信
OLED:显示颜色有限,最多两种颜色
STM32_超声波测距_第6张图片
相对其他商家,陶晶驰的串口屏使用起来比较简单,只需遵守它的串口屏协议就可以。
STM32_超声波测距_第7张图片
这是它的语法规则,只需要在控件后面加三个0xff字节作为结束符,没有其他规则的要求。

本实验设计的串口屏界面:
STM32_超声波测距_第8张图片
由于这款软件是中文的,所以不进行过多的介绍,直接点击帮助文档进行学习,官网关于这款软件的使用介绍的佷详细,只是与单片机的通信介绍的很少,不过官方文档给出了明确的说明,这属于高级应用,不过多介绍。
STM32_超声波测距_第9张图片

HMI串口屏注意事项:
1、当往屏幕添加了字库,字库才会有文字或者数字显示,字而库的添加最好以指定字库的形式添加,不要添加所有字库,否则下载到屏幕中会很慢。
2、串口屏数据传输支持两种方式下载:①串口号搜索下载②;SD内存卡下载。
3、关于屏幕中图片上传的格式要求是:png图片,且根据屏幕的尺寸大小它会对图片大小有限制,小一点的图片才可以加载进去。

关于控件的学习,可以直接下载USART HMI这款软件进行学习,点击软件里面的帮助就可以学习或者直接登入陶晶驰官网学习。

最后关于一些容易出错的地方做一些说明:
1、当串口屏和STM32开发板连接时,要把串口1的跳线帽拔掉,PA9和PA10分别接串口屏TX和RX引脚,野火指南针的串口1默认和CH340芯片连接。
拔掉跳线帽后,第一步可以让串口屏和CH340芯片的TX和RX引脚交叉连接,将USART HMI上位机中设计好的屏幕界面下载到串口屏中,下载好程序后,再拔掉连接PA9和PA10,与芯片通信。

2、CH340所起的作用就是一座桥梁,它可以将TTL电平转换为USB电平,一般单片机输出的都是TTL电平,那么如果想要单片机与电脑通信就需要USB接口,以前有使用DB9接口与电脑通信,但是现在用的很少,主要是USB更加小巧,方便通信。
STM32_超声波测距_第10张图片
3、串口屏的波特率一定要和所写程序的波特率设置成一样的,才可以进行通信,虽然上位机可以更改屏幕波特率,但是屏幕波特率可能出厂时已经固定好了,即使在上位机强制更改,通信的时候也无法正常通信,不知道屏幕的波特率,下载屏幕界面可以自动搜索,上面会有显示。

STM32_超声波测距_第11张图片

虽然有很多东西被淘汰了,主要是因为时代在进步,科技再不断更新迭代,但是新东西的诞生源于旧物件,旧物件涉及到的知识永远不会淘汰,除非一种知识被证明是错误的,那么错的东西肯定会被淘汰,这就像我们现在仍在学习几千年牛顿总结出的各种定律,如果这些定律被证明是错的,那么我们就不需要再学习了,今天需要学习的知识是太多了,学习途径也有很多,在知识的海洋中遨游,我们一定要站在巨人的肩膀上。

使用串口调试助手也可以显示数据,当拔下跳线帽时,串口调试助手就不可以使用了,或者使用其他串口来连接串口调试助手也可以,这里也要波特率设置成一样才可以通信

代码解析

hc-sr04.h
超声波模块的初始化是根据时序图进行编程

#ifndef  __HCSR04_H
#define  __HCSR04_H

#include "stm32f10x.h"

//超声波模块引脚
#define	TRIG_PORT      GPIOC		       
#define	ECHO_PORT      GPIOC		
#define	TRIG_PIN       GPIO_Pin_8   	//TRIG-发送引脚PC8       
#define	ECHO_PIN       GPIO_Pin_9		//ECHO-接收引脚PC9    

//超声波模块初始化
void sr04_init(void);

//超声波模块测距
int32_t sr04_get_distance(void);

#endif

hc-sr04.c

#include "hc-sr04.h"
#include "stm32f10x.h"
#include "delay.h"

uint32_t d;      //计算出的距离

//超声波初始化函数
void sr04_init(void)
{	
	GPIO_InitTypeDef GPIO_InitStructure;
		 	       
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);//时钟使能
    
	GPIO_InitStructure.GPIO_Pin = TRIG_PIN;		//PC8接TRIG
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	      //速度
	GPIO_Init(TRIG_PORT, &GPIO_InitStructure);	         //初始化GPIO 

	GPIO_InitStructure.GPIO_Pin = ECHO_PIN;				 //PC9接ECH0
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
	GPIO_Init(ECHO_PORT,&GPIO_InitStructure);		//初始化GPIO 

	GPIO_ResetBits(GPIOC,GPIO_Pin_8);//PC8初始状态为低电平,看时序图	
}

/*获取距离*/
int32_t sr04_get_distance(void)
{
	uint32_t t;	
		
	//PC8高电平
	GPIO_SetBits(TRIG_PORT,TRIG_PIN);
	delay_us(20);	//持续10us以上
	GPIO_ResetBits(TRIG_PORT,TRIG_PIN);//PC8低电平
	
	while(!GPIO_ReadInputDataBit(ECHO_PORT,ECHO_PIN));	//等待高电平
	
	//等待PC9出现高电平
	t=0;
	while(ECHO_PIN==0)
	{	
		//超时处理
		t++;
		delay_us(1);
		
		//如果超时,就返回一个错误码
		if(t >= 10000)
			return -1;
	
	}

	//测量高电平的时间
	t=0;
	while(GPIO_ReadInputDataBit(ECHO_PORT,ECHO_PIN))
	{	
		t++;
		delay_us(9);	//9us == 3mm
		
		//如果超时,就返回一个错误码
		if(t >= 10000)
			return -2;
	}
		
	//由于测量的时间,就是超声波从发射到返回的时间	
	d = t*0.3;
	
	return d;
	
}	





delay.h

#ifndef __DELAY_H
#define __DELAY_H

#include "stm32f10x.h"

void delay_us(uint32_t n);
void delay_ms(uint32_t n);

#endif

delay.c

#include "delay.h"

void delay_us(uint32_t n)
{
	SysTick->CTRL = 0; 			// Disable SysTick,关闭系统定时器
	SysTick->LOAD = (168*n)-1; // 配置计数值(168*n)-1 ~ 0
	SysTick->VAL  = 0; 		// Clear current value as well as count flag
	SysTick->CTRL = 5; 		// Enable SysTick timer with processor clock
	while ((SysTick->CTRL & 0x10000)==0);// Wait until count flag is set
	SysTick->CTRL = 0; 		// Disable SysTick	
}

void delay_ms(uint32_t n)
{
	while(n--)
	{
		SysTick->CTRL = 0; 		// Disable SysTick,关闭系统定时器
		SysTick->LOAD = (168000)-1; 	// 配置计数值(168000)-1 ~ 0
		SysTick->VAL  = 0; // Clear current value as well as count flag
		SysTick->CTRL = 5; 	// Enable SysTick timer with processor clock
		while ((SysTick->CTRL & 0x10000)==0);// Wait until count flag is set
	}
	
	SysTick->CTRL = 0; 		// Disable SysTick	

}



usart.h

#ifndef   __USART_H
#define	  __USART_H

#include "stm32f10x.h"
#include "stdio.h"

/*串口1参数宏定义*/
#define USART1_CLK			RCC_APB2Periph_USART1//串口时钟
#define USART1_BAUDRATE		9600//波特率

//串口1 GPIO引脚宏定义
#define USART1_GPIO_CLK		RCC_APB2Periph_GPIOA
#define USART1_GPIO_TX_PORT	GPIOA
#define USART1_GPIO_TX_PIN	GPIO_Pin_9
#define USART1_GPIO_RX_PORT	GPIOA
#define USART1_GPIO_RX_PIN	GPIO_Pin_10

//中断
#define  USART1_IRQ                USART1_IRQn
#define  USART1_IRQHandler         USART1_IRQHandler

//串口函数
void USART1_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t data);
void USART_SendString(USART_TypeDef* USARTx, char *DataString);


#endif /* __USART1_H */

usart.c

//串口
void USART1_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef  NVIC_InitStructure;
	
	/* config USART1 clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
	
	/* USART1 GPIO config */
	/* Configure USART1 Tx (PA.09) as alternate function push-pull */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);    
	/* Configure USART1 Rx (PA.10) as input floating */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	/* USART1 mode config */
	USART_InitStructure.USART_BaudRate = USART1_BAUDRATE;//波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//1个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No ;//无校验
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件控制流
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_Init(USART1, &USART_InitStructure);//串口初始化完成
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//使能串口
	
	//配置串口1的中断触发方法:接收一个字节触发中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

	//配置串口1的中断优先级
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
		
	USART_Cmd(USART1, ENABLE);
}



/*****************  发送一个字节 **********************/

void Usart_SendByte(USART_TypeDef * pUSARTx, uint8_t data)
{
	/* 发送一个字节数据到USART */
	USART_SendData(pUSARTx,data);
		
	/* 等待发送数据寄存器为空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

/*****************  发送字符串 **********************/

void USART_SendString(USART_TypeDef* USARTx, char *DataString)
{
	int i = 0;
	USART_ClearFlag(USARTx,USART_FLAG_TC);										//发送字符前清空标志位(否则缺失字符串的第一个字符)
	while(DataString[i] != '\0')												//字符串结束符
	{
		USART_SendData(USARTx,DataString[i]);									//每次发送字符串的一个字符
		while(USART_GetFlagStatus(USARTx,USART_FLAG_TC) == 0);					//等待数据发送成功
		USART_ClearFlag(USARTx,USART_FLAG_TC);									//发送字符后清空标志位
		i++;
	}
}

//重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(USART1, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

int fgetc(FILE *f)
{
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(USART1);
}

stm32f10x_it.c中断服务函数

#include "stm32f10x_it.h"
#include "usart.h"
#include "stdio.h"

void USART1_IRQHandler(void)
{
	u8 d;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
	{ 	
	    d=USART1->DR;
	  	printf("%c",d);    //将接受到的数据直接返回打印
	} 
	 
}

main.c

/**********************************************
 * 文件名  :main.c
 * 描述    :GPIOC8接超声波模块的TRIG
		   GPIOC9接超声波模块的ECHO
		   VCC接5V
		   GND接GND

             打开串口助手,配置波特率115200,8位,一个停止位,
		   无校验位。定时向PC发送测得的距离     

*******************************************************/

#include "stm32f10x.h"
#include "systick.h"
#include "stdio.h"
#include "usart.h"
#include "hc-sr04.h"
#include "delay.h"

/**********************************************
 * 文件名  :main.c
 * 描述    :GPIOC8接超声波模块的TRIG
		   GPIOC9接超声波模块的ECHO
		   VCC接5V
		   GND接GND

             打开串口助手,配置波特率115200,8位,一个停止位,
		   无校验位。定时向PC发送测得的距离     

*******************************************************/

#include "stm32f10x.h"
#include "systick.h"
#include "stdio.h"
#include "usart.h"
#include "hc-sr04.h"
#include "delay.h"

/*HMI串口屏函数定义,也可以直接使用串口字符串或者字节函数*/
void HMISends(char *buf1);
void HMISendb(u8 buf);

void HMISendstart(void)
	{
	 	delay_ms(200);
		HMISendb(0xff);
		delay_ms(200);
	}


int main(void)
{	
	uint32_t d;//距离
	USART1_Config();	//调用串口函数,配置模式为 115200 8-N-1
	NVIC_Configuration();//中断	
	sr04_init();//超声波模块初始化
	
	while(1)
	{		
		d=sr04_get_distance();
		printf("distance=%dcm\r\n",d);
		
		HMISendstart();  //串口HMI显示
		{ 
			printf("n0.val=%d",d);
			HMISendb(0xff);
		}			
	}
}

void HMISends(char *buf1)		 //字符串发送函数
{
	u8 i=0;
	while(1)
	{
	 if(buf1[i]!=0)
	 	{
			USART_SendData(USART1,buf1[i]);  //发送一个字节
			while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET){};//等待发送结束
		 	i++;
		}
	 else 
	 return ;

		}
}

void HMISendb(u8 k)		  //字节发送函数
{		 
	u8 i;
	 for(i=0;i<3;i++)
	 {
	 if(k!=0)
	 	{
			USART_SendData(USART1,k);  //发送一个字节
			while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET){};//等待发送结束
		}
	 else 
	 return ;

	 } 
} 


测距结果

测试距离

串口调试助手
STM32_超声波测距_第12张图片
串口屏
STM32_超声波测距_第13张图片

由于我没有使用定时器进行计时以及环境的影响,会存在一定的误差。

这篇文章所涉及的串口知识没有过多的文字介绍,只在代码中写了注释,其实串口通信很简单,只需要启动时钟,使用两根引脚的输入和输出模式,配置好相关参数即可,由于这篇文章的内容比较多,在这里就不进行过多的介绍,如果有机会,会在后面的文章会有所涉及。

在孤独中,一个人要像一支队伍。

作者能力水平有限,文章难免存在错误和纰漏,请大佬不吝赐教,非常欢迎大家与小白杨进行技术交流,一起学习技术。

你可能感兴趣的:(单片机,stm32,嵌入式,编程语言,物联网)