1. 程式人生 > >嵌入式核心及驅動開發之學習筆記(六) 驅動層中斷實現

嵌入式核心及驅動開發之學習筆記(六) 驅動層中斷實現

由於中斷訊號的突發性,CPU要捕獲中斷訊號,有兩種方式。一是不斷輪詢是否有中斷髮生,這樣有點傻;二是通過中斷機制,過程如下:

中斷源 ---> 中斷訊號  --->  中斷控制器 --->  CPU

 中斷源有很多,CPU拿到中斷訊號,如何區分是哪一個中斷源產生?那麼一定有一個序列,標識不同的中斷源發出的訊號,這就是中斷號了。

 

ARM裸機開發中,使用中斷前需要進行不少配置,比較繁瑣。而從核心的角度,我們只要明確兩個目標

  • 中斷號
  • 中斷的處理方法

 

實驗:在驅動中新增中斷機制,按鍵觸發外部中斷,中斷產生後,驅動列印中斷資訊。

 

步驟

定義中斷號

在通過原理圖,晶片手冊查詢,從硬體連線最終定位到中斷號

 

通過原始碼,系統裝置樹描述中

每一個裝置的節點都要有一個compatible屬性 ,用來查詢節點(也可以通過節點名或節點路徑查詢指定節點);interrupt-parent表示結點繼承至gic。

[email protected]:~/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>;
                };

 

手動定義裝置樹節點,參考上面的系統描述和硬體裝置號。定義如下

[email protected]:~/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共享給開發板執行。

[email protected]:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/chr_drv# make
[email protected]:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/chr_drv# make install

 

實驗結果

按下按鍵(1->0),觸發一次中斷。松下按鍵(0->1),又觸發一次中斷。