SR04 超声波测距模块

举报
糖果罐子 发表于 2023/07/26 14:51:53 2023/07/26
【摘要】 SR04 超声波测距模块@TOC 前言超声波测距模块 是利用超声波来测距。模块先发送超声波,然后接收反射回来的超声波,由反射经历的时间和声音的传播速度 340m/s,计算得出距离。本实验采用 中断 的方法,来进行测距。 一、SR04 模块介绍引脚 :VCC、Trig、Echo、GND。Trig 是 脉冲触发 引脚.Echo 是 回响接收 引脚.测距原理 :触发:向Trig(脉冲触发引脚)发出...

SR04 超声波测距模块

@TOC


前言

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


一、SR04 模块介绍

image.png

引脚 :VCCTrigEchoGND
Trig脉冲触发 引脚.
Echo回响接收 引脚.

测距原理 :

  1. 触发:
    向Trig(脉冲触发引脚)发出一个大约10us的高电平。
  2. 发出超声波,接收反射信号:
    模块就自动发出8个40Khz的超声波,超声波遇到障碍物后反射回来,模块收到返回来的超声波。
  3. 回响:
    模块接收到反射回来的超声波后,Echo引脚输出一个与检测距离成比例的高电平。
    我们只要在该引脚为高时,开启定时器计数,在该引脚变为低时,结束定时器计数。根据定时器的计数和定时器频率就可以算出经历时间,根据时间即可推导出距离。

二、设备树设置

设备树 中 compatible 与 驱动程序 进行匹配。
通过原理图可知 TrigEcho 引脚是低电平有效,将其分别接到 开发板的 gpio4-19gpio4-20 引脚。每一组 GPIO 有 32 个引脚。

配置设备树需要对 GPIO 引脚 以及相关的 pincontrol 配置。由于本实验是使用 SR04 模块,所以不需要配置 pincontrol 。

image.png

三、驱动程序

  1. 首先 定义、注册一个==file_operations== 结构体。read 函数便于读取引脚电平。major 是返回的主设备号。

在入口函数里进行 class_create 创建类 , device_create 创建设备节点,register_chrdev 注册 file_operations 结构体。
出口函数里 device_destroyclass_destroy 将其逐个销毁 ,platform_driver_unregister 卸载 file_operations 结构体 。

函数的详细使用可参考 上一篇文章:SR501人体红外模块

	static struct file_operations sr04_fops = {
		.owner	 = THIS_MODULE,
		.read    = sr04_drv_read,
	};

	/* 注册结构体 */
	major = register_chrdev(0, "sr04", &sr04_fops);  
  1. 定义、注册一个==platform_driver==。
    ask100_sr04 用于 设备树和驱动设备匹配。
static const struct of_device_id ask100_sr04[] = {
    { .compatible = "my,sr04"},
    { },
};

static struct platform_driver sr04s_driver = {
    .probe      = sr04_probe,
    .remove     = sr04_remove,
    .driver     = {
        .name   = "100ask_sr04",
        .of_match_table = ask100_sr04,
    },
};

/* 注册 platform_driver */
err = platform_driver_register(&sr04s_driver);
  1. 在==probe== 函数里进行 获取引脚,并对其引脚 初始化。
    使用 gpiod_get 获取对应引脚。参数二 是对应引脚的名字(设备树中自定义节点中的引脚名)。
	/* 设置 trig 初始化时为低电平状态 */
	trig_gpio = gpiod_get(&pdev->dev, "trig",GPIOD_OUT_LOW);
	
	/* 设置 echo初始化时为输入引脚 */
	echo_gpio = gpiod_get(&pdev->dev, "echo",GPIOD_IN);
  1. 获取中断号 irq ,==request_irq== 请求中断。

前面了解到 echo 为输入引脚,trig 为 输出引脚。 (获取中断号 和 请求中断 可以在 probe 函数里实现。)

	/* 获取中断号 */
	irq = gpiod_to_irq(echo_gpio);

	/* 申请中断 */
	request_irq(irq, sr04_isr, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "sr04", NULL);

那什么时候发生中断呢?

request_irq 函数里 可以看到 参数三 IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING。当 电平处于上升沿 或者 下降沿时发生中断(电平发生变化)。

  • 当 电平由 低变高 时,触发中断,记录时间为 t1。此时并不唤醒处于休眠的函数。
  • 当 电平由 高变低 时,触发中断,记录时间为 t2。t = t2 - t1 。 t 是 超声波 从发出到接受的时间,就是声波在待测距离上的往返时间。 这时 就可以唤醒休眠的 read 函数了。

read 函数读取时间 t 后,即可在测试程序中 算出距离 D = 340 * t / 2
在这里插入图片描述

  1. 在 入口函数里 初始化等待队列头。
static wait_queue_head_t sr04_wq;				 // 定义等待队列头对象

init_waitqueue_head(&sr04_wq);					// 初始化等待队列头
  1. 中断处理函数,==wake_up== 唤醒 休眠函数。
    触发中断后调用中断处理函数。

gpiod_get_value 获取相应引脚电平。

ktime_get_ns(); 获取内核启动到现在的时间,在挂起时会暂停。单位是 ns (纳秒)

wake_up 唤醒 在 read 函数里休眠的队列。

static irqreturn_t sr04_isr(int irq, void *dev_id)
{
	int val = gpiod_get_value(echo_gpio);

	if(val)
	{
		sr04_data_ns = ktime_get_ns();			//获取上升沿时的时间
	}
	else
	{
		sr04_data_ns = ktime_get_ns() - sr04_data_ns;		//获取下降沿时的时间,并相减得到高电平时间
		wake_up(&sr04_wq);							//唤醒队列
	}
	
	return IRQ_HANDLED; // IRQ_WAKE_THREAD;
}
  1. 实现 read 函数。
    wait_event_interruptible_timeout 负责 等待队列和超时控制。它的作用是使当前执行的线程(或进程)进入睡眠状态,直到满足指定的条件,或者经过指定的时间。
    gpiod_set_value 设置 trig 输出 不少于 10 us 的高电平。
static ssize_t sr04_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int timeout=0;

	/* 发送10us高电平    , 测量距离 2cm-450cm */
	gpiod_set_value(trig_gpio, 1);
	udelay(15);
	gpiod_set_value(trig_gpio, 0);

	timeout = wait_event_interruptible_timeout(sr04_wq, sr04_data_ns, HZ);
	if(timeout)
	{
		copy_to_user(buf, &sr04_data_ns, 4);
		sr04_data_ns = 0;
		return 4;
	}
	else
	{
		return -EAGAIN;
	}
}

四、测试程序

判断参数,打开文件,读取电平。若引脚为高电平 则读取距离,否则读取错误。

	if (argc != 2) 
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}
	fd = open(argv[1], O_RDWR);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}
	
	while (1)
	{
		if (read(fd, &ns, 4) == 4)
		{
			printf("get distance: %d ns\n", ns);
			printf("get distance: %d mm\n", ns*340/2/1000000);  /* mm */
		}
		else
			printf("get distance: -1\n");
		sleep(1);
	}
	close(fd);

五、上级测试及效果

执行 insmod 命令可以将 .ko 文件加载到内核中,再 执行测试程序。(rmmod命令可以卸载已加载的模块,lsmod 命令 可以观察已加载到内核的文件。)
/dev/sr04 是 驱动程序中创建的设备节点( device_create )。


总结

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。