当前位置: 首页 > news >正文

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特性&#xff0c;而我目前使用的版本&#xff08;Dolphin 小海豚&#xff09;不支持&#xff0c;查看官网说明需要最新版本&#xff0c;所以不得已进行了一下Android Studio版本升级&#xff0c;过程中遇到一些问题&#xff0c;本文仅做记录。&a…...

【JAVA凝气】异常篇

哈喽~大家好呀&#xff0c;这篇来看看JAVA异常篇。 目录 一、前言 二、Exception 异常 1、Java 的非检查性异常 2、Java 检查性异常类 三、Error 错误 四、捕获异常 五、多重捕获块 六、throws/throw 关键字 七、自定义异常类 八、图书推荐 一、前言 异常是程序中的一…...

C++中的函数模板

目录 1. 什么是函数模板&#xff1f; 2. 如何定义函数模板&#xff1f; 3. 如何使用函数模板&#xff1f; 4. 函数模板与函数重载的区别是什么&#xff1f; 5. 函数模板与类模板有何异同点&#xff1f; 1. 什么是函数模板&#xff1f; - 函数模板是一种通用的函数描述&…...

MapReduce【Shuffle-Combiner】

概述 Conbiner在MapReduce的Shuffle阶段起作用&#xff0c;它负责局部数据的聚合&#xff0c;我们可以看到&#xff0c;对于大数据量&#xff0c;如果没有Combiner&#xff0c;将会在磁盘上写入多个文件等待ReduceTask来拉取&#xff0c;但是如果有Combiner组件&#xff0c;我们…...

postman接口自动化测试

Postman除了前面介绍的一些功能&#xff0c;还有其他一些小功能在日常接口测试或许用得上。今天&#xff0c;我们就来盘点一下&#xff0c;如下所示&#xff1a; 1.数据驱动     想要批量执行接口用例&#xff0c;我们一般会将对应的接口用例放在同一个Collection中&#xf…...

历经70+场面试,我发现了大厂面试的套路都是···

今年的金三银四刚刚过去&#xff0c;我又想起了我在去年春招时面试了50余家&#xff0c;加上暑期实习面试了20余家&#xff0c;加起来也面试了70余场的面试场景了。 基本把国内有名的互联网公司都面了一遍&#xff0c;不敢说自己的面试经验很丰富&#xff0c;但也是不差的。 …...

可视区域兼容性问题的思考及方法封装

今日在复习可视化尺寸获取时突发奇想&#xff0c;为什么要在怪异模式下使用document.body.clientWidth&#xff0c;在标准模式下使用document.documentElement.clientWidth&#xff1f;以及是否在IE8及以下的版本中其中一个获取方式将返回undefined或0。  出于该问题的思考&am…...

安全工具 | CMSeeK [指纹识别]

0x00 免责声明 本文仅限于学习讨论与技术知识的分享&#xff0c;不得违反当地国家的法律法规。对于传播、利用文章中提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;本文作者不为此承担任何责任&#xff0c;一旦造成后果请自行承担…...

Android新logcat使用技巧

Android新logcat使用技巧 logcat新UI出现后&#xff0c;我常困惑于怎么过滤log&#xff0c;和以前的UI差异比较大&#xff0c;新UI界面结构如下&#xff1a; 这个新的 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 模型生态系统服务功能社会价值评估&#xff08;基于多源环境QGIS、PostgreSQL、ArcGIS、Maxent、R语言&#xff09; 目录 第一章、理论基础与研究热点 第二章、SolVES 4.0 模型运行环境配置 第三章、SolVES 4.0 模型运行 第四章、数据获取与入…...

Godot引擎 4.0 文档 - 入门介绍 - 学习新功能

本文为Google Translate英译中结果&#xff0c;DrGraph在此基础上加了一些校正。英文原版页面&#xff1a; Learning new features — Godot Engine (stable) documentation in English 学习新功能 Godot 是一个功能丰富的游戏引擎。有很多关于它的知识。本页介绍了如何使用…...

如何进行MySQL漏洞扫描

MySQL是一款广泛使用的关系型数据库管理系统&#xff0c;但由于其复杂的结构和功能&#xff0c;也存在不少安全漏洞&#xff0c;容易被黑客攻击。为了解决这些安全问题&#xff0c;进行MySQL漏洞扫描是必要的。那么MySQL怎么进行漏洞扫描?如何进行漏洞扫描?接下来就让小编带大…...

C语言函数大全-- x 开头的函数(3)

C语言函数大全 本篇介绍C语言函数大全-- x 开头的函数 1. xdr_opaque 1.1 函数说明 函数声明函数功能bool_t xdr_opaque(XDR *xdrs, char *buf, u_int len);用于编码或解码任意长度的二进制数据 参数&#xff1a; xdrs &#xff1a; 指向 XDR 数据结构的指针&#xff0c;表…...

计算机图形学-GAMES101-12阴影

