lv14 中断处理原理:接口及按键驱动 14

一、什么是中断

一种硬件上的通知机制,用来通知CPU发生了某种需要立即处理的事件

分为:

  1. 内部中断 CPU执行程序的过程中,发生的一些硬件出错、运算出错事件(如分母为0、溢出等等),不可屏蔽

  2. 外部中断 外设发生某种情况,通过一个引脚的高、低电平变化来通知CPU (如外设产生了数据、某种处理完毕等等)

二、中断处理原理

任何一种中断产生,CPU都会暂停当前执行的程序,跳转到内存固定位置执行一段程序,该程序被称为总的中断服务程序,在该程序中区分中断源,然后进一步调用该中断源对应的处理函数。

中断源对应的处理函数被称为分中断处理程序,一般每一个分中断处理程序对应一个外设产生的中断

写驱动时,如果外设有中断,则需要编写一个函数(分中断处理程序)来处理这种中断

三、中断接口

3.1 中断申请

一般再init中申请 

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
/*
参数:
    irq:所申请的中断号
    handler:该中断号对应的中断处理函数
    flags:中断触发方式或处理方式 
        触发方式:IRQF_TRIGGER_NONE      //无触发
                 IRQF_TRIGGER_RISING    //上升沿触发
                 IRQF_TRIGGER_FALLING  //下降沿触发
                IRQF_TRIGGER_HIGH   //高电平触发
                IRQF_TRIGGER_LOW        //低电平触发
        处理方式:
               IRQF_DISABLED        //用于快速中断,处理中屏蔽所有中断
                IRQF_SHARED       //共享中断
        name:中断名 /proc/interrupts
        dev:传递给中断例程的参数,共享中断时用于区分那个设备,一般为对应设备的结构体地址,无共享中断时写NULL
返回值:成功:0 失败:错误码
*/

3.2 中断释放

一般再exit中释放 

void free_irq(unsigned int irq, void *dev_id);
/*
功能:释放中断号
参数:
    irq:设备号
    dev_id:共享中断时用于区分那个设备一般强转成设备号,无共享中断时写NULL
*/

3.3 中断处理函数原型

typedef irqreturn_t (*irq_handler_t)(int, void *);
/*
参数:
    int:中断号
    void*:对应的申请中断时的dev_id
返回值:
    typedef enum irqreturn irqreturn_t; //中断返回值类型
    enum irqreturn {
        IRQ_NONE    = (0 << 0),   //什么都没处理
        IRQ_HANDLED = (1 << 0),   //处理正常
        IRQ_WAKE_THREAD = (1 << 1),
    };
    返回IRQ_HANDLED表示处理完了,返回IRQ_NONE在共享中断表示不处理
*/

四、按键驱动

重点:区分3种中断编号的含义,以区分中断申请中编号

lv14 中断处理原理:接口及按键驱动 14_第1张图片

  • 专用中断线
  • gpio复用的中断线,所有复用的有个编号(外部中断号,查芯片手册可知)
  • GIC内部组内有个编号
  • 内部中断、外部中断又有个统一的编号

我们通过外部中断号,找到分组编号,再申请对应的内核对应的中断号

内核软件又有个统一的编号,与上面都不一样,int irq

按键原理图:

lv14 中断处理原理:接口及按键驱动 14_第2张图片总的编号 ID

外部编号 Interrupt Source

组内编号SPI Port No(一个分配器可以将其路由到任意组合的处理器的外围中断)

lv14 中断处理原理:接口及按键驱动 14_第3张图片

 

gpx1节点的定义在/linux-3.14/arch/arm/boot/dts/exynos4x12-pinctrl.dtsi

我们通过找到外部中断引脚gpx1,对应芯片手册找到外部编号EINT[9],找到组内编号25

exynos4412-fs4412.dts中增加节点

mykey2_node {
    compatible = "mykey2,key2";
    key2-gpio = <&gpx1 1 0>;  // 0代表工作模式高电平有效
    interrupt-parent = <&gpx1>;
    interrupts = <1 3>;  //1代表图中<0 25 0> 3代表11,上升沿和下降沿都触发
};

设计思路

struct keyvalue
{
	int code;    //which KEY
	int status;  //按下或抬起
};


struct fs4412key2_dev
{
	struct cdev mydev;

