首页 > 系统相关 >Linux驱动基础(HC-SR04超声波模块)

Linux驱动基础(HC-SR04超声波模块)

时间:2023-06-09 12:32:04浏览次数:56  
标签:__ SR04 return int sr04 static Linux HC include

(文章目录)


前言

本篇文章将讲解HC-SR04超声波模块的驱动程序编写,有了上篇SR501模块驱动程序编写的基础后这篇文章大家将会学的非常轻松。

一、HC-SR04超声波模块介绍

HC-SR04超声波模块是一种常用于距离测量和障碍物检测的模块。它通过发射超声波信号并接收回波来计算所测量物体与传感器之间的距离。

HC-SR04超声波模块内置有发射器、接收器和控制电路。当模块接收到输入信号后,发射器将发射出一定频率的超声波脉冲信号,该信号在空气中传播并被障碍物反射后,被接收器检测到并转换成电信号返回给模块。模块通过计算从发射到接收所经历时间的差值,即回波延迟时间,乘以声波在空气中的行进速度,得出传感器与障碍物之间的距离。

HC-SR04超声波模块的工作范围一般在2厘米到4米之间,并且可以通过调节工作电压和发送脉冲的频率来改变其工作范围。该模块体积小、功耗低,常用于机器人导航、无人机、汽车避障、智能安防等场景中。

二、超声波时序原理讲解

超声波时序原理的讲解可以看我前面STM32的文章,原理上都是一样的。

地址:超声波模块原理

三、设备树编写

超声波模块需要使用到两个引脚一个是trig触发信号引脚,一个是echo接收信号引脚。trig需要被配置为输出引脚,echo配置为输入引脚,并且配置为中断模式。

这里特别需要注意的是在gpios前面加上trig和echo,这样在驱动里面就可以直接通过名字来找到对应的引脚了。

   sr04 {  /* for imx6ull */
        compatible = "my,sr04";
        trig-gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>;
        echo-gpios = <&gpio4 20 GPIO_ACTIVE_HIGH>;
    };

四、驱动程序编写

1.确定主设备号

主设备设置为0让系统自动帮我们分配主设备号。

static int major=0;/*主设备号*/

2.编写file_operations结构体

我们需要提供这个结构体并且编写其中的open和read函数,供应用程序使用。

static ssize_t sr04_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{
	int err;
	int timeout;
	/*发出至少10us的触发信号*/
	gpiod_set_value(sr04_trig, 1);
	udelay(15);
	gpiod_set_value(sr04_trig, 0);

	/* 等待数据 */
	timeout = wait_event_interruptible_timeout(sr04_wq, sr04_data_ns, HZ);	
	if (timeout)
	{
		err = copy_to_user(buf, &sr04_data_ns, 4);
		sr04_data_ns = 0;
		return 4;
	}
	else
	{
		return -EAGAIN;
	}

	
	return 0;

}

static int sr04_open (struct inode *inode, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}



static struct file_operations sr501_ops={
	.owner		= THIS_MODULE,
	.open		= sr501_open,
	.read		= sr501_read,	
};

3.注册file_operations结构体

在Linux中注册其实就是指在Linux内核中添加我们自己编写的这个file_operations结构体。这个注册的工作在入口函数中完成。

static int __init sr04_init(void)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/*确定主设备号*/
	major=register_chrdev(major, "mysr04", &sr04_ops);
	/*创建类*/
	sr04_class=class_create(THIS_MODULE, "sr04");
	if (IS_ERR(sr04_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "mysr04");
		return PTR_ERR(sr04_class);
	}

	init_waitqueue_head(&sr04_wq);//初始化队列
	
	err=platform_driver_register(&sr04);
	
	return 0;
}

4.出口函数编写

有入口函数就会有出口函数,在入口函数中做的是设备的注册等工作,那么出口函数就是做相反的工作,将设备注销。

static void __exit sr04_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	platform_driver_unregister(&sr04);
	class_destroy(sr04_class);
	unregister_chrdev(major, "mysr04");	
}

module_init(sr04_init);
module_exit(sr04_exit);

MODULE_LICENSE("GPL");

5.probe函数和remove函数编写

创建platform_driver结构体和of_device_id结构体,使用of_device_id结构体中的compatible 属性和设备树进行匹配,匹配完成后会调用到probe函数。

static int sr04_probe(struct platform_device *pdev)
{
	int err;

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/*1.获取硬件信息*/
	sr04_echo=gpiod_get(&pdev->dev, "echo", GPIOD_IN);
	if (IS_ERR(sr04_echo)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	}
	sr04_trig=gpiod_get(&pdev->dev, "trig", GPIOD_OUT_LOW);
	if (IS_ERR(sr04_trig)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	}

	/*得到irq*/
	irq = gpiod_to_irq(sr04_echo);
	/*申请中断并设置为双边沿触发*/
	err = request_irq(irq, sr04_isr, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "sr04", NULL);
	if (err != 0) {
		printk("request_irq is err\n");
	}
	
	/*2.创建设备节点*/	
	device_create(sr04_class, NULL, MKDEV(major, 0), NULL, "sr04");
        
    return 0;	
}

static int sr04_remove(struct platform_device *pdev)
{		
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(sr04_class, MKDEV(major, 0));
	free_irq(irq, NULL);
	gpiod_put(sr04_trig);
	gpiod_put(sr04_echo);
	return 0;
}

static const struct of_device_id my_sr04[] = {
    { .compatible = "my,sr04" },
    { },
};


static struct platform_driver sr04={
	.driver = {
		.name = "sr04",
		.of_match_table = my_sr04,	
	},
	.probe = sr04_probe,
	.remove	= sr04_remove,	
};

7.测试程序编写


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
#include <unistd.h>


/*
 * ./sr04_test /dev/sr04
 *
 */
int main(int argc, char **argv)
{
	int fd;
	int ns;

	int i;
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}


	/* 2. 打开文件 */
//	fd = open(argv[1], O_RDWR | O_NONBLOCK);
	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);
	
	return 0;
}




8.全部驱动程序

#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <linux/delay.h>
#include <linux/timex.h>



static int major=0;
static struct class *sr04_class;
static struct gpio_desc *sr04_echo;
static struct gpio_desc *sr04_trig;
static int irq;
static u64 sr04_data_ns = 0;
static wait_queue_head_t sr04_wq;



static ssize_t sr04_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{
	int err;
	int timeout;
	/*发出至少10us的触发信号*/
	gpiod_set_value(sr04_trig, 1);
	udelay(15);
	gpiod_set_value(sr04_trig, 0);

	/* 等待数据 */
	timeout = wait_event_interruptible_timeout(sr04_wq, sr04_data_ns, HZ);	
	if (timeout)
	{
		err = copy_to_user(buf, &sr04_data_ns, 4);
		sr04_data_ns = 0;
		return 4;
	}
	else
	{
		return -EAGAIN;
	}

	
	return 0;

}

static int sr04_open (struct inode *inode, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}



static struct file_operations sr04_ops={
	.owner		= THIS_MODULE,
	.open		= sr04_open,
	.read		= sr04_read,	
};

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

	if(val)
	{
		/*上升沿*/
		
sr04_data_ns = ktime_get_ns();
	}
	else
	{
		/*下降沿*/
		sr04_data_ns = ktime_get_ns() - sr04_data_ns;
		/* 2. 唤醒APP:去同一个链表把APP唤醒 */
		wake_up(&sr04_wq);
	}

	return IRQ_HANDLED; // IRQ_WAKE_THREAD;
}



static int sr04_probe(struct platform_device *pdev)
{
	int err;

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/*1.获取硬件信息*/
	sr04_echo=gpiod_get(&pdev->dev, "echo", GPIOD_IN);
	if (IS_ERR(sr04_echo)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	}
	sr04_trig=gpiod_get(&pdev->dev, "trig", GPIOD_OUT_LOW);
	if (IS_ERR(sr04_trig)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	}

	/*得到irq*/
	irq = gpiod_to_irq(sr04_echo);
	/*申请中断并设置为双边沿触发*/
	err = request_irq(irq, sr04_isr, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "sr04", NULL);
	if (err != 0) {
		printk("request_irq is err\n");
	}
	
	/*2.创建设备节点*/	
	device_create(sr04_class, NULL, MKDEV(major, 0), NULL, "sr04");
        
    return 0;	
}

static int sr04_remove(struct platform_device *pdev)
{		
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(sr04_class, MKDEV(major, 0));
	free_irq(irq, NULL);
	gpiod_put(sr04_trig);
	gpiod_put(sr04_echo);
	return 0;
}

static const struct of_device_id my_sr04[] = {
    { .compatible = "my,sr04" },
    { },
};


static struct platform_driver sr04={
	.driver = {
		.name = "sr04",
		.of_match_table = my_sr04,	
	},
	.probe = sr04_probe,
	.remove	= sr04_remove,	
};


static int __init sr04_init(void)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/*确定主设备号*/
	major=register_chrdev(major, "mysr04", &sr04_ops);
	/*创建类*/
	sr04_class=class_create(THIS_MODULE, "sr04");
	if (IS_ERR(sr04_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "mysr04");
		return PTR_ERR(sr04_class);
	}

	init_waitqueue_head(&sr04_wq);//初始化队列
	
	err=platform_driver_register(&sr04);
	
	return 0;
}

static void __exit sr04_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	platform_driver_unregister(&sr04);
	class_destroy(sr04_class);
	unregister_chrdev(major, "mysr04");	
}

module_init(sr04_init);
module_exit(sr04_exit);

MODULE_LICENSE("GPL");

总结

本篇文章讲解HC-SR04超声波驱动程序的编写,大家只要掌握好了驱动的基本框架,那么剩下的工作和单片机中的就是一样了。

标签:__,SR04,return,int,sr04,static,Linux,HC,include
From: https://blog.51cto.com/u_16153875/6446760

相关文章

  • Linux磁盘分区扩容
    linux分区常用命令:fdisk,修改MBR分区表,MBR格式,被修改的分区大小最大为2T1.查看磁盘vdc起始柱面fdisk-lvdc1分区start--开始--2048vdc1分区end--结束--1048575999记录好start的起始值2.查看到磁盘vdc已从500G扩容到2T,下面需要对vdc1分区进行扩容3.首先先卸载掉磁盘umonut......
  • Linux内核之 printk 打印
    前言printk在内核源码中用来记录日志信息的函数,只能在内核源码范围内使用,用法类似于printf函数。printk函数主要做两件事情:1.将信息记录到log中;2.调用控制台驱动来将信息输出。一、printk介绍printk将内核信息输出到内核信息缓冲区中,内核缓冲区在kernel/printk/p......
  • Linux下Qt创建共享库与链接共享库详解
    随着程序写的逐渐变多,或多或少的我们都会使用别人写好的库;或者我们不想让别人看到我们的一些核心程序,可以将核心程序封装成库。本次和大家分享的是在Ubuntu下使用Qt生成共享库以及在Qt中链接共享库的方法。 共享库是在Linux下的称呼,在Windows下被称为动态库。这块大家需要了解的是......
  • linux脚本书写笔记
    6月9日,记录init.d的S40network解析判断是否存在某文件: 此语句为判断目录/var/run是否存在(!-e/var/run),如果不存在则创建该目录(mkdir-p/var/run)。其中,!-e是一个逻辑运算符,用于判断文件或目录是否存在,而mkdir命令的-p选项表示如果需要的话,也会创建所有必要的父级目录。......
  • OpenHarmony 3.2 Release新特性解读之驱动HCS
    OpenAtomOpenHarmony(以下简称“OpenHarmony”)开源社区,在今年4月正式发布了OpenHarmony3.2Release版本,标准系统能力进一步完善,提升了系统的整体性能、稳定性和安全性。此次版本对于驱动也提供了一些新的特性,极大的方便了驱动开发工作。其中针对HCS文件开发提供新的开发思路,本文就......
  • dnsmasq( DNS和DHCP)服务
    目录一、简介二、dnsmasq安装三、dnsmasq的解析流程四、dnsmasq的作用以及相关配置五、DHCP服务器设置 一、简介Dnsmasq提供DNS缓存和DHCP服务功能。作为域名解析服务器(DNS),dnsmasq可以通过缓存DNS请求来提高对访问过的网址的连接速度。作为DHCP服务器......
  • Linux安装Redis
    1、系统环境[root@localhost~]#cat/etc/redhat-release2、安装依赖yum -y installgccgcc-c++pcrepcre-develzlibzlib-developensslopenssl-devel3将redis-3.2.1.tar.gz安装包上传到服务器的/usr/local/目录下3.1解压redis压缩包[root@localhostlocal]#tar-xvfre......
  • Linux Shell 脚本创建日期文件夹
    mk-time-dir.sh#!/bin/bashdir_name=$(date'+%Y%m%d%H%M')mkdir$dir_nameecho"已创建文件夹${dir_name}"#已创建文件夹202306090936参考:Linuxdate命令......
  • Linux dlopen系列函数
    Linux提供了一套API来动态装载库,编译时候要加入-ldl(指定dl库) dlopen()函数函数定义#include<dlfcn.h>void*dlopen(constchar*filename,intflag);描述dlopen()用于加载以\0结尾的字符串文件名命名的动态共享对象(或叫动态链接库),返回一个不透明的句柄,此句柄......
  • Linux 通过修改 grub 文件禁用节能模式
    1、执行vi/etc/default/grub命令以编辑该文件 将光标移至GRUB_CMDLINE_LINUX行,在rhgb前新增intel_idle.max_cstate=0intel_pstate=disable字段,输入wq命令,按回车即能保存退出。2、生成启动文件 3、将改动写入镜像 最后执行reboot或者shutdown-rnow命令来......