利用声音测距,声音在空气中的速度是340m/s(15℃)
当声音传播时,若遇到障碍物时,就会被反弹回来,通过计时反弹回来的时间就可以计算出从发射端到障碍物的距离
S=v*(t/2)
在单片机中借助超声波模块实现测距功能,通过串口调试助手或者显示屏进行显示反馈
HC-SR04超声波模块测量范围为2cm-400cm,精度可达3mm。
引脚功能:
VCC引脚:5V电源。
TRIG引脚:触发信号引脚,单片机给超声波模块一个信号,超声波模块就会工作。
ECHO引脚:回响信号引脚,当超声波模块已经测量距离成功后,通过该引脚告诉单片机当前超声波传输的时间。
GND:信号地。
模块工作原理:
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、不同的温度,声音的传播速度是不一样的,有以下公式:
一般取15℃时,v=340m/s,可以在测量距离时加入温湿度传感器测量环境温度,带入公式,动态计算声速值,使测量更加精确;
2、测距时,要求被测物体尽量平整,且面积不小于0.5平方米,否则会影响测量结果;
2、超声波的测量误差为3mm,可根据3mm的测距求出测量时间,求算如下:
这里使用这个公式测量距离,当延时9us,即ECHO持续高电平9us,测得的距离为3mm,这里只是近似值。
本实验使用的串口屏来自淘晶驰商家
型号为 :TJC3224K024_011R
320x240分辨率
_011:硬件版本号
K0系列
2.4寸屏幕(斜对角线的距离)
触摸屏说明:N=无触摸 R=电阻触摸屏 C=电容触摸屏
相对LCD(液晶屏)和OLED(有机发光显示器),串口屏幕更加简单,只有4根线,利用串口通信,而且屏幕丰富多彩,可以彩屏显示。
LCD:32根引脚,使用IIC或者SPI通信
OLED:显示颜色有限,最多两种颜色
相对其他商家,陶晶驰的串口屏使用起来比较简单,只需遵守它的串口屏协议就可以。
这是它的语法规则,只需要在控件后面加三个0xff字节作为结束符,没有其他规则的要求。
本实验设计的串口屏界面:
由于这款软件是中文的,所以不进行过多的介绍,直接点击帮助文档进行学习,官网关于这款软件的使用介绍的佷详细,只是与单片机的通信介绍的很少,不过官方文档给出了明确的说明,这属于高级应用,不过多介绍。
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更加小巧,方便通信。
3、串口屏的波特率一定要和所写程序的波特率设置成一样的,才可以进行通信,虽然上位机可以更改屏幕波特率,但是屏幕波特率可能出厂时已经固定好了,即使在上位机强制更改,通信的时候也无法正常通信,不知道屏幕的波特率,下载屏幕界面可以自动搜索,上面会有显示。
虽然有很多东西被淘汰了,主要是因为时代在进步,科技再不断更新迭代,但是新东西的诞生源于旧物件,旧物件涉及到的知识永远不会淘汰,除非一种知识被证明是错误的,那么错的东西肯定会被淘汰,这就像我们现在仍在学习几千年牛顿总结出的各种定律,如果这些定律被证明是错的,那么我们就不需要再学习了,今天需要学习的知识是太多了,学习途径也有很多,在知识的海洋中遨游,我们一定要站在巨人的肩膀上。
使用串口调试助手也可以显示数据,当拔下跳线帽时,串口调试助手就不可以使用了,或者使用其他串口来连接串口调试助手也可以,这里也要波特率设置成一样才可以通信
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 ;
}
}
测试距离
由于我没有使用定时器进行计时以及环境的影响,会存在一定的误差。
这篇文章所涉及的串口知识没有过多的文字介绍,只在代码中写了注释,其实串口通信很简单,只需要启动时钟,使用两根引脚的输入和输出模式,配置好相关参数即可,由于这篇文章的内容比较多,在这里就不进行过多的介绍,如果有机会,会在后面的文章会有所涉及。
在孤独中,一个人要像一支队伍。
作者能力水平有限,文章难免存在错误和纰漏,请大佬不吝赐教,非常欢迎大家与小白杨进行技术交流,一起学习技术。