Linux驱动 | HC-SR501红外热释电驱动(中断&内核线程)

HC-SR501红外热释电驱动

参考:HC-SR501热释电红外传感器驱动(STM32)_一灯大师_Alex的博客-CSDN博客_stm32hc-sr501
https://blog.csdn.net/huazhen1234/article/details/86558294

该模块驱动的使用非常简单,就是使用gpio获取其输出的状态即可,跟按键、LED驱动一样,使用gpio即可

两种编程方式:

  • 使用中断
  • gpio不支持没有中断,使用内核线程轮询获取模块输出状态

驱动实现

1、设备树编写

根节点下添加hc_sr501节点

	hc_sr501 {
	 	compatible = "hc_sr501";
	 	pinctrl-names = "default";
	  	pinctrl-0 = <&pinctrl_key>;
	 	gpios = <&gpio5 1 GPIO_ACTIVE_LOW>;
	 	status = "okay"; 
	 }; 

设备树编译:

ares@ubuntu:~/work/ebf_linux_kernel-ebf_4.19.35_imx6ul$ cat make_dtb.sh
#!/bin/sh

make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs

将设备树拷贝系统目录:

debian@npi:~/nfs_root/driver$ cat cp_dtb_to_linux.sh
#!/bin/sh

sudo cp imx6ull-mmc-npi.dtb /usr/lib/linux-image-4.19.35-carp-imx6/
  • /usr/lib/linux-image-4.19.35-carp-imx6/ ,系统存放设备树的目录

重启系统设备树生效:

sudo reboot

2、驱动实现

自定义数据结构以及头文件包含:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/timer.h>
#include <asm/spinlock.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/of_irq.h>
#include <linux/wait.h>
#include <linux/sched/signal.h> 
#include <linux/poll.h>
#include <linux/kthread.h>

#define HC_SR501_DTS_NAME    "hc_sr501"

#define DEV_NAME    "hc-sr501"

struct hc_sr501 {
    int gpio;
    int irq;
    enum of_gpio_flags flag;
    struct gpio_desc *sr501_gpio;
    dev_t dev;
    struct cdev chrdev;
    struct class *class;
    struct mutex  m_lock;
    wait_queue_head_t  wq;
};

static struct hc_sr501  sr501;
static int sr501_val = 0;

struct task_struct *sr501_task;

驱动硬件和设备树匹配:

static struct file_operations sr501_drv_ops = { 
	.owner	= THIS_MODULE,
	.open   = hc_sr501_drv_open,
    .read   = hc_sr501_drv_read,
    .poll   = hc_sr501_drv_poll,
};

/* 设备树的匹配列表 */
static struct of_device_id dts_match_table[] = {
    {.compatible = HC_SR501_DTS_NAME, },                     /* 通过设备树来匹配 */
};
方式1 - 使用中断
static ssize_t hc_sr501_drv_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    int ret;
#if 0
    DECLARE_WAITQUEUE(wait, current);       /* 定义并初始化等待队列项 */
    add_wait_queue(&sr501.wq, &wait);       /* 将等待队列项加入等待队列 */

    mutex_lock(&sr501.m_lock);
    if (sr501_val == 0)
    {
        if (filp->f_flags & O_NONBLOCK)       /* 使用非阻塞方式 */  
        {
            ret = -EAGAIN;
            goto out;
        }

        /* 阻塞方式 */
        __set_current_state(TASK_INTERRUPTIBLE);     /* 设置进程状态,只是标记为未睡眠状态 */
        mutex_unlock(&sr501.m_lock);                 /* 解锁 */
        schedule();                                  /* 让出CPU,真正让进程睡眠 */
        
        if (signal_pending(current))                 /*  被信号唤醒 */
        {
            ret = -ERESTARTSYS;
            goto out2;
        }                     

        mutex_lock(&sr501.m_lock);
    }

    if (size > 4)
        size = 4;
    /* 将数据拷贝到用户空间 */
    if (copy_to_user(buf, &sr501_val, size)) 
    {
        ret = -EFAULT;
    } 
    else
    {
        ret = size;
    }
    sr501_val = 0;
out:
    mutex_unlock(&sr501.m_lock);
out2:
    remove_wait_queue(&sr501.wq, &wait);            /* 将该进程移除等待队列 */
    set_current_state(TASK_RUNNING);                /* 设置进程为运行态*/
	return ret;
#else
    mutex_lock(&sr501.m_lock);

    if (filp->f_flags & O_NONBLOCK)            /* 使用非阻塞方式 */  
    {
        if (sr501_val == 0)                            /* 无数据 */
        {
            ret = -EAGAIN;
        }
        else
        {
            size = size > 4 ? 4 : size;
            if (copy_to_user(buf, &sr501_val, size))
            {
                ret = -EFAULT;
            } 
            else
            {
                ret = size;
            }
            sr501_val = 0;
        }
        goto out;
    }
    /* 阻塞方式打开的驱动  */
    ret = wait_event_interruptible(sr501.wq, sr501_val);    /* 进程进入休眠等待数据 */
    if (ret == -ERESTARTSYS)                  /* 被信号唤醒 */
    {
        goto out;
    }

    size = size > 4 ? 4 : size;
	if (copy_to_user(buf, &sr501_val, size)) 
    {
        ret = -EFAULT;
    } 
    else 
    {
        ret = size;
    }
    sr501_val = 0;
out:
    mutex_unlock(&sr501.m_lock);            /* 解锁 */
    return ret;
#endif
}

/* 使驱动支持多路复用IO */
__poll_t hc_sr501_drv_poll(struct file *filp, struct poll_table_struct *wait)
{
    __poll_t mask = 0;

    // wait_event_interruptible
    mutex_lock(&sr501.m_lock);

    poll_wait(filp, &sr501.wq, wait); 

    if (sr501_val)
    {
        mask |= POLLIN | POLLRDNORM;      /* 通知用户空间数据可读 */
    }

    mutex_unlock(&sr501.m_lock);

    return mask;
}

static irqreturn_t hc_sr501_isr(int irq_num, void *dev)
{
    int ret = 0;
    // wake_up(&sr501.wq)

    sr501_val = 1;
    ret = gpiod_get_value(sr501.sr501_gpio);
    printk("hc-sr501 gpio %d\r\n", ret);
    // wake_up_interruptible(&sr501.wq);           /* 唤醒等待队列中进入休眠的进程 */
    wake_up(&sr501.wq);           /* 唤醒等待队列中进入休眠的进程 */
 
    printk("hc-sr501 irq %d\r\n", sr501_val);
    return IRQ_RETVAL(IRQ_HANDLED);   
}

static int hc_sr501_driver_probe(struct platform_device *dev)
{ 
    int err;
    struct device *hc_sr501_dev;
    int count;
    
    struct device_node *node = dev->dev.of_node;

    if (!node) {          
        printk("hc-sr501 dts node can not found!\r\n");    
        return -EINVAL; 
    }

    count = of_gpio_count(node);  
    printk("gpio count %d\r\n", count);  

    sr501.sr501_gpio = gpiod_get(&dev->dev, NULL, 0);
     if (IS_ERR(sr501.sr501_gpio)) {              
        dev_err(&dev->dev, "Failed to get GPIO for hc-sr501\n");             
        return PTR_ERR(sr501.sr501_gpio);      
    }

	gpiod_direction_input(sr501.sr501_gpio);

	sr501.irq = gpiod_to_irq(sr501.sr501_gpio);

    err = request_irq(sr501.irq, hc_sr501_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, DEV_NAME, NULL);    /* 申请中断 */
    if (err < 0) {
        printk(KERN_INFO"failed to request irq %d\r\n", sr501.irq);
        return err;
    }

    err = alloc_chrdev_region(&sr501.dev, 0, 1, DEV_NAME);          /* 内核自动分配设备号 */
	if (err < 0) {
		pr_err("Error: failed to register mbochs_dev, err: %d\n", err);
		return err;
	}

	cdev_init(&sr501.chrdev, &sr501_drv_ops);

	cdev_add(&sr501.chrdev, sr501.dev, 1);

    sr501.class = class_create(THIS_MODULE, DEV_NAME);
	if (IS_ERR(sr501.class)) { 
        err = PTR_ERR(sr501.class);
        goto failed1;
	}

    /* 创建设备节点 */
    hc_sr501_dev = device_create(sr501.class , NULL, sr501.dev, NULL, "sr501"); 
    if (IS_ERR(hc_sr501_dev)) {
        // err = -EINVAL;
        err = PTR_ERR(hc_sr501_dev);
		goto failed2;
	}

    init_waitqueue_head(&sr501.wq);     /* 初始化等待队列头  */
    mutex_init(&sr501.m_lock);                 /* 初始化互斥锁  */   
    return 0;
failed2:
    device_destroy(sr501.class, sr501.dev);
    class_destroy(sr501.class);
failed1:
    unregister_chrdev_region(sr501.dev, 1);
    cdev_del(&sr501.chrdev);
    return err;
}

