嵌入式大赛初探之-(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\r\n", DBGMCU_GetCHIPID() );
	//printf("This is printf example\r\n");

	while(1)
    {
    
    
	float distance = sonar_mm();
	printf("distance:%f\r\n",distance);

	}
}

总结

目前由于项目要用到更为复杂的测距方案,因此本文在借鉴其他大佬的HC-SR04的库,只给出了最基础的应用实例。

猜你喜欢

转载自blog.csdn.net/qq_53092944/article/details/130122806