作者简介:大家好啊,我叫DW,每天分享一些我新学到的知识,期待和大家一起进步
系列专栏:STM32
小实验目标:在OLED上显示HC_SR04测距的值
如有写得不好的地方欢迎大家指正开发板:STM32F103ret6
创作时间:2022年6月11日
目录
HCSR_04特点
超声波测距原理
实物图连接图
HC_SR04程序编写
配置IO口
超声波测距函数
HC_SR04 超声波测距模块可提供 2cm~400cm的非接触式距离感测功能,测距精度可达到3mm;模块包括超声波发射器、接收器与控制电路。
超声波测距原理是在超声波发射装置发出超声波,它的根据是接收器接到超声波时的时间差,与雷达测距原理相似。 超声波发射器向某一方向发射超声波,在发射时刻的同时开始计时,超声波在空气中传播,途中碰到障碍物就立即返回来,超声波接收器收到反射波就立即停止计时。
超声波在空气中的传播速度为340m/s,根据计时器记录的时间t(秒),就可以计算出发射点距障碍物的距离(s),即:s=340t/2。
VCC 供 5V电源
GND 为地线
Trig 触 发 控 制 信 号 输入,与STM32单片机的接PA6相连接
Echo() 回响信号,与STM32单片机的接PA7相连接
初始化Trig和Echo两个引脚,并配置定时器中断。
u32 msCount = 0;
void HC_SR04_UserConfig(void){
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE); //使能定时器6时钟
GPIO_InitStructure.GPIO_Pin = Trig; //触发测距引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_Init(HC_PROT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = Echo; //信号回响引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉
GPIO_Init(HC_PROT, &GPIO_InitStructure);
TIM_DeInit(TIM6);
TIM_InitStructure.TIM_Period = 1000-1;//1MS
TIM_InitStructure.TIM_Prescaler = 72-1;//预分配系数
TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//不分频
TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_InitStructure.TIM_RepetitionCounter = DISABLE;//不开启重复计数
TIM_TimeBaseInit(TIM6,&TIM_InitStructure);//定时器初始化
TIM_ClearFlag(TIM6,TIM_FLAG_Update);
TIM_ITConfig(TIM6,TIM_IT_Update|TIM_IT_Trigger,ENABLE);//使能中断源和中断触发
TIM_Cmd(TIM6,DISABLE);//关闭定时器
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;//选择TIM6中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//子占优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//中断使能
NVIC_Init(&NVIC_InitStructure);//初始化中断
}
void TIM6_IRQHandler(void){
if(TIM_GetITStatus(TIM6,TIM_IT_Update) != RESET){//判断中断是否产生 1MS
TIM_ClearITPendingBit(TIM6,TIM_IT_Update);//清空中断标志位
msCount++;
}
}
为什么配置Echo引脚为下拉模式呢?因为由时序图可以看出,没有检测到回响信号时,Echo一直处于低电平状态,如果模式设置为浮空的话是不稳定的。
当检测到信号时引脚拉高,当没有检测到信号时引脚自动拉低,所以配置为下拉模式。
以上时序图表明你只需要提供一个 10uS 以上脉冲触发信号,该模块内部将发出 8 个 40kHz 周期电平并检测回波。一旦检测到有回波信号则输出回响信号。回响信号的脉冲宽度与所测的距离成正比。由此通过发射信号时的时间和接收到回响信号时的时间的间隔,就可以计算得到距离。
公式:uS/58=厘米或者 uS/148=英寸;或是:距离= 高电平时间*声速(340M/S)/2;建议测量周期为 60ms 以上,以防止发射信号对回响信号的影响。
1. data1为通过厘米换算的值,data2为通过声速换算的值。
2. 由超声波时序图可以知道,当产生10us以上的触发脉冲时,Echo处于信号回响状态,当一开始时我们不确定此时输出回响信号Echo是否处在低电平状态,所以首先要判断Echo引脚的电平状态,以此用来查看上次有没有完成数据转换,之后再去触发信号Trig。
while(GPIO_ReadInputDataBit(HC_PROT,Echo) == 1);//!1,则执行下一步
Trig_High;
delay_us(20);
Trig_Low;
3. 如果有触发信号Trig,模块内部会自动产生8 个 40kHz 周期电平,之后再判断Echo是否有高电平输出;
while(GPIO_ReadInputDataBit(HC_PROT,Echo) == 0);//!=0
TIM_SetCounter(TIM6,0);//清空计数器
msCount = 0;//清空中断计数器值
TIM_Cmd(TIM6,ENABLE);//开启TIM6中断
4. 当Echo不为高电平时,则我们记录下一个回响电平,我们此时关闭TIM6.
while(GPIO_ReadInputDataBit(HC_PROT,Echo) == 1);//!1
TIM_Cmd(TIM6,DISABLE);
5. 获取高电平时间,并进行距离换算。
Count = msCount*1000;//us = ms * 1000
Count = Count + TIM_GetCounter(TIM6);//高电平时间
*data1 = Count/58;// us/58
*data2 = Count*0.017;// 340 00/1000 000=0.034 0.034/2=0.017
//有来回两段距离,故需要/2
delay_ms(100);
完整函数代码如下:
void HC_SR04_Ranging(u16 *data1,u16 *data2){
u32 Count = 0;
while(GPIO_ReadInputDataBit(HC_PROT,Echo) == 1);//!1,则执行下一步
Trig_High;
delay_us(20);
Trig_Low;
while(GPIO_ReadInputDataBit(HC_PROT,Echo) == 0);//!=0
TIM_SetCounter(TIM6,0);//清空计数器
msCount = 0;//清空中断计数器值
TIM_Cmd(TIM6,ENABLE);//开启TIM6中断
while(GPIO_ReadInputDataBit(HC_PROT,Echo) == 1);//!1
TIM_Cmd(TIM6,DISABLE);//关闭定时器
Count = msCount*1000;//us = ms * 1000
Count = Count + TIM_GetCounter(TIM6);//高电平时间
*data1 = Count/58;// us/58
*data2 = Count*0.017;// 340 00/1000 000=0.034 0.034/2=0.017
delay_ms(100);
}
340m/s等于 ? cm/us
1s=1000 000 us
340 00/1000 000=0.034
340m/s=0.034cm/us
6. 为了使获得的数据更加准确,我们需要多次测量获取平均值。
void HC_SR04_Debolan(u8 mode){
u16 data1 = 0,data2 = 0; u32 data = 0;
HC_SR04_Ranging(&data1,&data2);
if(mode){ //厘米换算
for(u8 i=0;i<5;i++){
data = data + data1;
}
OLED_Write_Number(0,40,data/5);
}
else{ //声速换算
for(u8 i=0;i<5;i++){
data = data + data2;
}
OLED_Write_Number(4,40,data/5);
}
}
我们配置了两种模式,模式1为厘米换算的值,模式0为声速换算的值。
7. 主函数
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "oled.h"
#include "HC_SR04.h"
int main(void)
{
delay_init();
OLED_UserConfig();
OLED_Init();
OLED_Display_On();
HC_SR04_UserConfig();
//OLED_Display_Off();
while(1){
//OLED_Write_Number(2,40,131);
HC_SR04_Debolan(1);//厘米换算
}
}
全部的源代码已经介绍完毕,我们在测距模块3cm出放置障碍物,可以看到液晶屏幕上显示数字3,在模式1和模式0下都能准确的测量出距离。
为了方便下次查找,记得点点关注哦。
本章结束,我们下一章见
参考资料:
1.STM32固件库手册
2.正点原子STM32不完全手册_库函数版本
3.参考视频
资料已上传,需要自取