Linux驱动开发之按键中断实验【完整教程】

【完整代码在文章最后面】

本实验目的是驱动正点原子阿尔法开发板上的KEY0按键,采用中断的方式,并且采用定时器来实现按键消抖,应用程序APP读取按键值并且打印出来。

  
实验前需要了解的知识:

什么是中断:
中断使得硬件得以发出通知给处理器。中断本质上是一种特殊的电信号,由硬件设备发向处理器,不同设备对应的中断不同,而每个中断都通过一个唯一的数字标志。
什么是中断处理函数:
中断处理函数是被内核调用来响应中断的,而他们运行于我们称之为中断上下文的特殊上下文中
什么是中断上下文(中断上下半部)
当执行一个中断处理函数时,内核处于中断上下文中。中断上下文和进程并没有什么瓜葛,中断上下文不可以睡眠,它具有较为严格的时间限制。

一:中断相关函数
(1)Linux中断:每个中断都有一个中断号,通过中断号区分不同中断。
(2)申请中断函数:request_irq()函数是在Linux内核中想要使用某个中断时候需要申请的
(3)释放中断函数:free_irq(unsigned int irq, void *dev).
(4)中断处理函数:irqreturn_t(irq_handler_t)(int , void),使用request_irq函数申请中断的时候需要设置中断处理函数,
(5)中断使能与禁止中断:中断使能函数enable_irq(unsigned int irq),中断禁止函数disable_irq(unsigned int irq),enable_irq和disable_irq用于使能和禁止指定的中断,irq就是要禁止的中断号。

二:上半部和下半部
  request_irq申请中断的时候注册的中断服务函数属于中断处理的上半部,只要中断触发,中断服务函数就会执行,中断处理函数一定要快点执行完毕,越快越好,但事实上事与愿违,于是我们必须缩短中断服务函数的执行时间,这个时候中断处理就分为上半部和下半部了。
  上半部:上半部就是中断处理函数,耗时较短。
  下半部:如果中断处理过程比较耗时,就将这耗时的代码提出来,交给下半部去执行。
  上下半部的没有严格区分,这里给出几个参考点:
①、如果要处理的内容不希望被其他中断打断,那么可以放到上半部。
②、如果要处理的任务对时间敏感,可以放到上半部。
③、如果要处理的任务与硬件有关,可以放到上半部
④、除了上述三点以外的其他任务,优先考虑放到下半部。
这里记录一下下半部机制
(1)软中断
  Linux内核使用结构体softirq_action表示软中断,定义在include/linux/interrupt.h文件中

struct softirq_action
{
   
	void (*action)(struct softirq_action *);
};

软中断描述符有10个,了解一下就好了,要使用软中断,要先注册,使用函数open_softir,注册以后还要使用raise_softirq触发
(2)tasklet
  tasklet是利用软中断来实现的另外一种下半部机制,在软中断和tasklet之间,推荐使用tasklet。Linux内核使用结构体tasklet_struct来定义

struct tasklet_struct
{
   
	struct tasklet_struct *next; /* 下一个 tasklet */
	unsigned long state; /* tasklet 状态 */
	atomic_t count; /* 计数器,记录对 tasklet 的引用数 */
	void (*func)(unsigned long); /* tasklet 执行的函数 */
	unsigned long data; /* 函数 func 的参数 */
};

tasklet也需要用到上半部,知识上半部的中断处理函数重点是调用tasklet_schedule,使用tasklet首先要定义一个tasklet函数,然后是初始化(重点是设置对应的处理函数)
(3)工作队列
  工作队列是另外一种下半部执行方式,工作队列在进程上下文执行,工作队列将要推后的工作交给一个内核线程去执行,工作队列在Linux内核中是一个结构体

struct work_struct {
   
	atomic_long_t data;
	struct list_head entry;
	work_func_t func; /* 工作队列处理函数 */
};

