嵌入式内核及驱动开发之学习笔记(六) 驱动层中断实现
由于中断信号的突发性,CPU要捕获中断信号,有两种方式。一是不断轮询是否有中断发生,这样有点傻;二是通过中断机制,过程如下:
中断源 ---> 中断信号 ---> 中断控制器 ---> CPU
中断源有很多,CPU拿到中断信号,如何区分是哪一个中断源产生?那么一定有一个序列,标识不同的中断源发出的信号,这就是中断号了。
在ARM裸机开发中,使用中断前需要进行不少配置,比较繁琐。而从内核的角度,我们只要明确两个目标
- 中断号
 - 中断的处理方法
 
实验:在驱动中添加中断机制,按键触发外部中断,中断产生后,驱动打印中断信息。
步骤
定义中断号
在通过原理图,芯片手册查询,从硬件连接最终定位到中断号

通过源码,系统设备树描述中
每一个设备的节点都要有一个compatible属性 ,用来查找节点(也可以通过节点名或节点路径查找指定节点);interrupt-parent表示结点继承至gic。
  
   - 
    
     
    
    
     
      root@linux:~/linux-3.14-fs4412# vim arch/arm/boot/dts/exynos4x12-pinctrl.dtsi
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     		 gpx1: gpx1 {
     
    
 
   - 
    
     
    
    
     
                              gpio-controller;
     
    
 
   - 
    
     
    
    
                             #gpio-cells = <2>;
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
                              interrupt-controller;
     
    
 
   - 
    
     
    
    
     
                              interrupt-parent = <&gic>;
     
    
 
   - 
    
     
    
    
     
                              interrupts = <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>,
     
    
 
   - 
    
     
    
    
     
                                           <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>;
     
    
 
   - 
    
     
    
    
                             #interrupt-cells = <2>;
     
    
 
   - 
    
     
    
    
     
                      };
     
    
 
  
 
手动定义设备树节点,参考上面的系统描述和硬件设备号。定义如下
  
   - 
    
     
    
    
     
      root@linux:~/linux-3.14-fs4412# vim arch/arm/boot/dts/exynos4412-fs4412.dts
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      key_int_node{
     
    
 
   - 
    
     
    
    
     
           compatible = "test_key";
     
    
 
   - 
    
     
    
    
     
           interrupt-parent = <&gpx1>;
     
    
 
   - 
    
     
    
    
     
           interrupts = <2 4>;
     
    
 
   - 
    
     
    
    
     
      };
     
    
 
  
 
重新编译设备树,并更新tftp根目录下的设备树文件
  
   - 
    
     
    
    
     
      make dtbs
     
    
 
   - 
    
     
    
    
     
      cp -raf arch/arm/boot/dts/exynos4412-fs4412.dtb  /tftpboot/
     
    
 
  
 
