嵌入式核心及驅動開發之學習筆記(六) 驅動層中斷實現
阿新 • • 發佈:2018-11-27
由於中斷訊號的突發性,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),又觸發一次中斷。