目录
一、工作方式
二、接口图
三、编写思路
1.构造file_operations结构体
2.实现read函数
3.编写入口函数
4.编写中断处理函数
5.编写出口函数
6.声明出入口函数以及协议
四、源码
五、课程链接
一、工作方式
SR501人体红外感应模块有两种工作模式:
通过跳线来设置是否可以重复触发,默认为L。其中L表示不可重复,H表示可重复。含义如下:
①不可重复触发方式:
感应到人体并输出高电平后,延时时间一结束,输出将自动从高电平变为低电平。
②重复触发方式:
感应到人体后输出高电平后,在延时时间段内,如果有人体在其感应范围内活动,其输出将一直保持高电平,直到人离开后才延时将高电平变为低电平(感应模块检测到人体的每一次活动后会自动顺延一个延时时间段,并且以最后一次活动的时间为延时时间的起始点)。
在本次实验中,我们使用的是不可重复触发方式。
二、接口图
根据下面接口图,我们可以算出SR501模块所在的引脚编号为115。
三、编写思路
1.构造file_operations结构体
对于SR501模块我们只需要读取他即可。
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")
四、源码
驱动
#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");
应用
#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