正点原子linux阿尔法开发板使用——Linux中断实验

中断

1、先知道需要使用的中断对应的中断号

2、先申请request_irq,此函数会激活中断。

正点原子linux阿尔法开发板使用——Linux中断实验_第1张图片
3、如果不用中断了,那就时放点,free_irq;

4、中断处理函数 irqreturn_t (*irq_handler_t) (int, void *)

5、5、中断使能与禁止函数
常用的中断使用和禁止函数如下所示:

void enable_irq(unsigned int irq)
void disable_irq(unsigned int irq)

正点原子linux阿尔法开发板使用——Linux中断实验_第2张图片
正点原子linux阿尔法开发板使用——Linux中断实验_第3张图片

2022.5.31

上半部和下半部

上半部分占用时间少,下半部分占用时间多。

中断处理一定要越快越好。

参考点:
①、如果要处理的内容不希望被其他中断打断,那么可以放到上半部。
②、如果要处理的任务对时间敏感,可以放到上半部。
③、如果要处理的任务与硬件有关,可以放到上半部
④、除了上述三点以外的其他任务,优先考虑放到下半部。

1、软中断

正点原子linux阿尔法开发板使用——Linux中断实验_第4张图片
正点原子linux阿尔法开发板使用——Linux中断实验_第5张图片
要先注册软中断、要先注册。软中断要在编译的时候静态使用!!!
正点原子linux阿尔法开发板使用——Linux中断实验_第6张图片
触发软中断:
正点原子linux阿尔法开发板使用——Linux中断实验_第7张图片
*** 软中断不要去用!!!

正点原子linux阿尔法开发板使用——Linux中断实验_第8张图片
也需要使用到上半部,只是在上半部的中断处理函数重点调用的是tasklet_schedule

1、定义一个tasklet。

2、初始化、重点是设置对应的处理函数。

正点原子linux阿尔法开发板使用——Linux中断实验_第9张图片
对于 gpio 来说,gpio 节点也可以作为中断控制器,比如 imx6ull.dtsi 文件中的 gpio5 节点内容如下所示:

示例代码 51.1.3.2 gpio5 设备节点
gpio5: gpio@020ac000 {
		compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
		reg = <0x020ac000 0x4000>;
		interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,
		<GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
		gpio-controller;
		#gpio-cells = <2>;
		interrupt-controller;
		#interrupt-cells = <2>;
};

如果需要写驱动的话 需要做的事情。

正点原子linux阿尔法开发板使用——Linux中断实验_第10张图片

驱动代码编写

添加中断信息

	key {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "atkalpha-key";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_key>;
		key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */
		
		interrupt-parent = <&gpio1>;
		interrupts = <18 IRQ_TYPE_EDGE_BOTH>; /* FALLING RISING */
		status = "okay";
	};

加载驱动失败的原因是因为:
两个节点都使用了 同一个GPIO
正点原子linux阿尔法开发板使用——Linux中断实验_第11张图片
原厂驱动代码解析 按键设备树。
正点原子linux阿尔法开发板使用——Linux中断实验_第12张图片

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


#define	  IMX6ULIRQ_NAME "imx6ul"
#define   IMX6ULIRQ_COUNT 1 

#define IMX6ULIRQOFF 0 /* 关灯 */
#define IMX6ULIRQON 1 /* 开灯 */



#define KEY_NUM 	1

#define KEY0_VALUE 0X01
#define INVAKEY 0XFF


/*按键结构体*/
struct irq_keydesc{
	int gpio;			/*io编号*/
	int irqnum;			/*中断号*/
	unsigned char value;	/*键值*/
	char name[10];		/*名字*/
	irqreturn_t (*handler)(int ,void *);					/*中断处理函数*/

};



struct imx6uirq_dev {
	
	struct cdev cdev;
	struct class *class;/*类:为了自动创建节点*/
	struct device *device;/*设备:为了自动创建节点*/
	dev_t devid; //设备号
	int major;   //主设备号
	int minor;   //次设备号
	struct device_node *nd; //设备节点
	int led_gpio;

	struct irq_keydesc	irqkey[KEY_NUM];

	struct timer_list	timer;

	atomic_t keyvalue;
	
	atomic_t releasekey; //
	

	struct mutex lock;
};

struct imx6uirq_dev imx6uirq; //imx6uirq设备

static int imx6uirq_open(struct inode *inode,struct file *filp)
{
	filp->private_data = &imx6uirq;

	return 0;
}

