本教程基于韦东山百问网出的 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 定时器驱动
- 初始化定时器
在初始化的时候除了要打开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;
}
- 轮询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;}elsereturn -EIO;break;}default:break;}return (int)length;
}
- 第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;
在drv_ ultra.c中实现了一个UltraDev结构体,代码如下:
static struct UltraDev gDevice = {
.distance = 0,.Init = UltraDevInit,.Read = UltraDevRead
};
最后需要向上层应用提供获取SR04设备的接口:
struct UltraDev *UltraGetDevice(void)
{return &gDevice;
}
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;
}
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;
}
- 第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);}
}
34.6 测试结果
将程序烧写到开发板上运行可以得到如下图所示的探测结果: