Linux驱动基础(SR501人体感应模块)
文章目录
- 前言
- 一、SR501模块介绍
- 二、设备树编写
- 三、驱动编写
- 1.确定主设备号
- 2.编写file_operations结构体
- 3.注册file_operations结构体
- 4.出口函数编写
- 5.probe函数和remove函数编写
- 6.中断编写
- 7.测试程序编写
- 8.全部驱动程序
- 总结
前言
本篇文章将给大家介绍一下SR501驱动程序的编写。
一、SR501模块介绍
SR501是一种基于红外线感应原理的人体感应模块,通常被用于安防等一系列自动控制场景中。它主要通过红外线传感器检测感应区域内的人体热辐射,当检测到人体进入这个区域时,输出高电平信号;当人体离开这个区域时,输出低电平信号。
SR501模块整体封装在一块小板子上,板子上有两个旋钮,可以通过旋转它们来调节感应灵敏度和输出信号类型,以适应不同的应用场景。此外,模块还具有自动感应和手动感应两种模式,可以通过调节模式选择开关来进行调节。
SR501模块拥有许多优点,例如可以灵敏地探测人体,响应速度快、稳定性好、安装简单等等,因此被广泛应用于各种人体感应应用场景中。
SR501接线:
VCC----电源
GND----GND
OUT-----GPIO(设置为输入模式)
二、设备树编写
这里我将SR501模块接到了GPIO4_19:
这里主要需要注意的就是compatible属性,使用这个属性来和我们编写的驱动进行匹配。
sr501{compatible = "my,sr501";gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>;};
三、驱动编写
1.确定主设备号
主设备设置为0让系统自动帮我们分配主设备号。
static int major=0;/*主设备号*/
2.编写file_operations结构体
我们需要提供这个结构体并且编写其中的open和read函数,供应用程序使用。
static ssize_t sr501_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{int err;wait_event_interruptible(sr501_wq, sr501_data);err = copy_to_user(buf, &sr501_data, 4);sr501_data = 0;return 0;}static int sr501_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 sr501_init(void)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/*确定主设备号*/major=register_chrdev(major, "mysr501", &sr501_ops);/*创建类*/sr501_class=class_create(THIS_MODULE, "sr501");if (IS_ERR(sr501_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "mysr501");return PTR_ERR(sr501_class);}init_waitqueue_head(&sr501_wq);//初始化队列err=platform_driver_register(&sr501);return 0;
}
4.出口函数编写
有入口函数就会有出口函数,在入口函数中做的是设备的注册等工作,那么出口函数就是做相反的工作,将设备注销。
static void __exit sr501_exit(void)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);platform_driver_unregister(&sr501);class_destroy(sr501_class);unregister_chrdev(major, "mysr501");
}module_init(sr501_init);
module_exit(sr501_exit);MODULE_LICENSE("GPL");
5.probe函数和remove函数编写
创建platform_driver结构体和of_device_id结构体,使用of_device_id结构体中的compatible 属性和设备树进行匹配,匹配完成后会调用到probe函数。
static const struct of_device_id my_sr501[] = {{ .compatible = "my,sr501" },{ },
};static struct platform_driver sr501={.driver = {.name = "sr501",.of_match_table = my_sr501, },.probe = sr501_probe,.remove = sr501_remove,
};
static int sr501_probe(struct platform_device *pdev)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/*1.获取硬件信息*/sr501_gpio=gpiod_get(&pdev->dev, NULL, GPIOD_IN);if (IS_ERR(sr501_gpio)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);}/*得到irq*/irq = gpiod_to_irq(sr501_gpio);/*申请中断并设置为双边沿触发*/err = request_irq(irq, sr501_isr, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "sr501", NULL);if (err != 0) {printk("request_irq is err\n");}/*2.创建设备节点*/ device_create(sr501_class, NULL, MKDEV(major, 0), NULL, "sr501");return 0;
}static int sr501_remove(struct platform_device *pdev)
{ printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(sr501_class, MKDEV(major, 0));free_irq(irq, NULL);gpiod_put(sr501_gpio);return 0;
}
6.中断编写
这里我们使用中断来实现SR501的核心功能,因为当检测到有人的时候SR501表现为高电平,没有人的时候SR501则表现为低电平。那么这样我们就可以把SR501的引脚配置为中断引脚,当电平发生变化的时候就会产生中断。
这里使用到了wait_queue_head_t 定义了一个等待队列,当有人的时候使用wake_up函数唤醒等待队列读取数据。
static wait_queue_head_t sr501_wq;/*等待队列*/static irqreturn_t sr501_isr(int irq, void *dev_id)
{int val = gpiod_get_value(sr501_gpio);if(val){sr501_data = 1;wake_up(&sr501_wq);}return IRQ_HANDLED; // IRQ_WAKE_THREAD;
}
7.测试程序编写
有了驱动程序后相应的也需要一个应用程序来测试驱动代码是否正确,在应用程序中使用read函数来读取数据。
#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>/** ./sr501_test /dev/sr501**/
int main(int argc, char **argv)
{int fd;int data;/* 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){read(fd, &data, 4);if (data){printf("have people\n");}else{printf("no people\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 *sr501_class;
static struct gpio_desc *sr501_gpio;/*sr501 gpio*/
static int sr501_data = 0;
static int irq;/*中断号*/
static wait_queue_head_t sr501_wq;/*等待队列*/static ssize_t sr501_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{int err;wait_event_interruptible(sr501_wq, sr501_data);err = copy_to_user(buf, &sr501_data, 4);sr501_data = 0;return 0;}static int sr501_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,
};static irqreturn_t sr501_isr(int irq, void *dev_id)
{int val = gpiod_get_value(sr501_gpio);if(val){sr501_data = 1;wake_up(&sr501_wq);}return IRQ_HANDLED; // IRQ_WAKE_THREAD;
}static int sr501_probe(struct platform_device *pdev)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/*1.获取硬件信息*/sr501_gpio=gpiod_get(&pdev->dev, NULL, GPIOD_IN);if (IS_ERR(sr501_gpio)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);}/*得到irq*/irq = gpiod_to_irq(sr501_gpio);/*申请中断并设置为双边沿触发*/err = request_irq(irq, sr501_isr, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "sr501", NULL);if (err != 0) {printk("request_irq is err\n");}/*2.创建设备节点*/ device_create(sr501_class, NULL, MKDEV(major, 0), NULL, "sr501");return 0;
}static int sr501_remove(struct platform_device *pdev)
{ printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(sr501_class, MKDEV(major, 0));free_irq(irq, NULL);gpiod_put(sr501_gpio);return 0;
}static const struct of_device_id my_sr501[] = {{ .compatible = "my,sr501" },{ },
};static struct platform_driver sr501={.driver = {.name = "sr501",.of_match_table = my_sr501, },.probe = sr501_probe,.remove = sr501_remove,
};static int __init sr501_init(void)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/*确定主设备号*/major=register_chrdev(major, "mysr501", &sr501_ops);/*创建类*/sr501_class=class_create(THIS_MODULE, "sr501");if (IS_ERR(sr501_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "mysr501");return PTR_ERR(sr501_class);}init_waitqueue_head(&sr501_wq);//初始化队列err=platform_driver_register(&sr501);return 0;
}static void __exit sr501_exit(void)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);platform_driver_unregister(&sr501);class_destroy(sr501_class);unregister_chrdev(major, "mysr501");
}module_init(sr501_init);
module_exit(sr501_exit);MODULE_LICENSE("GPL");
总结
本篇文章就讲解到这里,只要掌握Linux驱动的核心框架写一个驱动程序并不是那么困难。
相关文章:
Linux驱动基础(SR501人体感应模块)
文章目录 前言一、SR501模块介绍二、设备树编写三、驱动编写1.确定主设备号2.编写file_operations结构体3.注册file_operations结构体4.出口函数编写5.probe函数和remove函数编写6.中断编写7.测试程序编写8.全部驱动程序 总结 前言 本篇文章将给大家介绍一下SR501驱动程序的编…...
Android Studio Flamingo (火烈鸟) 升级踩坑记录
由于想要验证Compose最新的debug特性,而我目前使用的版本(Dolphin 小海豚)不支持,查看官网说明需要最新版本,所以不得已进行了一下Android Studio版本升级,过程中遇到一些问题,本文仅做记录。&a…...
【JAVA凝气】异常篇
哈喽~大家好呀,这篇来看看JAVA异常篇。 目录 一、前言 二、Exception 异常 1、Java 的非检查性异常 2、Java 检查性异常类 三、Error 错误 四、捕获异常 五、多重捕获块 六、throws/throw 关键字 七、自定义异常类 八、图书推荐 一、前言 异常是程序中的一…...
C++中的函数模板
目录 1. 什么是函数模板? 2. 如何定义函数模板? 3. 如何使用函数模板? 4. 函数模板与函数重载的区别是什么? 5. 函数模板与类模板有何异同点? 1. 什么是函数模板? - 函数模板是一种通用的函数描述&…...
MapReduce【Shuffle-Combiner】
概述 Conbiner在MapReduce的Shuffle阶段起作用,它负责局部数据的聚合,我们可以看到,对于大数据量,如果没有Combiner,将会在磁盘上写入多个文件等待ReduceTask来拉取,但是如果有Combiner组件,我们…...
postman接口自动化测试
Postman除了前面介绍的一些功能,还有其他一些小功能在日常接口测试或许用得上。今天,我们就来盘点一下,如下所示: 1.数据驱动 想要批量执行接口用例,我们一般会将对应的接口用例放在同一个Collection中…...
历经70+场面试,我发现了大厂面试的套路都是···
今年的金三银四刚刚过去,我又想起了我在去年春招时面试了50余家,加上暑期实习面试了20余家,加起来也面试了70余场的面试场景了。 基本把国内有名的互联网公司都面了一遍,不敢说自己的面试经验很丰富,但也是不差的。 …...
可视区域兼容性问题的思考及方法封装
今日在复习可视化尺寸获取时突发奇想,为什么要在怪异模式下使用document.body.clientWidth,在标准模式下使用document.documentElement.clientWidth?以及是否在IE8及以下的版本中其中一个获取方式将返回undefined或0。 出于该问题的思考&am…...
安全工具 | CMSeeK [指纹识别]
0x00 免责声明 本文仅限于学习讨论与技术知识的分享,不得违反当地国家的法律法规。对于传播、利用文章中提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本文作者不为此承担任何责任,一旦造成后果请自行承担…...
Android新logcat使用技巧
Android新logcat使用技巧 logcat新UI出现后,我常困惑于怎么过滤log,和以前的UI差异比较大,新UI界面结构如下: 这个新的 logcat 的问题是如何过滤信息并不是很明显。 获取应用的日志信息 要获取我们当前调试应用的日志信息&…...
使用Makefile笔记总结
文章目录 一、简单了解Makefile1.1 Makefile示例1.2 基本规则1.3 make是如何工作的1.4 使用变量1.5 make自动推导 二、变量2.1 变量的定义和引用2.2 变量的两种高级用法2.3 override 和 define 关键字2.4 环境变量与目标变量2.5 自动变量 三、Makefile规则3.1 通配符3.2 目标依…...
npm下载依赖项目跑不起来--解决方案
code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! While resolving: vue-element-admin4.4.0 npm ERR! Found: webpack4.46.0 npm ERR! node_modules/webpack npm ERR! webpack“^4.23.0” from the root project npm ERR! npm ERR! Coul…...
SolVES模型生态系统服务功能社会价值评估
查看原文>>>SolVES 模型生态系统服务功能社会价值评估(基于多源环境QGIS、PostgreSQL、ArcGIS、Maxent、R语言) 目录 第一章、理论基础与研究热点 第二章、SolVES 4.0 模型运行环境配置 第三章、SolVES 4.0 模型运行 第四章、数据获取与入…...
Godot引擎 4.0 文档 - 入门介绍 - 学习新功能
本文为Google Translate英译中结果,DrGraph在此基础上加了一些校正。英文原版页面: Learning new features — Godot Engine (stable) documentation in English 学习新功能 Godot 是一个功能丰富的游戏引擎。有很多关于它的知识。本页介绍了如何使用…...
如何进行MySQL漏洞扫描
MySQL是一款广泛使用的关系型数据库管理系统,但由于其复杂的结构和功能,也存在不少安全漏洞,容易被黑客攻击。为了解决这些安全问题,进行MySQL漏洞扫描是必要的。那么MySQL怎么进行漏洞扫描?如何进行漏洞扫描?接下来就让小编带大…...
C语言函数大全-- x 开头的函数(3)
C语言函数大全 本篇介绍C语言函数大全-- x 开头的函数 1. xdr_opaque 1.1 函数说明 函数声明函数功能bool_t xdr_opaque(XDR *xdrs, char *buf, u_int len);用于编码或解码任意长度的二进制数据 参数: xdrs : 指向 XDR 数据结构的指针,表…...
计算机图形学-GAMES101-12阴影
Shadow mapping 问题的提出 我们之前在进行着色时,对于每个物体仅考虑自己,而不考虑其他物体对它的影响。限定在光栅化中,如何解决阴影问题呢?阴影能被摄像机看到,但不能被光源所照亮。经典的Shadow mapping只能处理…...
iOS_Swift高阶函数
iOS_Swift高阶函数 #mermaid-svg-NxX1czIESDq47OQw {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-NxX1czIESDq47OQw .error-icon{fill:#552222;}#mermaid-svg-NxX1czIESDq47OQw .error-text{fill:#552222;stroke:#…...
探索Vue的组件世界-组件复用
目录 Mixin【混入】 缺陷 HOC(higher order component)【高阶组件】 相比较Mixin的优点: 不足: Renderless组件【函数式组件,无渲染组件,Vue社区使用比较多的一种业务复用模式】 优点: M…...
OMA通道-2
1 简介 本文档中指定的 API 使移动应用程序能够访问移动设备中的不同 SE,例如 SIM 或嵌入式 SE。 本规范提供了接口定义和 UML 图,以允许在各种移动平台和不同的编程语言中实现。 如果编程语言支持命名空间,则它应为 org.simalliance.openmob…...
后端开发者体验 AI 前端:用 TinyVue 做一个智能业务表单 Demo
摘要 作为 Java 后端开发者,我平时更多关注接口、SQL 和业务逻辑,但后台系统里也绕不开表单、列表和报表页面。本文结合 OpenTiny NEXT 学习体验,用 TinyVue 做一个智能业务表单 Demo,聊聊 AI 前端对后端开发者到底有没有实际帮助…...
如何精准识别区域内的产学研合作机会?
核心要点 产学研精准对接的核心矛盾在于供需两端“底数不清”,必须用产业知识图谱对企业技术家底做CT扫描,再以持证技术经纪人入企二次挖掘,才能将模糊的升级意愿转化为可决策的结构化需求。告别“签完即凉”的致命伤,关键不是办更…...
网盘直链下载助手:彻底告别限速的终极免费解决方案
网盘直链下载助手:彻底告别限速的终极免费解决方案 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘…...
医用超声图像干扰伪像算法:原理、识别与抑制技术综述
引言 医用超声成像因其无创、实时、低成本等优点,已成为临床诊断不可或缺的工具。然而,超声图像质量极易受到各种物理因素和系统限制的影响,从而产生干扰伪像。这些伪像并非真实的解剖结构,而是由声波传播特性、设备硬件、操作手法等因素导致的虚假或失真的图像信息。准确…...
展锐RM500U 5G CPE固件升级避坑指南:为什么你的QFlash总卡在‘开始下载’?
展锐RM500U 5G CPE固件升级疑难解析:从QFlash卡顿到完美升级的实战手册 当你的展锐RM500U 5G CPE设备需要固件升级时,QFlash工具本应是简单高效的解决方案。然而,许多用户在点击"Start"按钮后,却遭遇了进度条停滞不前的…...
AI写论文神器合集!4款AI论文写作工具,解决你的论文烦恼!
AI写论文工具测评 在2025年,学术写作正在经历一场智能化的浪潮,越来越多的人开始尝试使用AI写论文工具。尽管这些工具的数量众多,但在撰写硕士或博士论文等长篇学术作品时,它们往往面临很多挑战。许多AI写论文工具缺乏必要的理论…...
数据集上新:柬埔寨环境健康入户调查
本数据集基于柬埔寨马德望省约400户家庭的环境健康入户调查而成,包括基本社会经济信息、家庭成员结构、呼吸道健康信息、其他健康信息(包括部分测量信息)、营养信息、清洁炉灶和燃料使用、风险和时间偏好、调查员自观察信息等数百条子数据。如…...
Trae+Playwright MCP:企业级浏览器自动化测试底座构建指南
1. 这不是又一个“安装教程”,而是一套能跑通、能维护、能交付的浏览器自动化测试底座你有没有遇到过这样的情况:项目刚立项,测试同学信心满满说“用Playwright写自动化脚本”,结果三天过去,环境还卡在npm install pla…...
无Root安卓隐私检测:Frida+Camille实战指南
1. 为什么“不Root也能做隐私检测”这件事值得大书特书 去年在给一家金融类App做第三方合规评估时,客户明确提了一条硬性要求:“所有检测必须在未Root的量产机上完成,测试环境要完全模拟真实用户场景。”当时我第一反应是皱眉——毕竟市面上…...
Unity 2D游戏地图制作:从零上手Tile Palette的7个核心工具(附快捷键清单)
Unity 2D游戏地图制作:从零上手Tile Palette的7个核心工具(附快捷键清单)在独立游戏开发领域,2D游戏因其独特的艺术风格和相对较低的开发门槛,始终保持着旺盛的生命力。无论是复古风格的平台跳跃游戏,还是精…...