static ssize_t imx6uirq_read(struct file *filp,char __user *buf,size_t cnt, loff_t *offt)
{
	int ret = 0;
	unsigned char keyvalue;
	unsigned char releasekey;
	struct imx6uirq_dev *dev = filp->private_data;

	keyvalue = atomic_read(&dev->keyvalue);
	releasekey = atomic_read(&dev->releasekey);
	if (releasekey)
	{
		if (keyvalue & 0x80)
		{
			keyvalue &= ~0x80;
			ret = copy_to_user(buf,&keyvalue,sizeof(keyvalue));
		}
		else{
			goto data_error;
		}
		atomic_set(&dev->releasekey,0);//按下标志清0
	}else{
		goto data_error;
	}


	return ret;

data_error:
	return -EINVAL;

}

static ssize_t imx6uirq_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{
	//struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;
	
	return 0;
}

static int imx6uirq_release(struct inode *inode,struct file *filp)
{
	//struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;

	return 0;
}

static irqreturn_t key0_handler(int irq,void *dev_id)
{

	struct imx6uirq_dev *dev = dev_id;	

#if 0
	value = gpio_get_value(dev->irqkey[0].gpio);
	if (value == 0) 
	{
		printk("key 0 press\r\n");
	}
	else if (value == 1)
	{
		printk("key 0 release\r\n");
	}
#endif
	dev->timer.data = (volatile unsigned long)dev_id;
	mod_timer(&dev->timer,jiffies + msecs_to_jiffies(10)); //10ms
	return IRQ_HANDLED;
}

/*定时器处理函数*/
static void timer_func(unsigned long arg)
{
	int value = 0;

	struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;	

	value = gpio_get_value(dev->irqkey[0].gpio);
	if (value == 0) 
	{
		atomic_set(&dev->keyvalue,dev->irqkey[0].value);
		printk("key 0 press\r\n");
	}
	else if (value == 1)
	{
		atomic_set(&dev->keyvalue,0x80|(dev->irqkey[0].value));
		atomic_set(&dev->releasekey,1);//完整的释放 按键完成之后写入1
		printk("key 0 release\r\n");
	}

	//重新开启定时器
	//mod_timer(&dev->timer,jiffies + msecs_to_jiffies(dev->timerperiod));
	
}


static int keyio_init(struct imx6uirq_dev *dev)
{
	/*1、按键的初始化*/
	int i = 0;
	int ret = 0;
	dev->nd = of_find_node_by_path("/key");
	if (dev->nd == NULL)
	{
		printk("key node not find!\r\n");
		return -EINVAL;

	}

	for(i = 0; i <KEY_NUM;i++){
		
		dev->irqkey[i].gpio = of_get_named_gpio(dev->nd,"key-gpio",i);
		if (dev->irqkey[i].gpio < 0) {
		printk("can't get key%d\r\n", i);
		}
		
	}
	for(i = 0; i <KEY_NUM;i++){
		// /*获取按键的IO编号*/
		// dev->irqkey[i].gpio = of_get_named_gpio(dev->nd,"key-gpios",i);


		memset(dev->irqkey[i].name,0,sizeof(dev->irqkey[i].name));

		sprintf(dev->irqkey[i].name,"KEY%d",i);

		gpio_request(dev->irqkey[i].gpio,dev->irqkey[i].name);

		gpio_direction_input(dev->irqkey[i].gpio);

		dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);

		printk("key%d:gpio=%d, irqnum=%d\r\n",i,dev->irqkey[i].gpio,dev->irqkey[i].irqnum);
		//或者
		//dev->irqkey[i].irqnum = irq_of_parse_and_map(dev->nd,i);
	}


	dev->irqkey[0].handler = key0_handler;
	dev->irqkey[0].value = KEY0_VALUE;
	/*2、中断初始化*/


	for(i = 0; i <KEY_NUM;i++){
		ret = request_irq(dev->irqkey[i].irqnum,dev->irqkey[i].handler,
		IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
		dev->irqkey[i].name,&imx6uirq);

		if (ret<0)
		{
			printk("irq request_irq err");
			goto fail_irq;
		}
	}

	/*3 初始化定时器*/
	init_timer(&imx6uirq.timer);

	//imx6uirq.timer.expires 	= jiffies + msecs_to_jiffies(imx6uirq.timerperiod);

	imx6uirq.timer.function = timer_func;

	imx6uirq.timer.data = (unsigned long)&imx6uirq;

	return 0;