三:设备树中断信息节点
  在imx6ull.dtsi文件中,有中断信息节点,如下

intc: interrupt-controller@00a01000 {
   
	compatible = "arm,cortex-a7-gic";
	#interrupt-cells = <3>;
	interrupt-controller;
	reg = <0x00a01000 0x1000>,
	<0x00a02000 0x100>;
};

**

驱动编写

**
一:修改设备树
  在前几期的Linux驱动开发之按键实验已经编写好了key节点,本次中断实验只需要在原来的节点添加interrupt属性。如下,其中 IRQ_TYPE_EDGE_BOTH是在include/linux/irq.h中有定义。IRQ_TYPE_EDGE_BOTH意味着松开和释放按键都会触发中断

key{
   
		compatible = "alientek,key";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_key>;
		key-gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>;
		status = "okay";
		interrupt-parent = <&gpio1>;
		interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
	};

二:老规矩,摆上创建好的字符设备驱动框架,做驱动嘛,就应该保留好能够兼容其他驱动的框架,全部替换重命名就好了。有需要的也可以复制下面的驱动框架。

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>

#define IMX6UIRQ_CNT 1
#define IMX6UIRQ_NAME "imx6uirq"

/*imx6uirq设备结构体*/
struct imx6uirq_dev{
   
    dev_t devid;
    int major; //主设备号
    int minor; //次设备号
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
};
struct imx6uirq_dev imx6uirq;/*imx6uirq*/

static int imx6uirq_open(struct inode *inode, struct file *filp)
{
   
    filp->private_data = &imx6uirq;
    return 0;
}
static int imx6uirq_release(struct inode *inode, struct file *filp)
{
   
    return 0;
}
static ssize_t imx6uirq_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
   
    int ret;
        
    return 0;
}
/*cdev操作集合*/
static const struct file_operations imx6uirq_fops = {
   
    .owner   = THIS_MODULE,
    .write   = imx6uirq_write,
    .open    = imx6uirq_open,
    .release = imx6uirq_release,
};

/*驱动入口函数*/
static int __init imx6uirq_init(void)
{
   
    int ret = 0;

    /*1注册字符设备驱动*/
    imx6uirq.major = 0;
    if(imx6uirq.major){
    //给定主设备号
        imx6uirq.devid = MKDEV(imx6uirq.major, 0);
        register_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME);
    }else{
     //没有给定设备号
        alloc_chrdev_region(&imx6uirq.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);
        imx6uirq.major = MAJOR(imx6uirq.devid);
        imx6uirq.minor = MINOR(imx6uirq.devid);
    }
    if(ret <0){
   
        goto fail_devid;
    }
    printk("imx6uirq major = %d, imx6uirq = %d \r\n",imx6uirq.major,imx6uirq.minor);

    /*2初始化cdev*/
    imx6uirq.cdev.owner = THIS_MODULE;
    cdev_init(&imx6uirq.cdev, &imx6uirq_fops);//初始化cdev

    /*3添加cdev*/
    cdev_add(&imx6uirq.cdev, imx6uirq
  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Linux驱动开发中,中断是一种重要的机制。在Linux中,中断有两种类型:可抢占的内核代码运行在进程上下文中,而中断上下文则是不可被抢占的,会一直运行直到结束。对于硬件产生的中断,比如按键中断或网卡中断,被称为硬件中断,每个硬件中断都有对应的处理函数。在中断处理函数中,由于中断必须短暂且不能休眠,因此不能使用信号量或互斥锁,而是使用自旋锁来实现对共享资源的保护。此外,在Linux驱动开发中,可以使用计时函数来获取当前的系统时间。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [linux驱动开发-中断](https://blog.csdn.net/weixin_29898767/article/details/124320089)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* [关于Linux驱动开发中断处理](https://blog.csdn.net/weixin_44468026/article/details/119763134)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

拼个世界给自己

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

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

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

打赏作者

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

抵扣说明:

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

余额充值