	int gpio;   
	int irqno;  

	struct keyvalue data;   
	int newflag;       //1 代表又新按键产生,0代表没有产生
	spinlock_t lock;   //对data 和flag两个共享资源加锁控制,因为即会在异常上下文中使用,也会在任务上下文中使用

	wait_queue_head_t rq;  //为了实现读时候的阻塞
};

五、按键实现

修改设备树

lv14 中断处理原理:接口及按键驱动 14_第4张图片

lv14 中断处理原理:接口及按键驱动 14_第5张图片

 定义

fs4412_key.h

#ifndef FS4412_KEY_H
#define FS4412_KEY_H

enum KEYCODE
{
	KEY2 = 1002,
	KEY3,
	KEY4,
};

enum KEY_STATUS
{
	KEY_DOWN = 0,
	KEY_UP,
};

struct keyvalue
{
	unsigned int code;   //which key
	unsigned int status; //1 0
};

#endif

创建

fs4412_key2.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "fs4412_key.h"


int major = 11;
int minor = 0;
int fs4412key2_num  = 1;

struct fs4412key2_dev
{
	struct cdev mydev;
	
	unsigned int gpio;
	unsigned int irqno;

	struct keyvalue data;
	unsigned int newflag;
	
	spinlock_t lock;

	wait_queue_head_t rq;
};

struct fs4412key2_dev * pgmydev = NULL;

int fs4412key2_open(struct inode *pnode,struct file *pfile)
{
	pfile->private_data =(void *) (container_of(pnode->i_cdev,struct fs4412key2_dev,mydev));
	//暂不处理重复设备打开的情况
	return 0;
}

ssize_t fs4412key2_read(struct file *pfile,char __user *puser,size_t count,loff_t *p_pos)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
	int size = 0;
	int ret = 0;

	if(count < sizeof(struct keyvalue))
	{
		printk("expect read size is invalid\n");
		return -1;
	}

	spin_lock(&pmydev->lock);
	if(!pmydev->newflag)
	{
		if(pfile->f_flags & O_NONBLOCK)
		{//非阻塞
			spin_unlock(&pmydev->lock);
			printk("O_NONBLOCK No Data Read\n");
			return -1;
		}
		else
		{//阻塞
			spin_unlock(&pmydev->lock);
			ret = wait_event_interruptible(pmydev->rq,pmydev->newflag == 1);
			if(ret)
			{
				printk("Wake up by signal\n");
				return -ERESTARTSYS;
			}
			spin_lock(&pmydev->lock);
		}
	}

	if(count > sizeof(struct keyvalue))
	{
		size = sizeof(struct keyvalue);
	}
	else
	{
		size = count;
	}

	ret = copy_to_user(puser,&pmydev->data,size);
	if(ret)
	{
		spin_unlock(&pmydev->lock);
		printk("copy_to_user failed\n");
		return -1;
	}

	pmydev->newflag = 0;

	spin_unlock(&pmydev->lock);

	return size;
}

unsigned int fs4412key2_poll(struct file *pfile,poll_table *ptb)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
	unsigned int mask = 0;

	poll_wait(pfile,&pmydev->rq,ptb);

	spin_lock(&pmydev->lock);
	if(pmydev->newflag)
	{
		mask |= POLLIN | POLLRDNORM;
	}
	spin_unlock(&pmydev->lock);

	return mask;
}

int fs4412key2_close(struct inode *pnode,struct file *pfile)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;

	spin_lock(&pmydev->lock);
	pmydev->newflag = 0;
	spin_unlock(&pmydev->lock);
	return 0;
}

irqreturn_t key2_irq_handle(int no,void *arg)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
	int status = 0;

	status = gpio_get_value(pmydev->gpio);
	mdelay(1); //消抖
	if(status != gpio_get_value(pmydev->gpio))
	{
		return IRQ_NONE;
	}

	spin_lock(&pmydev->lock);
	if(status == pmydev->data.status)
	{
		spin_unlock(&pmydev->lock);
		return IRQ_NONE;
	}

	pmydev->data.code = KEY2;
	pmydev->data.status = status;
	pmydev->newflag = 1;

	spin_unlock(&pmydev->lock);
	wake_up(&pmydev->rq);

	return IRQ_HANDLED;
}

struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = fs4412key2_open,
	.release = fs4412key2_close,
	.read = fs4412key2_read,
	.poll = fs4412key2_poll,
};

int __init fs4412key2_init(void)
{
	int ret = 0;
	dev_t devno = MKDEV(major,minor);

	struct device_node *pnode = NULL;

	pnode = of_find_node_by_path("/fs4412-key2");
	if(NULL == pnode)
	{
		printk("find node failed\n");
		return -1;
	}


	pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);
	if(NULL == pgmydev)
	{
		printk("kmallc for struct fs4412key2_dev failed\n");
		return -1;
	}

	pgmydev->gpio = of_get_named_gpio(pnode,"key2-gpio",0);

	pgmydev->irqno = irq_of_parse_and_map(pnode,0);

	/*申请设备号*/
	ret = register_chrdev_region(devno,fs4412key2_num,"fs4412key2");
	if(ret)
	{
		ret = alloc_chrdev_region(&devno,minor,fs4412key2_num,"fs4412key2");
		if(ret)
		{
			kfree(pgmydev);
			pgmydev = NULL;
			printk("get devno failed\n");
			return -1;
		}
		major = MAJOR(devno);//容易遗漏,注意
	}

	/*给struct cdev对象指定操作函数集*/	
	cdev_init(&pgmydev->mydev,&myops);

	/*将struct cdev对象添加到内核对应的数据结构里*/
	pgmydev->mydev.owner = THIS_MODULE;
	cdev_add(&pgmydev->mydev,devno,fs4412key2_num);


	init_waitqueue_head(&pgmydev->rq);

	spin_lock_init(&pgmydev->lock);
	
	ret = request_irq(pgmydev->irqno,key2_irq_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"fs4412key2",pgmydev);
	if(ret)
	{
		printk("request_irq failed\n");
		cdev_del(&pgmydev->mydev);
		kfree(pgmydev);
		pgmydev = NULL;
		unregister_chrdev_region(devno,fs4412key2_num);
		return -1;
	}
	return 0;
}

void __exit fs4412key2_exit(void)
{
	dev_t devno = MKDEV(major,minor);
	
	free_irq(pgmydev->irqno,pgmydev);

	cdev_del(&pgmydev->mydev);

	unregister_chrdev_region(devno,fs4412key2_num);

	kfree(pgmydev);
	pgmydev = NULL;
}


MODULE_LICENSE("GPL");

module_init(fs4412key2_init);
module_exit(fs4412key2_exit);

修改Makefile

ifeq ($(KERNELRELEASE),)

ifeq ($(ARCH),arm)
KERNELDIR ?= /home/linux/Linux_4412/kernel/linux-3.14
ROOTFS ?= /opt/4412/rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)


modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:
	rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versions

else

CONFIG_MODULE_SIG=n
obj-m += mychar.o
obj-m += mychar_poll.o
obj-m += openonce_atomic.o
obj-m += openonce_spinlock.o
obj-m += mychar_sema.o
obj-m += mychar_mutex.o
obj-m += second.o
obj-m += leddrv.o
obj-m += leddrv_dt.o
obj-m += fs4412_key2.o

endif

测试应用程序编写

testkey2_app.c

#include 
#include 
#include 
#include 
#include 

#include 

#include "fs4412_key.h"

int main(int argc,char *argv[])
{
	int fd = -1;
	struct keyvalue keydata = {0};
	int ret = 0;

	if(argc < 2)
	{
		printf("The argument is too few\n");
		return 1;
	}

	fd = open(argv[1],O_RDONLY);
	if(fd < 0)
	{
		printf("open %s failed\n",argv[1]);
		return 3;
	}

	while((ret = read(fd,&keydata,sizeof(keydata))) == sizeof(keydata))
	{
		if(keydata.status == KEY_DOWN)
		{
			printf("Key2 is down!\n");
		}
		else
		{
			printf("Key2 is up!\n");
		}
	}

	close(fd);
	fd = -1;
	return 0;
}

 编译拷贝到rootfs

 lv14 中断处理原理:接口及按键驱动 14_第6张图片

插入内核模块,添加按键设备测试

lv14 中断处理原理:接口及按键驱动 14_第7张图片 

你可能感兴趣的:(嵌入式开发,linux,arm开发)