Shadow mapping 问题的提出 我们之前在进行着色时&#xff0c;对于每个物体仅考虑自己&#xff0c;而不考虑其他物体对它的影响。限定在光栅化中&#xff0c;如何解决阴影问题呢&#xff1f;阴影能被摄像机看到&#xff0c;但不能被光源所照亮。经典的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&#xff08;higher order component&#xff09;【高阶组件】 相比较Mixin的优点&#xff1a; 不足&#xff1a; Renderless组件【函数式组件&#xff0c;无渲染组件&#xff0c;Vue社区使用比较多的一种业务复用模式】 优点&#xff1a; M…...

OMA通道-2

1 简介 本文档中指定的 API 使移动应用程序能够访问移动设备中的不同 SE&#xff0c;例如 SIM 或嵌入式 SE。 本规范提供了接口定义和 UML 图&#xff0c;以允许在各种移动平台和不同的编程语言中实现。 如果编程语言支持命名空间&#xff0c;则它应为 org.simalliance.openmob…...

投资于人如何落地?红海云数字化重构国有企业人才价值链

2025年底&#xff0c;中央经济工作会议以“五个必须”概括了新形势下经济工作的规律性认识。其中&#xff0c;“必须坚持投资于物和投资于人紧密结合”这一论断&#xff0c;首次将“投资于人”提升到与“投资于物”同等重要的战略高度。这不仅是对传统增长模式的深刻反思&#…...

04.Python 循环:while+for详解

1. 循环 while或 for后边都记得加:&#xff08;英文冒号&#xff09; 1.1 while 1.1.1 概述 ① 初始化计数器 ② 编写循环条件&#xff08;判断计数器是否达到了目标位置&#xff09; ③ 在循环内部更新计数器 1.1.2 猜数字案例 #适用于 循环次数未知的情况, 例如: 猜数字游戏.…...

微信小程序物流查询插件接入全攻略:从资质申请到waybill_token获取(附完整代码)

微信小程序物流查询插件深度接入指南&#xff1a;全流程解析与实战代码 最近在帮一个电商客户优化小程序时&#xff0c;发现物流查询功能直接影响了30%的用户留存率。微信官方提供的物流查询插件确实能解决这个问题&#xff0c;但接入过程中遇到的坑比想象中多得多。今天就把完…...

结构调整法降AI怎么做?4步把AI率从80%降到30%以内

结构调整法是把AI生成的“标准段落结构“打散重组&#xff0c;通过改变逻辑顺序来消除AI检测特征。原理上可行&#xff0c;但操作比翻译大法更复杂。 我用一篇8000字论文测试了完整流程&#xff0c;结论是&#xff1a;结构调整法效果不如专业工具稳定&#xff0c;但作为人工辅…...

基于RBF(BP)神经网络与PID控制器的自适应控制:方波信号跟踪与参数调整

基于神经网络的自适应PID控制器 通过将RBF&#xff08;BP&#xff09;神经网络和PID控制器相结合&#xff0c;建立了神经网络PID控制器&#xff0c;采用传递函数进行系统建模&#xff0c;通过自动调整PID参数&#xff0c;实现了对方波信号的跟踪。 程序有注释PID控制器作为工业…...

Linux/Android文件系统架构深度剖析

文章目录一、preface1、资料快车2、概述3、专业术语二、Linux文件系统架构1、文件系统框架图2、文件系统之块设备字符设备框架3、内核如何读取文件&#xff1f;4、文件系统类型1&#xff09;持久文件系统1、本地文件系统2、网络文件系统2&#xff09;运行时文件系统3&#xff0…...

作业61 10 11 12

# 输入三角形三边a float(input("请输入三角形的边A&#xff1a;"))b float(input("请输入三角形的边B&#xff1a;"))c float(input("请输入三角形的边C&#xff1a;"))# 判断是否能构成三角形&#xff08;边长>0 且 任意两边之和大于第三…...

跑不出密码别怪字典!实战解析Kali Linux中aircrack-ng跑包效率提升的5个关键技巧

跑不出密码别怪字典&#xff01;实战解析Kali Linux中aircrack-ng跑包效率提升的5个关键技巧 当你盯着终端里aircrack-ng跑包的进度条&#xff0c;看着它缓慢地遍历字典却始终无法命中正确密码时&#xff0c;那种挫败感每个渗透测试者都深有体会。但问题真的出在字典上吗&…...

Comsol弱形式求解三维光子晶体能带:快速而精确的模拟方法探索光子晶体的局域化光学行为

Comsol弱形式求解三维光子晶体能带。深夜两点盯着屏幕上扭曲的能带曲线&#xff0c;突然意识到三维光子晶体的数值模拟就像在量子迷宫里玩俄罗斯方块——每个晶格参数都可能让整个能带结构瞬间崩塌。传统界面操作总让我感觉戴着镣铐跳舞&#xff0c;直到某天偶然翻到COMSOL的弱…...

5分钟上手BilibiliDown:Windows/Mac/Linux三平台通用的B站视频下载神器

5分钟上手BilibiliDown&#xff1a;Windows/Mac/Linux三平台通用的B站视频下载神器 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader &#x1f633; 项目地址: https://gitcode.…...