static int hc_sr501_driver_remove(struct platform_device *dev)
{
    device_destroy(sr501.class, sr501.dev);
	class_destroy(sr501.class);
	unregister_chrdev_region(sr501.dev, 1);
    cdev_del(&sr501.chrdev);
    free_irq(sr501.irq, NULL);             /* 释放中断*/
    gpiod_put(sr501.sr501_gpio);           /* 释放gpio*/
    printk(KERN_INFO"hc-sr501 drv remove success\n");

    return 0;
}

static struct platform_driver hc_sr501_platform_driver = {
      .probe = hc_sr501_driver_probe,
      .remove = hc_sr501_driver_remove,
      .driver = {
        .name = HC_SR501_DTS_NAME,
        .owner = THIS_MODULE,
        .of_match_table = dts_match_table,         /* 通过设备树匹配 */
      },
};

驱动出口入口:

// 入口函数
static int __init hc_sr501_driver_init(void)
{
    int ret;
    printk(" %s\n", __FUNCTION__);
    
    ret = platform_driver_register(&hc_sr501_platform_driver);   //注册platform驱动
    return ret;
}

// 出口函数
static void __exit hc_sr501_driver_exit(void)
{
    printk(" %s\n", __FUNCTION__);
    platform_driver_unregister(&hc_sr501_platform_driver);
}

module_init(hc_sr501_driver_init);
module_exit(hc_sr501_driver_exit);

MODULE_AUTHOR("Ares");
MODULE_LICENSE("GPL");

方式2-使用内核线程

static int sr501_capture_thread(void *data)
{
    int prev = 0;

    do {
        mutex_lock(&sr501.m_lock);
        sr501_val = gpiod_get_value(sr501.sr501_gpio);
        mutex_unlock(&sr501.m_lock);
        if (prev != sr501_val) 
        {
            is_data_already = 1;
            wake_up(&sr501.wq);                  /* 唤醒线程*/
            prev = sr501_val;            
        }      
#if 1
        msleep(1000);     
        // printk("%s\n", __FUNCTION__);
#else
        // printk(" %s %s %d\n", __FILE__, __FUNCTION__, cnt++);
        // set_current_state(TASK_INTERRUPTIBLE);       /* 设置内核线程状态 */
        // schedule_timeout(HZ*5);                       /* 阻塞延时 */
        
#endif
    } while (!kthread_should_stop());

    return 0;
}

当驱动和设备树匹配成功,hc_sr501_driver_probe函数会被调用,在该函数中创建启动内核线程:

#if 0
    sr501_task = kthread_create(sr501_capture_thread, NULL, "hc-sr501d");     /* 创建内核线程 */
    if (IS_ERR(sr501_task))
    {
        err = PTR_ERR(sr501_task);
        goto failed2;
    }
    wake_up_process(sr501_task);      /* 唤醒内核线程 sr501_task */
#else
    sr501_task = kthread_run(sr501_capture_thread, NULL, "hc-sr501d");     /* 创建内核线程 */
    if (IS_ERR(sr501_task))
    {
        err = PTR_ERR(sr501_task);
        goto failed2;
    }
#endif

当驱动卸载时hc_sr501_driver_remove函数会被调用,在该函数中停止内核线程:

  kthread_stop(sr501_task);              /* 停止内核线程 */

内核线程中的实现:

static int sr501_capture_thread(void *data)
{
    int prev = 0;

    do {
        mutex_lock(&sr501.m_lock);
        sr501_val = gpiod_get_value(sr501.sr501_gpio);
        mutex_unlock(&sr501.m_lock);
        if (prev != sr501_val) 
        {
            is_data_already = 1;
            wake_up(&sr501.wq);                  /* 唤醒线程*/
            prev = sr501_val;            
        }      
#if 1
        msleep(1000);     
        // printk("%s\n", __FUNCTION__);
#else
        // printk(" %s %s %d\n", __FILE__, __FUNCTION__, cnt++);
        // set_current_state(TASK_INTERRUPTIBLE);       /* 设置内核线程状态 */
        // schedule_timeout(HZ*5);                       /* 阻塞延时 */
        
#endif
    } while (!kthread_should_stop());

    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

欲盖弥彰1314

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值