Linux驱动入门实验班——SR501红外模块驱动(附百问网视频链接)

目录

一、工作方式

二、接口图

三、编写思路

1.构造file_operations结构体

2.实现read函数

3.编写入口函数

4.编写中断处理函数

5.编写出口函数

6.声明出入口函数以及协议

四、源码

五、课程链接


一、工作方式

SR501人体红外感应模块有两种工作模式:

通过跳线来设置是否可以重复触发,默认为L。其中L表示不可重复,H表示可重复。含义如下:

①不可重复触发方式:

感应到人体并输出高电平后,延时时间一结束,输出将自动从高电平变为低电平。

②重复触发方式:

感应到人体后输出高电平后,在延时时间段内,如果有人体在其感应范围内活动,其输出将一直保持高电平,直到人离开后才延时将高电平变为低电平(感应模块检测到人体的每一次活动后会自动顺延一个延时时间段,并且以最后一次活动的时间为延时时间的起始点)。

在本次实验中,我们使用的是不可重复触发方式。

二、接口图

根据下面接口图,我们可以算出SR501模块所在的引脚编号为115。

三、编写思路

1.构造file_operations结构体

对于SR501模块我们只需要读取他即可。

cpp 复制代码
static struct file_operations sr501_drv = {
	.owner = THIS_MODULE,
	.read = sr501_drv_read,
};

2.实现read函数

先是构造了,一个环形buf用来存放数据,读取也是直接从这个环形buf中读取数据。此外,还引入了对应用层是使用阻塞 还是非阻塞方式执行的判断。

使用到的函数:

  • wait_event_interruptible()
  • copy_to_user()
  • DECLARE_WAIT_QUEUE_HEAD()

3.编写入口函数

先将gpio编号转换为中断号,然后再申请中断,然后就是注册file_operations结构体

使用到的函数:

  • gpio_to_irq()
  • request_irq()
  • register_chrdev()
  • class_create()
  • device_create()

4.编写中断处理函数

每当产生上升沿或者下降沿时,就会触发中断,这时候就读取引脚电平,将数据放入环形buf。

使用到的函数:

  • gpio_get_value()

5.编写出口函数

释放掉入口函数中注册的资源。

使用到的函数:

  • device_destroy()
  • class_destroy()
  • unregister_chrdev()
  • free_irq()

6.声明出入口函数以及协议

  • module_init()
  • module_exit()
  • MODULE_LICENSE("GPL")

四、源码

驱动

cpp 复制代码
#include "asm-generic/errno-base.h"
#include "asm-generic/gpio.h"
#include "asm/uaccess.h"
#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>

#define BUF_LEN 128

struct gpio_desc{
	int gpio;
	int irq;
	char *name;
	int key;
	struct timer_list key_time;
};

static struct gpio_desc gpios[2] = {
	{115, 0, "sr501"},
};

static int major;
static struct class *sr501_class;
static struct fasync_struct *sr501_fasync;

static int r, w;
static int g_buf[BUF_LEN];

static int is_empty(void)
{
	return (r == w);
}

static int is_full(void)
{
	return (r = ((w + 1) % BUF_LEN));
}

static void put_val(int val)
{
	if (!is_full())
	{
		g_buf[w] = val;
		w = (w + 1) % BUF_LEN;
	}
}

static int get_val(void)
{
	int val = 0;
	if (!is_empty())
	{
		val = g_buf[r];
		r = (r + 1) % BUF_LEN;
	}
	return val;
}

static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);

static ssize_t sr501_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int val;
	int ret;
	if (!is_empty() && (file->f_flags & O_NONBLOCK))
	{
		return -EINVAL;
	}

	wait_event_interruptible(gpio_wait, !is_empty());
	val = get_val();
	ret = copy_to_user(buf, &val, 4);

	return 4;
}


static struct file_operations sr501_drv = {
	.owner = THIS_MODULE,
	.read = sr501_drv_read,
};

static irqreturn_t sr501_isr(int irq, void *dev_id)
{
	int val;
	int key;
	struct gpio_desc *gpio_desc = dev_id;

	val = gpio_get_value(gpio_desc->gpio);
	key = (gpio_desc->key) | (val << 8);
	put_val(key);
	wake_up_interruptible(&gpio_wait);

	return IRQ_HANDLED;
}

static int __init sr501_drv_init(void)
{
	int ret;
	int count = sizeof(gpios) / sizeof(gpios[0]);
	int i;
	for (i = 0; i < count; i++)
	{
		gpios[i].irq = gpio_to_irq(gpios[i].gpio);

		ret = request_irq(gpios[i].irq, sr501_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpios[i].name, &gpios[i]);
	}
	major = register_chrdev(0, "sr501_drv",&sr501_drv);

	sr501_class = class_create(THIS_MODULE, "sr501_class");
	device_create(sr501_class, NULL, MKDEV(major, 0), NULL, "sr501_drv");

	return ret;
}

static void __exit sr501_drv_exit(void)
{
	int i;
	int count = sizeof(gpios) / sizeof(gpios[0]);
	
	device_destroy(sr501_class, MKDEV(major, 0));
	class_destroy(sr501_class);
	unregister_chrdev(major, "sr501_drv");

	for (i = 0; i < count; i++)
	{
		free_irq(gpios[i].irq, &gpios[i]);
	}
}

module_init(sr501_drv_init);
module_exit(sr501_drv_exit);

MODULE_LICENSE
("GPL");

应用

cpp 复制代码
#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>

int main(int argc, char **argv)
{
	int fd;
	int val;
	
	if (argc != 2)
	{
		printf("Usage : %s <dev>\n", argv[0]);
		return -1;
	}

	fd = open(argv[1], O_RDWR);
	if (fd == -1)
	{
		printf("open %s error\n", argv[1]);
		return -1;
	}

	while (1)
	{
		if (read(fd, &val, 4) == 4)
		{
			printf("get sr501 : %d\n", val);
		}
		else
		{
			printf("get sr501 : error\n");
		}
	}

	close(fd);
	return 0;
}

五、课程链接

40_模板1实战_SR501红外模块驱动编程 (100ask.net)https://video.100ask.net/p/t_pc/course_pc_detail/video/v_636c762ce4b0276efeaea816?product_id=p_634cbce4e4b00a4f37500252&content_app_id=&type=6

相关推荐
筏.k13 分钟前
grep、wc 与管道符快速上手指南
linux
Johny_Zhao17 分钟前
华为MAAS、阿里云PAI、亚马逊AWS SageMaker、微软Azure ML各大模型深度分析对比
linux·人工智能·ai·信息安全·云计算·系统运维
CodeOfCC26 分钟前
c语言 封装跨平台线程头文件
linux·c语言·windows
广药门徒26 分钟前
定时器时钟来源可以从输入捕获引脚输入
单片机·嵌入式硬件
科文小白狼28 分钟前
Linux下VSCode开发环境配置(LSP)
linux·vscode·里氏替换原则·lsp
jugt2 小时前
CentOS 7.9安装Nginx1.24.0时报 checking for LuaJIT 2.x ... not found
linux·运维·centos
多多*3 小时前
LUA+Reids实现库存秒杀预扣减 记录流水 以及自己的思考
linux·开发语言·redis·python·bootstrap·lua
何双新4 小时前
第21讲、Odoo 18 配置机制详解
linux·python·开源
21号 14 小时前
9.进程间通信
linux·运维·服务器
爱睡觉的王宇昊4 小时前
二、【ESP32开发全栈指南:ESP32 GPIO深度使用】
单片机·嵌入式硬件