fail_irq:
	for(i = 0; i <KEY_NUM;i++)
		gpio_free(dev->irqkey[i].gpio);
	return ret;
}


static struct file_operations imx6uirq_fops = {
	.owner = THIS_MODULE,
	.write = imx6uirq_write,
	.open = imx6uirq_open,
	.release = imx6uirq_release,
	.read = imx6uirq_read,
};

static int __init imx6uirq_init(void)
{

	int ret = 0; 

	/*互斥体初始化*/
	mutex_init(&imx6uirq.lock);


	/*注册字符设备*/
	imx6uirq.major = 0;
	if (imx6uirq.major){	
		imx6uirq.devid = MKDEV(imx6uirq.major,0);//设备号
		ret = register_chrdev_region(imx6uirq.devid,IMX6ULIRQ_COUNT,IMX6ULIRQ_NAME);
	}	else{
		ret = alloc_chrdev_region(&imx6uirq.devid,0,IMX6ULIRQ_COUNT,IMX6ULIRQ_NAME);
		imx6uirq.major = MAJOR(imx6uirq.devid);
		imx6uirq.minor = MINOR(imx6uirq.devid);
	}printk("led major = %d led minor = %d \r\n",imx6uirq.major,imx6uirq.minor);
	if (ret < 0){
		goto faile_devid;
	}

	/*2、添加字符设备*/
	imx6uirq.cdev.owner = THIS_MODULE;
	cdev_init(&imx6uirq.cdev,&imx6uirq_fops);
	ret = cdev_add(&imx6uirq.cdev,imx6uirq.devid,IMX6ULIRQ_COUNT);
	if(ret<0){
		goto fail_cdev;
	}
	/*3、创建设备类*/
	imx6uirq.class = class_create(THIS_MODULE,IMX6ULIRQ_NAME);
	if(IS_ERR(imx6uirq.class)){
		ret = PTR_ERR(imx6uirq.class);
		printk("can't class_create \r\n");
		goto fail_class;
	}
	/*创建设备节点*/
	imx6uirq.device = device_create(imx6uirq.class,NULL,imx6uirq.devid,NULL,IMX6ULIRQ_NAME);
	if(IS_ERR(imx6uirq.device)){
		ret = PTR_ERR(imx6uirq.device);
		printk("can't device_create \r\n");
		goto fail_device;
	}
	

	ret = keyio_init(&imx6uirq);
	if (ret<0)
	{
		goto fail_keyinit;
	}


	atomic_set(&imx6uirq.keyvalue,INVAKEY);
	atomic_set(&imx6uirq.releasekey,0);
	
	

fail_keyinit:
	device_destroy(imx6uirq.class,imx6uirq.devid);
fail_device:
	class_destroy(imx6uirq.class);
fail_class:
	cdev_del(&imx6uirq.cdev);
fail_cdev:
	unregister_chrdev_region(imx6uirq.devid,IMX6ULIRQ_COUNT);
faile_devid:
	return ret;

}

static void __exit imx6uirq_exit(void)
{
	int i = 0;
	
	//1、释放中断
	for(i = 0; i <KEY_NUM;i++)
	free_irq(imx6uirq.irqkey[i].irqnum,&imx6uirq);

	//2、释放IO
	for(i = 0; i <KEY_NUM;i++)
	gpio_free(imx6uirq.irqkey[i].gpio);

	/*删除定时器*/
	del_timer_sync(&imx6uirq.timer);

	/*删除设备*/
	cdev_del(&imx6uirq.cdev);
	/*释放设备号*/
	unregister_chrdev_region(imx6uirq.devid,IMX6ULIRQ_COUNT);

	/*摧毁设备*/
	device_destroy(imx6uirq.class,imx6uirq.devid);

	/*释放类*/
	class_destroy(imx6uirq.class);

	/*释放IO*/
	//gpio_free(imx6uirq.led_gpio);
}


//模块加载函数
module_init(imx6uirq_init);

//模块卸载
module_exit(imx6uirq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("qhy");

中断处理流程

正点原子linux阿尔法开发板使用——Linux中断实验_第13张图片

中断下文使用

正点原子linux阿尔法开发板使用——Linux中断实验_第14张图片

你可能感兴趣的:(#,嵌入式驱动linux,linux,驱动开发,单片机)