• 第34章_瑞萨MCU零基础入门系列教程之SR04超声波测距实验


    本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写,需要的同学可以在这里获取: https://item.taobao.com/item.htm?id=728461040949

    配套资料获取:https://renesas-docs.100ask.net

    瑞萨MCU零基础入门系列教程汇总https://blog.csdn.net/qq_35181236/article/details/132779862


    第34章 SR04超声波测距实验

    34.1 SR04超声波模块简介

    34.1.1 产品概述

    超声波测距模块是利用超声波来测距。模块先发送超声波,然后接收反射回来的超声波,由反射经历的时间和声音的传播速度340m/s,计算得出距离。

    SR04是一款常见的超声波传感器,模块自动发送8个40KHz的方波,自动检测是否有信号返回,用户只需提供一个触发信号,随后检测回响信号的时间长短即可。

    SR04采用5V电压,静态电流小于2mA,感应角度最大约15度,探测距离约2cm-450cm。

    34.1.2 硬件连接

    主机通过两条数据线与SR04连接:主机通过Trig引脚发脉冲给SR04,主机检测Echo引脚的高电平时长。

    SR04模块上面有四个引脚,分别为:VCC、Trig、Echo、GND。

    Trig是脉冲触发引脚,即控制该脚让SR04模块开始发送超声波。

    Echo是回响接收引脚,即SR04模块一旦接收到超声波的返回信号则输出回响信号,回响信号的脉冲宽度与所测距离成正比。

    34.1.3 测距时序

    SR04超声波测距模块时序图所下图所示:

    要测距,需如下操作:

    ① 触发:

    向Trig(脉冲触发引脚)发出一个大约10us的高电平。

    ② 发出超声波,接收反射信号:

    模块就自动发出8个40Khz的超声波,超声波遇到障碍物后反射回来,模块接收返回来的超声波。

    ③ 回响:

    模块接收到反射回来的超声波后,Echo引脚输出一个与检测距离成比例的高电平。

    我们只要在该引脚为高时,开启定时器计数,在该引脚变为低时,结束定时器计数。根据定时器的计数和定时器频率就可以算出经历时间,根据时间即可推导出距离。

    计算公式为:测试距离=(高电平时间*声速(340M/S))/2;

    34.2 模块配置

    34.2.1 GPIO配置

    本次实验使用的超声波模块会使用到2个IO:Trig和Echo。P003连接到Trig,将其配置为输出模式:

    P503引脚连接到Echo,使用它的GPT触发功能,触发GPT的计数开启或者停止。

    34.2.2 定时器配置

    本次实验获取回响时间采用的方法是,使用P503触发GPT开始计数和结束计数。

    已知Echo默认情况下是低电平的,当Trig发出信号后,Echo会产生高脉冲。

    所以,使用P503的上升沿触发GPT的计数开启,下降沿触发GPT的计数停止,在Trig引脚发出开始脉冲后,就轮询GPT的状态和计数变化。

    如果GPT处于停止状态,且计数不为0,那就代表着Echo触发了一次GPT计数,将计数值读取出来,就可以算出时间、举例。

    RA6M5的P503引脚具有的外设复用功能如下图所示:

    本书利用其GPY_POEG2:GTETRGC功能触发GPT,本次实验使用的是GPT0。对于GPT的配置如下图所示:

    这里配置的计数周期值是32位GPT最大计数,这是为了尽可能让测量时间在GPT的一次计数周期内完成,不产生溢出以免增加计算的复杂度。

    34.2.3 GPT_POEG配置

    因为使用到了外部引脚触发GPT的POEG功能,因而还需要添加POEG Stack模块:

    另外P503是PORG2,所以添加的POEG模块通道要修改为2,如下图所示:

    34.3 驱动程序

    34.3.1 IO驱动

    和DS18B20的IO驱动一模一样,参考《32.4.1 IO驱动》。

    34.3.2 定时器驱动

    1. 初始化定时器

    在初始化的时候除了要打开GPT以外,还需要打开POEG设备:

    static int GPTDrvInit(struct TimerDev *ptdev)
    {
        if(NULL==ptdev) return -EINVAL;
        switch(ptdev->channel)
        {
            case 0:
            {
                /* 打开GPT设备完成初始化 */
                fsp_err_t err = g_timer0.p_api->open(g_timer0.p_ctrl, g_timer0.p_cfg);
                assert(FSP_SUCCESS == err);
                err = g_poeg0.p_api->open(g_poeg0.p_ctrl, g_poeg0.p_cfg);
                assert(FSP_SUCCESS == err);
                err = g_timer0.p_api->enable(g_timer0.p_ctrl);
                assert(FSP_SUCCESS == err);
                break;
            }
            default:break;
        }
        
        return ESUCCESS;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    1. 轮询GPT状态以及读取计数值
    static int GPTDrvRead(struct TimerDev *ptdev, unsigned char *buf, unsigned int length)
    {
        if(NULL == ptdev)   return -EINVAL;
        if(NULL == buf)     return -EINVAL;
        if(0 == length)     return -EINVAL;
    
        switch(ptdev->channel)
        {
            case 0:
            {
                timer_status_t status = {.state = TIMER_STATE_STOPPED};
                fsp_err_t err = g_timer0.p_api->statusGet(g_timer0.p_ctrl, &status);
                assert(FSP_SUCCESS == err);
                if(TIMER_STATE_STOPPED == status.state && status.counter == 0)
                {
                    return -EIO;
                }
                else if(TIMER_STATE_STOPPED == status.state && status.counter != 0)
                {
                    unsigned int *pbuf = (unsigned int *)buf;
                    *pbuf = status.counter*10;    /* ns */
                    err = g_timer0.p_api->reset(g_timer0.p_ctrl);
                    assert(FSP_SUCCESS == err);
                    return (int)length;
                }
                else
                    return -EIO;
                break;
            }
            default:break;
        }
        return (int)length;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 第12行:获取定时器状态;
    • 第14行:判断状态是否停止和计数值是否为零,如果是这种情况那么就代表没有回响信号;
    • 第18行:如果定时器是停滞状态,且GPT计数值不为0,就表示成功测量了一次回响信号,下面就取出计数值存入buffer。

    34.4 SR04模块

    34.4.1 SR04设备对象

    要操作SR04,只需要对它进行初始化、然后读取数值。抽象出如下结构体(dev_ultra.h):

    typedef struct UltraDev{
        float distance;
        unsigned int humidity;
        int (*Init)(struct UltraDev *ptdev);
    int (*Read)(struct UltraDev *ptdev);
    } UltraDevice;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在drv_ ultra.c中实现了一个UltraDev结构体,代码如下:

    static struct UltraDev gDevice = {
    .distance = 0,
        .Init = UltraDevInit,
        .Read = UltraDevRead
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    最后需要向上层应用提供获取SR04设备的接口:

    struct UltraDev *UltraGetDevice(void)
    {
        return &gDevice;
    }
    
    • 1
    • 2
    • 3
    • 4

    34.4.2 初始化设备

    SR04的初始化函数就是初始化IO以及定时器:

    static int UltraDevInit(struct UltraDev *ptdev)
    {
        if(NULL == ptdev)   return -EINVAL;
        gTrigDevice = IODeviceFind("Ultra Trig");
        if(NULL == gTrigDevice)
        {
            printf("Failed to Find Ultra Trig IO!\r\n");
            return -ENXIO;
        }
        if(ESUCCESS != gTrigDevice->Init(gTrigDevice))
        {
            printf("Failed to init GPIO!\r\n");
            return -EIO;
        }
        gTrigDevice->Write(gTrigDevice, 0);
    
        gEchoTimerDevice = TimerDeviceFind("Ultra Echo Timer");
        if(NULL == gEchoTimerDevice)
        {
            printf("Failed to Find Ultra Echo Timer!\r\n");
            return -ENXIO;
        }
        if(ESUCCESS != gEchoTimerDevice->Init(gEchoTimerDevice))
        {
            printf("Failed to init Ultra Echo Time!\r\n");
            return -EIO;
        }
    
        return ESUCCESS;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    34.4.3 测量距离

    当使用Trig发出开始脉冲后,只需要等待读取定时器的状态和计算出来的时间值即可:

    static int UltraDevRead(struct UltraDev *ptdev)
    {
        if(NULL == ptdev)   return -EINVAL;
        gTrigDevice->Write(gTrigDevice, 1);
        udelay(20);
        gTrigDevice->Write(gTrigDevice, 0);
        unsigned int time = 0;
        while(gEchoTimerDevice->Read(gEchoTimerDevice, (unsigned char*)&time, 4) != 4);
        ptdev->distance = (float)(time*34.0/2/1000000.0);
        return ESUCCESS;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 第09行:因为计算出来的时间是ns单位,因而通过声速和时间的公式计算出探测距离。

    34.5 测试程序

    本次实验每隔10ms探测一次距离:

    void DeviceTest(void)
    {
        UartDevicesRegister();
        TimerDevicesRegister();
        IODevicesRegister();
        
        UltraDevice *pDevice = UltraGetDevice();
        if(NULL == pDevice)
        {
            printf("Error. There is no SR04 Ultra device!\r\n");
            return;
        }
        pDevice->Init(pDevice);
        while(1)
        {
            if(ESUCCESS == pDevice->Read(pDevice))
            {
                printf("测试距离:%.4fcm\r\n", pDevice->distance);
            }
            mdelay(10);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    34.6 测试结果

    将程序烧写到开发板上运行可以得到如下图所示的探测结果:


    本章完
  • 相关阅读:
    ubuntu下vscode+cmake实现gtest(cmake引入gtest,glog)
    手把手带你使用ZigBee——通过爱智控制EFR32,以及 Simplicity Studio 使用过程中注意事项
    分类预测 | Matlab实现GA-XGBoost遗传算法优化XGBoost的多特征分类预测
    〖Python 数据库开发实战 - MySQL篇㉜〗- 事务的隔离级别
    软考系列(系统架构师)- 2012年系统架构师软考案例分析考点
    【高级IO】第一讲(5种IO模型的介绍、select函数介绍、一个简单select服务器)
    PageHelper详解
    mongodb 数据分析
    校招|拿到腾讯、阿里、字节等10家互联网测试开发岗的offer
    [SpringBoot系列]消息中间件解决方案
  • 原文地址:https://blog.csdn.net/qq_35181236/article/details/132866765