SR04 超声波测距模块

文章介绍了如何使用SR04超声波测距模块进行测距,包括模块的工作原理、设备树配置、驱动程序编写,特别是中断处理和读取时间的机制。在驱动程序中,详细阐述了如何注册file_operations结构体、platform_driver以及中断请求。最后,提供了测试程序的示例代码,展示如何读取距离信息。
摘要由CSDN通过智能技术生成


前言

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


一、SR04 模块介绍

在这里插入图片描述
引脚 :VCCTrigEchoGND
Trig脉冲触发 引脚.
Echo回响接收 引脚.

测距原理 :

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

二、设备树设置

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

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

在这里插入图片描述

三、驱动程序

  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 )。
在这里插入图片描述


总结

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

糖果罐子♡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值