编写驱动代码
get_irqno_from_node函数通过设备树的路径到设备结点的中断号;然后request_irq申请中断,并设置触发方式是双边沿触发,key_irq_handler指定为中断处理函数;在key_irq_handler中只有一条打印信息,当中断产生,触发这条函数,打印信息;模块卸载时,通过free_irq释放掉之前申请的中断资源。
  
   - 
    
     
    
    
     
      //key_drv.c
     
    
 
   - 
    
     
    
    
     
      #include <linux/init.h>
     
    
 
   - 
    
     
    
    
     
      #include <linux/module.h>
     
    
 
   - 
    
     
    
    
     
      #include <linux/of.h>
     
    
 
   - 
    
     
    
    
     
      #include <linux/of_irq.h>
     
    
 
   - 
    
     
    
    
     
      #include <linux/interrupt.h>
     
    
 
   - 
    
     
    
    
     
      #include <linux/slab.h>
     
    
 
   - 
    
     
    
    
     
      #include <linux/fs.h>
     
    
 
   - 
    
     
    
    
     
      #include <linux/device.h>
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      #include <asm/io.h>
     
    
 
   - 
    
     
    
    
     
      #include <asm/uaccess.h>
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      irqreturn_t key_irq_handler(int irqno, void *devid);
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      static int irqno;
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      int get_irqno_from_node(void)
     
    
 
   - 
    
     
    
    
     
      {	
     
    
 
   - 
    
     
    
    
     	//从设备树路径,查找节点
     
    
 
   - 
    
     
    
    
     	struct device_node *np = of_find_node_by_path("/key_int_node");
     
    
 
   - 
    
     
    
    
     	if(np){
     
    
 
   - 
    
     
    
    
     		printk("find node ok\n");
     
    
 
   - 
    
     
    
    
     
      	}else{
     
    
 
   - 
    
     
    
    
     		printk("find node failed\n");
     
    
 
   - 
    
     
    
    
     
      	}
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     	int irqno = irq_of_parse_and_map(np, 0);
     
    
 
   - 
    
     
    
    
     	printk("irqno = %d\n", irqno);
     
    
 
   - 
    
     
    
    
     	
     
    
 
   - 
    
     
    
    
     	return irqno;
     
    
 
   - 
    
     
    
    
     
      }
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      static int __init key_drv_init(void)
     
    
 
   - 
    
     
    
    
     
      {
     
    
 
   - 
    
     
    
    
     	int ret;
     
    
 
   - 
    
     
    
    
     	
     
    
 
   - 
    
     
    
    
     	//拿到中断号
     
    
 
   - 
    
     
    
    
     
      	irqno = get_irqno_from_node();
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     	//申请中断资源
     
    
 
   - 
    
     
    
    
     
      	ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, 
     
    
 
   - 
    
     
    
    
     					"key3_eint10", NULL);
     
    
 
   - 
    
     
    
    
     	if(ret != 0)
     
    
 
   - 
    
     
    
    
     
      	{
     
    
 
   - 
    
     
    
    
     		printk("request_irq error\n");
     
    
 
   - 
    
     
    
    
     		return ret;
     
    
 
   - 
    
     
    
    
     
      	}
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     	return 0;
     
    
 
   - 
    
     
    
    
     
      }
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      static void __exit key_drv_exit(void)
     
    
 
   - 
    
     
    
    
     
      {
     
    
 
   - 
    
     
    
    
     	//释放中断资源
     
    
 
   - 
    
     
    
    
     	free_irq(irqno, NULL);
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      }
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      irqreturn_t key_irq_handler(int irqno, void *devid)
     
    
 
   - 
    
     
    
    
     
      {
     
    
 
   - 
    
     
    
    
     	printk("-------%s-------------\n", __FUNCTION__);
     
    
 
   - 
    
     
    
    
     	
     
    
 
   - 
    
     
    
    
     	return IRQ_HANDLED;
     
    
 
   - 
    
     
    
    
     
      }
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      module_init(key_drv_init);
     
    
 
   - 
    
     
    
    
     
      module_exit(key_drv_exit);
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      MODULE_LICENSE("GPL");
     
    
 
   - 
    
     
    
    
      
     
    
 
  
 
Makefile文件
  
   - 
    
     
    
    
     
      ROOTFS_DIR = /nfs/rootfs
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      ifeq ($(KERNELRELEASE), )
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      KERNEL_DIR = /mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412
     
    
 
   - 
    
     
    
    
     
      CUR_DIR = $(shell pwd)
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      all :
     
    
 
   - 
    
     
    
    
     
      	make -C  $(KERNEL_DIR) M=$(CUR_DIR) modules
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      clean :
     
    
 
   - 
    
     
    
    
     
      	make -C  $(KERNEL_DIR) M=$(CUR_DIR) clean
     
    
 
   - 
    
     
    
    
     	
     
    
 
   - 
    
     
    
    
     
      install:
     
    
 
   - 
    
     
    
    
     
      	cp -raf *.ko   $(ROOTFS_DIR)/drv_module
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      else
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      obj-m += key_drv.o
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      endif
     
    
 
   - 
    
     
    
    
      
     
    
 
  
 
编译
Ubuntu环境编译,目标文件输出到nfs目录,nfs共享给开发板执行。
  
   - 
    
     
    
    
     
      root@linux:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/chr_drv# make
     
    
 
   - 
    
     
    
    
     
      root@linux:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/chr_drv# make install
     
    
 
  
 
实验结果
按下按键(1->0),触发一次中断。松下按键(0->1),又触发一次中断。

文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/feit2417/article/details/84106949
- 点赞
 - 收藏
 - 关注作者
 
            
           
评论(0)