基于stm32的多功能时钟4——超声波测距
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于stm32的多功能时钟4——超声波测距相关的知识,希望对你有一定的参考价值。
参考技术A hello,读者们好!前两章,主要讲述了环境参量的测量获取,想必大家都有些许收获。在这一章中,我将介绍如何利用超声波来测距。在现实生活中,利用超声波测距的应用很多,广泛应用于机器人避障 、物体测距 、液位检测 、公共安防、停车场检测等领域。
本次测距使用的超声波为HC-SRO4,该模块共有4个引脚,分别是两个电源引脚VCC和GND,一个触发控制信号输入(TRIG)和一个回响信号输出( ECHO),性能稳定,测度距离精确,模块高精度,盲区小。
那么,超声波模块测距原理是:首先,给Trig引脚至少10us的高电平信号,检测Echo是否有信号返回,若有信号返回,则Echo发出高电平。高电平持续的时间就是超声波从发射到返回的时间,所以测试距离为(高电平时间*声速)/2。下面,就是超声波模块的时序图。
本模块使用方法简单,配合stm32的定时器TIM4,一个控制口发一个10us以上的高电平,就可以在接收口等待高电平输出。一有输出就可以开定时器TIM4计时,当此口变为低电平时就可以读定时器TIM4的值,此时就为此次测距的时间,方可算出距离。如此不断的周期测,即可以达到你移动测量的值。
(1)配置超声波的引脚
/*初始化超声波引脚:Trig:PB0,Echo:PB1*/
void ultra_gpio_init(void)
GPIO_InitTypeDef GPIO_InitStructure;
/*使能GPIO的RCC时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*配置Trig引脚*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//Trig
GPIO_Init(GPIOB,&GPIO_InitStructure);
/*配置Echo引脚*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//Echo
GPIO_Init(GPIOB,&GPIO_InitStructure);
由于PB0接超声波Trig引脚,所以选择推挽输出模式,PB1接超声波Echo引脚,所以选择浮空输入模式。这样,超声波模块引脚就配置完成。
(2)定时器TIM4初始化
/*定时器4的NVIC配置*/
void tim4_nvic_config(void)
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStruct.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级为0
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;//子优先级为0
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
/*定时器4初始化*/
void tim4_config(void)
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
tim4_nvic_config(); //配置NVIC
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//开启时钟
TIM_DeInit(TIM4); //定时器4复位
TIM_TimeBaseInitStruct.TIM_Period = 1000-1; //自动重装载寄存器值
TIM_TimeBaseInitStruct.TIM_Prescaler = 72-1; //时钟预分频数
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; //采样分频
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStruct); //初始化TIM4
TIM_ClearFlag(TIM4, TIM_FLAG_Update); //清除溢出中断标志
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM4, DISABLE);
由于考虑到测距时的距离过大,计数会溢出,出现不准确的现象,这里需要用到长计时,并且使用TIM4中断对计时变量进行自增,所以需要配置NVIC。这里设置的中断优先级比较高,因为测距不能被其他中断打断,否则可能出现数据不准的现象,或是数据抖动现象。其次,设置TIM4的中断溢出时间为1ms,此时还不能开启定时器TIM4。
(3)编写定时器中断程序
/*定时器4中断服务函数*/
void TIM4_IRQHandler(void)
if(TIM_GetITStatus(TIM4 ,TIM_IT_Update)!=RESET)
TIM4_NUM++;//长计时变量
TIM_ClearITPendingBit(TIM4 ,TIM_FLAG_Update);
为避免测量的距离过长,这里我们需要进行长计时,只需在中断函数里这样操作:TIM4_NUM++,同时记得在每次测量距离前对TIM4_NUM复位即可。
(4)编写超声波测距相关函数
/*启动超声波测距*/
u16 ultra_measure(void)
u16 distance;
TRIG_H;
delay_us(20);
TRIG_L;
while(ECHO==RESET);
TIM_SetCounter(TIM4,0);
TIM4_NUM = 0;
TIM_Cmd(TIM4, ENABLE);
while(ECHO!=RESET);
TIM_Cmd(TIM4, DISABLE);
distance = (u16)ultra_get_distance();
return distance;
/*获取超声波传播时间,间接计算出距离*/
float ultra_get_distance(void)
u32 time;
float distance;
time = TIM4_NUM*1000;
time += TIM_GetCounter(TIM4);//获取超声波测距总时间
TIM4->CNT = 0; //定时器复位
distance = (float)time*0.017;
return distance;
这里,需要查阅超声波手册中的时序图,方可编写程序。首先,向给trig 发送至少10 us的高电平脉冲,然后等待,捕捉 echo 端输出上升沿,捕捉到上升沿的同时,打开定时器开始计时,再次等待捕捉echo的下降沿,当捕捉到下降沿,读出计时器的时间,这就是超声波在空气中运行的时间,按照测试距离=(高电平时间*声速)/2 就可以算出超声波到障碍物的距离。
这里我们测算的距离:distance = (float)time*0.017,计算的距离单位为cm。
(5)主函数调用测距函数
最后,在主函数里,调用测距函数即可获取到距离值,再通过lcd显示函数,显示出距离值。
value = ultra_measure();
lcd_display_string(0,32,"测量距离");
lcd_display_num_m(3, 48, value/1000);
lcd_display_num_m(3, 56, (value%1000)/100);
lcd_display_num_m(3, 64, (value%100)/10);
lcd_display_num_m(3, 72, value%10);
通过本章的介绍,相信你对于超声波测距应该了解不少了吧,相信你也可以做出来的。通过不断改变超声波和障碍物之间的位置,距离值会随之改变,是不是很有趣啊~
到目前为止,多功能时钟已经具备了显示时间、测量温湿度、测量空气质量以及测距的功能,但我们的LCD显示部分还没有优化。在下一章中,我将带着大家完成多功能时钟人机交互界面(简称UI)的开发,到时候,我们的界面就会变得比较美观了。敬请期待~
基于STM32和超声波测距传感器的测距功能设计
基于STM32和超声波测距传感器的测距功能设计(使用陌生传感器的流程介绍)
引言
最近和很多单片机和嵌入式的初学者交流,发现大家对使用一个没接触过的传感器往往感到无从下手。正好我最近使用一款我以前没使用过的超声波测距传感器开发新的功能,这里就来逐步详细的介绍如何使用一款陌生的传感器。大家在项目开发中,如果使用陌生传感器没有思路,可以仔细看一下本文。可以说,几乎使用所有的传感器,往往开发流程大同小异,所以阅读本文其实就可以对开发有一个清晰的认识。
项目设计
项目简介
本项目选用STM32为MCU,使用485接口的超声波测距传感器,实现测量距离的功能。
开发工具
- keil 5:STM32程序开发平台
- SSCOM:串口调试助手
硬件设计
- 使用目前主流的STM32F103C8T6充当MCU(编写程序使用标准库)。
- 使用KS114超声波测距传感器,其中数据传输接口为485(具体使用何种型号和厂商的传感器其实不重要,只要选用好是何种接口,比如是485还是IIC,另外配有手册就可以完成开发,本文也会详细介绍如何接触一个陌生的传感器模块进行开发的流程)。
- USB转TTL模块,主要为了调试用,没有也可以。
软件设计
- 需要在STM32单片机和超声波测距传感器之间建立通信,因为是485,所以主要是编写USART的数据收发。
- 查询手册获取控制超声波传感器的控制指令,每250ms向超声波测距传感器发送一次控制超声波测距传感器测距的命令。
- 将接收到的距离值进行校验,保证数据安全。
- 将接收到的距离值进行解析,转换成米和厘米的组合。
注:现在常规使用的传感器往往集成化很高,我们其实就是使用传感器提供的接口进行数据收发并对数据进行处理,无非就是使用什么通信方式而已。
开发流程
阅读手册
- 我们使用一个陌生的传感器,首先需要看一下他的功能摘要。
- 然后需要查看传感器的电性能参数,比如我们使用超声波测距传感器,我们就可以看一下他的供电电压、波束脚和工作状态的功率。
- 使用一个传感器,我们必须知道传感器每个引脚具体是什么作用的。比如正、负极和485通信引脚。
可以看到,图中说明了485-A、485-B、正极和负极具体是那个颜色的线,我们使用的时候也就可以接上相应的线。
- 对于使用陌生的传感器最关键的就是要看一下此传感器的工作流程。
我们从上述手册中知道了,使用此超声波测距传感器的主要流程是:先配置相关波特率、串口地址等基本信息 ->向超声波测距传感器发送测距命令 -> 接收超声波测距的命令。
- 从手册的工作流程我们知道,我们需要知道超声波的地址有哪些,然后开始查手册。
- 我们还需要知道探测指令都有哪些,之后仍然是查手册。
- 我们如果想使用多个超声波传感器,那么一般需要传感器给我们回传的信息中有相应的标识,此传感器中的串口地址就有此效果,那么如何让他回传呢,我们还是要查手册获得。
我们看到如何配置此通信协议了。
- 在手册的最下面我们看到他的使用建议里有如何快速上手,这往往可以让我们的开发更加快捷。
配置并测试传感器相关设置
注:每个传感器的配置流程和测试流程都不同,这里只是以本次使用的超声波测距传感器为例进行介绍平时的流程。读者进行开发的时候,还是要你自己使用的传感器手册为准,本节大家大致看一下即可,不用细看。
- 首先我们使用USB转TTL模块,将其中的485-A和485-B和超声波传感器的A和B相连,并将超声波测距传感器的正负极连接上电源并通电。
- 使用传感器手册中给的调试软件,根据手册给的串口信息进行设置。
连接成功后,我们可以向传感器发送控制指令,测试一下是否能正常使用。
- 我们需要修改波特率,这里设置为9600。
当修改完波特率后,我们以刚设置的波特率重新连接一下。
- 我们修改一下地址,这里设置为ea。
- 最后我们修改一下通信协议,选择手册里我们查询到的对应通信格式的指令,然后设置。
- 最后就是发送指令,测试是否符合我们的要求。
我们看到,测试的效果完全符合我们的预期,那么我们就可以进行MCU的软件开发了。
软件开发
- 为了增强可读性,我们往往需要将一些数据使用宏定义增加代码的可读性。我们将传感器手册中,我们使用到的指令都可以宏定义一下。
/****定义指令****/
#define Left_Address_cmd 0xe8
#define Right_Address_cmd 0xea
#define Register_02_cmd 0x02
#define Probe1_cmd 0x14//探测范围1cm-2m
#define Probe2_cmd 0x1e//探测范围1cm-3m
#define Probe3_cmd 0xb0//探测范围1cm-5m
- 我们要开发MCU和超声波测距传感器的通信,硬件上只需要将485的A与B连接上即可,下面主要说一下软件的编写。首先我们需要初始化串口,相关的程序我相信大家都看过很多次了。
void USART2_485_Init(u32 bound)
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置波特率
USART_InitStructure.USART_BaudRate = bound;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_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(USART2, &USART_InitStructure);
// 使能串口接收中断
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
USART_Cmd(USART2, ENABLE);
// 清除发送完成标志
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0X08;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0X00;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
然后编写串口发送。
void USART_Send(USART_TypeDef* USARTx,u8 *data, u8 len)
for(int i = 0; i < len; i++)
USART_SendData(USARTx, data[i]); //向串口发送数据
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET); //等待发送结束
最后编写串口接收函数,这里使用中断接收。
void USART2_IRQHandler(void)
u8 Res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断
Res = USART_ReceiveData(USART2); //读取接收到的数据
if(rbWrite(&pRb_dis, &Res, 1) != 1)//此处使用到了环形队列,每次接收一个字节放进环形队列中,环形队列的编写不是此项目的重点,大家可以用别的方式实现,主要是存下来字节的数据。
printf("DIS_溢出");
else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) //接收中断
USART2->SR;//先读SR寄存器
USART2->DR; //再读DR寄存器
- 我们编写函数实现给超声波发送相关指令,控制超声波执行对应工作的任务。
void control_ultrasonic(uint8_t address,uint8_t instruction)
uint8_t tmp[5];
switch(instruction)
case Probe1_cmd:
tmp[0]=address;
tmp[1]=Register_02_cmd;
tmp[2]=Probe1_cmd;
USART_Send(USART2,tmp,3);
break;
case Probe2_cmd:
tmp[0]=address;
tmp[1]=Register_02_cmd;
tmp[2]=Probe2_cmd;
USART_Send(USART2,tmp,3);
break;
case Probe3_cmd:
tmp[0]=address;
tmp[1]=Register_02_cmd;
tmp[2]=Probe3_cmd;
USART_Send(USART2,tmp,3);
break;
default:
break;
此函数的编写主要就是将我们需要发送的指令放在一个数组中,然后调用串口发送函数进行发送。
在这里我们也可以很明显的发现宏定义的好处,让这里可读性增强了非常多。
- 我们使用传感器,最重要的当然是想要传感器的数据。我们一般需要编写一个函数用以处理我们接受到的传感器的数据,也就执行数据处理的角色。
void distance_data(void)
uint8_t dis_Temp_Buff[4] = 0;
rbRead(&pRb_dis, dis_Temp_Buff, 4);
if(((dis_Temp_Buff[0]==Left_Address_cmd)&&(dis_Temp_Buff[3]==((uint8_t)(Summation(dis_Temp_Buff,3)&0xFF)))))
Ultrasonic_distance[0]=dis_Temp_Buff[1];
Ultrasonic_distance[0]=(Ultrasonic_distance[0]<<8)|(dis_Temp_Buff[2]);
else if( (dis_Temp_Buff[0]==Right_Address_cmd)&&( dis_Temp_Buff[3]==((uint8_t)(Summation(dis_Temp_Buff,3)&0xFF))) )
Ultrasonic_distance[1]=dis_Temp_Buff[1];
Ultrasonic_distance[1]=(Ultrasonic_distance[1]<<8)|(dis_Temp_Buff[2]);
else
Dis_Error_Cnt++;
if(Dis_Error_Cnt > 5)
Dis_Error_Cnt = 0;
printf("超声波距离数据错误");
我们首先将环形队列中的数据取出来四个字节放在一个数组中,然后我们进行数据校验,校验方式可以在手册中得到。数据校验正确时,我们将超声波发来的数据中代表距离的数据,放在一个全局变量中,这样的好处是,方便我们在任何一个函数中使用。此时其实我们的主要需求已经都能满足,项目主体已经完成。
- 最后就是使用我们编写的这些程序,因为每个人的需求都不同,这里也就不具体放代码了。但编写流程就是:循环发送control_ultrasonic(Left_Address_cmd ,Probe2_cmd);和control_ultrasonic(Right_Address_cmd,Probe2_cmd);,使超声波传感器一直将测量的距离发送给MCU -> 调用processing_distance_data函数将接受到的数据进行数据处理 ->我们想要使用这个数据的时候,直接读取这个Ultrasonic_distance全局变量数组即可使用距离数据。
总结
嵌入式软件工程师和单片机工程师使用一个陌生的传感器的流程上面已经详细介绍完毕。具体流程就是先仔细阅读一遍技术手册或者说明书,然后使用配套软件或者其他第三方软件进行需要的配置信息设置和大致通信测试,再后进行程序的编写,最后是进行总体测试。可以说,使用所有传感器几乎都是这几个步骤,大家可以试一下。
后续
更多嵌入式开发知识可以关注公众号:物联网知识。欢迎大家和我进行交流,一同进步。
以上是关于基于stm32的多功能时钟4——超声波测距的主要内容,如果未能解决你的问题,请参考以下文章
基于STM32和超声波测距传感器的测距功能设计(如何使用陌生传感器的流程介绍)