Linux中斷-簡單中斷,以GPIO中斷為例


Linux中斷基礎概念

中斷上下文

Linux內核的中斷回調可以有兩部分,即上下文。當中斷比較簡單時,可以只有上文。

一般中斷上文是指由中斷產生的回調函數直接執行的部分;中斷下文在上文中啟用調度,再由內核調度。

中斷上文:處理盡可能少的任務,特點是響應速度快

中斷下文:處理耗時任務,可以被新的中斷打斷

中斷嵌套

Linux中斷現在不能嵌套,之前可以

中斷相關的函數及命令

獲取中斷號

如果是有設備樹的內核,一般通過節點的interrupt-parent和interrupt屬性來描述中斷

對GPIO來說,GPIO的節點可以作為中斷控制器,一般由BSP廠家編寫

<linux/of_irq.h>

//從設備樹的設備節點中獲取中斷號
unsigned int irq_of_parse_and_map(struct device_node *dev, int index);
//參數:dev設備節點,index索引(節點中interrupts屬性可能包含多條中斷信息,通過index確認)
//返回值:中斷號

//如果是GPIO的話,可以不從設備樹中獲取
int gpio_to_irq(unsigned int gpio);
//參數:gpio的編號
//返回值:gpio對應的中斷號

申請中斷

申請中斷的函數

int request_irq(unsigned int irq,
               irq_handler_t handler,
               unsigned long flags,
               const char *name,
               void *dev);
//參數:
//irq:要申請中斷的中斷號
//handler:中斷處理函數
//flags:中斷標志
//name:中斷名字,可在/proc/interrupts文件中看到對應的名字
//dev:flags為IRQF_SHARED時,dev用來區分不同的中斷。一般將dev設置為設備結構體,傳遞給irq_handler_t的第二個參數

//返回值:0申請成功,其他負值申請失敗;如果返回-EBUSY標識已經被申請

中斷標志(申請中斷函數的flags參數)定義在 include/linux/interrupt.h中

常見的中斷標志:

標志 功能
IRQF_SHARED 多個設備共享一個中斷線,申請中斷函數的dev參數是區分它們的唯一標志
IRQF_ONESHOT 單次中斷,中斷執行一次就結束
IRQF_TRIGGER_NONE 無觸發
IRQF_TRIGGER_RISING 上升沿觸發
IRQF_TRIGGER_FALLING 下降沿觸發
IRQF_TRIGGER_HIGH 高電平觸發
IRQF_TRIGGER_LOW 低電平觸發

中斷處理函數

使用request_irq申請中斷的時候需要中斷處理函數irq_handler_t來做參數,

這里的irq_handler_t函數可以理解為中斷上文的回調函數,發生中斷時內核會調用處理函數。

irqretuen_t (*irq_handler_t)(int, void*)
//參數:int型中斷號,void*型需要和中斷申請函數的dev參數保持一致
//返回值:irqretuen_t類型
enum irqreturn {
	IRQ_NONE = (0<<0),  //不處理
	IRQ_HANDLED = (1<<0),  //正常處理
	IRQ_WAKE_THREAD = (1<<1), //使用中斷下文處理
};
typedef enum irqreturn irqreturn_t;
//irqretuen_t是一個枚舉類型,一般中斷處理函數會返回第二個數,格式如下
//return ITQ_RETVAL(IRQ_HANDLED)

釋放中斷

void free_irq(unsigned int irq, void *dev);
//參數:irq要釋放的中斷號,dev如果設置為IRQ_SHARED的話,此參數來區分具體中斷

查看中斷是否存在

#查看中斷是否存在
cat /proc/interrupts

#查看中斷執行次數
cat /proc/irq/xx/spurious
#xx指中斷號

中斷實驗-以GPIO按鍵中斷為例

實驗的gpio中斷內容很少,所以只使用中斷上文

使用訊為的itop-4412開發板,home鍵,按鍵按下時進入中斷執行打印

設備樹修改

home鍵的gpio是gpx1_1,將之前home鍵的描述注釋掉,添加自己的節點

home_inter {
		compatible = "taxue,home_key";
		status = "okay";
		inter_gpio = <&gpx1 1 GPIO_ACTIVE_LOW>;
	};

驅動部分的代碼步驟

以平台總線driver的驅動作為框架,在probe函數里執行以下步驟

  • 從設備樹獲取描述GPIO的節點
  • 通過節點中的gpio參數,獲取gpio編號
  • 申請gpio
  • 設置gpio方向
  • 獲取gpio對應的中斷號
  • 申請中斷(中斷處理函數)

編寫中斷處理函數

platform_driver的of_match_table參數要和設備樹節點中的compatible參數一致,保證可以匹配

在remove函數里,釋放中斷、釋放GPIO

代碼

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>

//用於和設備匹配
#define DEVICE_NAME "taxue,home_key"

int home_gpio_idx;  //home鍵的gpio編號
int home_inter_idx; //home鍵gpio對應的中斷號

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

        printk("%s(%d)\n", __FUNCTION__, __LINE__);  //__LINE__,代碼所在的行數
		printk("HOME KEY HIGH TO LOW!\n");
		
        return IRQ_HANDLED; //返回值,表示正常執行
}

int drProbe(struct platform_device *dev){
    int ret;
    struct device_node *inter_node;

    //根據設備樹路徑獲取節點
    inter_node = of_find_node_by_path("/home_inter");  
    if(inter_node == NULL){
        printk("find node failed\n");
        return -1;
    }

    //根據節點的inter_gpio屬性,獲取gpio編號
    home_gpio_idx = of_get_named_gpio( inter_node, "inter_gpio", 0);  

    //申請gpio
    ret = gpio_request(home_gpio_idx,"home");  
    if( ret != 0){
        printk("gpio request failed\n");
        return -1;
    }

    //將gpio設置為輸入
    gpio_direction_input(home_gpio_idx);  

    //獲取中斷號
    home_inter_idx = gpio_to_irq(home_gpio_idx);  

    //申請中斷,下降沿觸發
    ret = request_irq(home_inter_idx, home_interrupt, IRQF_TRIGGER_FALLING, "home_key", dev);  
    if(ret < 0){
        printk("request interrupt failed, IRQ=%d,ret=%d\n", home_inter_idx, ret);
        return -1;
    }
   
    return 0;
}
int drRemove(struct platform_device *dev){
    free_irq(home_inter_idx, dev);  //釋放中斷
    gpio_free(home_gpio_idx);       //釋放gpio
    printk("driver remove\n");
    return 0;
}

static const struct of_device_id of_interr_match[] = {
    {.compatible = DEVICE_NAME},
    {},
};

static struct platform_driver pdrv = {
    .probe = drProbe,
    .remove = drRemove,
    .driver = {
        .name = DEVICE_NAME,
        .owner = THIS_MODULE,
        .of_match_table = of_interr_match,
    }
};

//模塊入口
static int driver_init_interr(void){
    int ret=0;
    ret = platform_driver_register(&pdrv);  //注冊平台driver
    if(ret < 0){
        printk("platform driver regist failed\n");
        return -1;
    }
    return 0;
}

//模塊出口
static void driver_exit_interr(void){
   platform_driver_unregister(&pdrv);   //卸載平台driver
   printk("platform driver exit!\n");
}

module_init(driver_init_interr);
module_exit(driver_exit_interr);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TAXUE");


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM