您现在的位置是:首页 >技术教程 >嵌入式大赛初探之-(3)外接超声波距离传感器网站首页技术教程
嵌入式大赛初探之-(3)外接超声波距离传感器
因为要用到测距功能,需要熟悉外接测距传感器,并研究其测距精度。
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
对于外设,首先需要学会用mounriver studio添加外设库,并编写相关函数在主程序完成测距的功能。
一、超声波传感器HC-SR04
外接的 超声波是振动频率高于20KHZ的机械波。它具有频率高、波长短、绕射现象小、方向性好、能够成为射线而定向传播等特点,应用广泛,适合大学生、工程师、技术人员以及电子爱好者等操作。
新版HC-SR04,性能远超老版HC-SRO4、US-015,在测距精度高于老版HC-SRO4和US-015的情况下,测距范围更远,可达6米,远超一般超声波测距模块。采用CS-100A超声波测距SOC芯片,高性能,工业级,宽电压、低价格,只有普通超声波测距模块一半的价格,而性能远超普通超声波测距模块。性能与US-025相同,均采用CS100A芯片,接口完全兼容。
这里选择拓展接口的PD10和PD11。
超声波HC-SR04模块:
VCC --> VCC
GND --> GND
Trig --> PD10
Echo --> PD11
二.工作原理
Trig (Trigger) 引脚用于触发超声波脉冲。
Echo 回声当接收到反射信号时,引脚产生一个脉冲。脉冲的长度与检测发射信号所需的时间成正比。
当持续时间至少为10 µS(10微秒)的脉冲施加到触发引脚时,一切开始。响应于此,传感器以40 KHz发射八个脉冲的声音脉冲。8脉冲模式从而使接收器能够将发射模式与环境超声噪声区分开。
八个超声波脉冲通过空气传播,远离发射器。同时,回声引脚变为高电平,开始形成回声信号的开始。
如果这些脉冲没有被反射回来(即最大距离内无障碍物时),则回波信号将在38毫秒(38毫秒)后超时并返回低电平。因此38 ms的脉冲表示在传感器范围内没有阻塞。
如果,脉冲被反射回去,则在收到信号后,Echo引脚就会变低。这会产生一个脉冲,其宽度在150 µS至25 mS之间变化,具体取决于接收信号所花费时间。
因此,当无障碍时,获取的回应脉冲为38ms长度,而存在障碍时,获取的回应脉冲长度为150 µS至25 mS之间变化。
总结:距离= 高电平时间*声速(340M/S)/2;
三、MRS添加库文件
1.新建.c和.h文件
打开MRS,创建一个CH32V307VCT6项目,在左侧的CH32V307VCT6栏目新建一个hardware文件夹,右键-新建-文件夹,起名叫HC-SR04,再右键-新建-头文件、源文件。
添加新的头文件需要添加到头文件寻址路径中,点击菜单栏-项目-属性配置按钮,在弹出的页面中,如下图,点击绿色加号添加路径即可。
2.编写.c函数
具体思路是:只需要将一个IO口配置推挽输出作为Trig,一个IO口配置下拉输入作为Echo,因为当Echo收到回响信号会由低电平置高电平,因此默认设置Echo为低电平。距离= 高电平时间*声速(340M/S)/2;
先用SysTick软件延时控制Trig发出高电平,之后等待回响信号,当Echo收到信号,即开启定时器,但是我们这个程序是在定时器中运行的,利用同一个定时器TIM2,在收到信号后要先把定时器清零。之后得到电平持续时间,计算出距离后再次把定时器清零,并且开启定时器,下次定时器中断方能正常触发。
(1)编写定时器函数
代码如下:
#include "debug.h" // Device header
//初始化定时器,采用基本定时器6,
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); //启用TIM3时钟
TIM_InternalClockConfig(TIM6); //设置TIM3使用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体,配置定时器
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置1分频(不分频)
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置计数模式为向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; //设置最大计数值,达到最大值触发更新事件,因为从0开始计数,所以计数10次是10-1,每10微秒触发一次
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //设置时钟预分频,72-1就是每 时钟频率(72Mhz)/72=1000000 个时钟周期计数器加1,每1微秒+1
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器(高级定时器才有,所以设置0)
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseInitStructure); //初始化TIM6定时器
TIM_ClearFlag(TIM6, TIM_FLAG_Update); //清除更新中断标志位
TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE); //开启更新中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体,配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn; //指定中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //中断使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //设置响应优先级
NVIC_Init(&NVIC_InitStructure); // https://blog.zeruns.tech
TIM_Cmd(TIM6, ENABLE); //开启定时器
}
/*
void TIM3_IRQHandler(void) //更新中断函数
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) //获取TIM3定时器的更新中断标志位
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除更新中断标志位
}
}*/
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif
(2)编写HC-SR04初始化与测距函数
#include "debug.h"
#include "stdio.h"
#include "ch32v30x.h"
#define Echo GPIO_Pin_10 //HC-SR04模块的Echo脚接GPIOD10
#define Trig GPIO_Pin_11 //HC-SR04模块的Trig脚接GPIOD11
uint64_t time=0; //声明变量,用来计时
uint64_t time_end=0; //声明变量,存储回波信号时间
void HC_SR04_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE); //启用GPIOB的外设时钟
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置GPIO口为推挽输出
GPIO_InitStructure.GPIO_Pin = Trig; //设置GPIO口5
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置GPIO口速度50Mhz
GPIO_Init(GPIOD,&GPIO_InitStructure); //初始化GPIOB
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置GPIO口为下拉输入模式
GPIO_InitStructure.GPIO_Pin = Echo; //设置GPIO口6
GPIO_Init(GPIOD,&GPIO_InitStructure); //初始化GPIOB
GPIO_WriteBit(GPIOD,GPIO_Pin_10,0); //输出低电平,让回应信号先保持低电平状态
Delay_Us(15); //延时15微秒
}
int16_t sonar_mm(void) //测距并返回单位为毫米的距离结果
{
uint32_t Distance,Distance_mm = 0;
GPIO_WriteBit(GPIOB,Trig,1); //输出高电平
Delay_Us(15); //延时15微秒
GPIO_WriteBit(GPIOB,Trig,0); //输出低电平
while(GPIO_ReadInputDataBit(GPIOB,Echo)==0); //等待低电平结束
time=0; //计时清零
while(GPIO_ReadInputDataBit(GPIOB,Echo)==1); //等待高电平结束
time_end=time; //记录结束时的时间
if(time_end/100<38) //判断是否小于38毫秒,大于38毫秒的就是超时,直接调到下面返回0
{
Distance=(time_end*346)/2; //计算距离,25°C空气中的音速为346m/s
Distance_mm=Distance/100; //因为上面的time_end的单位是10微秒,所以要得出单位为毫米的距离结果,还得除以100
}
return Distance_mm; //返回测距结果
}
float sonar(void) //测距并返回单位为米的距离结果
{
uint32_t Distance,Distance_mm = 0;
float Distance_m=0;
GPIO_WriteBit(GPIOB,Trig,1); //输出高电平
Delay_Us(15);
GPIO_WriteBit(GPIOB,Trig,0); //输出低电平
while(GPIO_ReadInputDataBit(GPIOB,Echo)==0);
time=0;
while(GPIO_ReadInputDataBit(GPIOB,Echo)==1);
time_end=time;
if(time_end/100<38)
{
Distance=(time_end*346)/2;
Distance_mm=Distance/100;
Distance_m=Distance_mm/1000;
}
return Distance_m;
}
void TIM3_IRQHandler(void) //更新中断函数,用来计时,每10微秒变量time加1
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) //获取TIM3定时器的更新中断标志位
{
time++;
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除更新中断标志位
}
}
#ifndef __HCSR04_H
#define __HCSR04_H
void HC_SR04_Init(void);
int16_t sonar_mm(void);
float sonar(void);
#endif
(3)编写主程序
这里要实现的是使用毫米为单位,并用串口打印测得距离。
#include "debug.h"
#include "timer_6.h"
#include "HC-SR04.h"
/* Global typedef */
/* Global define */
/* Global Variable */
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SystemCoreClockUpdate();
Delay_Init();
USART_Printf_Init(115200);
Timer_Init();
HC_SR04_Init();
//printf( "ChipID:%08x
", DBGMCU_GetCHIPID() );
//printf("This is printf example
");
while(1)
{
float distance = sonar_mm();
printf("distance:%f
",distance);
}
}
总结
目前由于项目要用到更为复杂的测距方案,因此本文在借鉴其他大佬的HC-SR04的库,只给出了最基础的应用实例。