《中斷學習—— GPIO外部中斷驅動實例》


1.應用場景

  使用的是海思3516dv300的開發板。海思已經默認GPIO相關模塊已全部編入內核。

  通過cat /sys/class/gpio可以查看:

  

  cd gpiochip0

  

 

  可以在設備樹中查看:

  

 

2.GPIO中斷驅動實例

 

#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/module.h>
//模塊參數,GPIO組號、組內偏移、方向、輸出時的輸出初始值
static unsigned int gpio_chip_num = 11;
module_param(gpio_chip_num, uint, S_IRUGO);
MODULE_PARM_DESC(gpio_chip_num, "gpio chip num");

static unsigned int gpio_offset_num = 2;
module_param(gpio_offset_num, uint, S_IRUGO);
MODULE_PARM_DESC(gpio_offset_num, "gpio offset num");

static unsigned int gpio_dir = 1;
module_param(gpio_dir, uint, S_IRUGO);
MODULE_PARM_DESC(gpio_dir, "gpio dir");

static unsigned int gpio_out_val = 1;
module_param(gpio_out_val, uint, S_IRUGO);
MODULE_PARM_DESC(gpio_out_val, "gpio out val");

//模塊參數,中斷觸發類型
/*
 * 0 - disable irq
 * 1 - rising edge triggered
 * 2 - falling edge triggered
 * 3 - rising and falling edge triggered
 * 4 - high level triggered
 * 8 - low level triggered
 */
static unsigned int gpio_irq_type = 4;
module_param(gpio_irq_type, uint, S_IRUGO);
MODULE_PARM_DESC(gpio_irq_type, "gpio irq type");

spinlock_t lock;

static int gpio_dev_test_in(unsigned int gpio_num)
{
		//設置方向為輸入
        if (gpio_direction_input(gpio_num)) {
                pr_err("[%s %d]gpio_direction_input fail!\n",
                                __func__, __LINE__);
                return -EIO;
        }
		//讀出GPIO輸入值
        pr_info ("[%s %d]gpio%d_%d in %d\n", __func__, __LINE__,
                        gpio_num / 8, gpio_num % 8,
                        gpio_get_value(gpio_num));

        return 0;
}
//中斷處理函數
static irqreturn_t gpio_dev_test_isr(int irq, void *dev_id)
{
        pr_info("[%s %d]\n", __func__, __LINE__);

        return IRQ_HANDLED;
}

static int gpio_dev_test_irq(unsigned int gpio_num)
{
        unsigned int irq_num;
        unsigned int irqflags = 0;
		//設置方向為輸入
        if (gpio_direction_input(gpio_num)) {
                pr_err("[%s %d]gpio_direction_input fail!\n",
                                __func__, __LINE__);
                return -EIO;
        }

        switch (gpio_irq_type) {
                case 1:
                        irqflags = IRQF_TRIGGER_RISING;
                        break;
                case 2:
                        irqflags = IRQF_TRIGGER_FALLING;
                        break;
                case 3:
                        irqflags = IRQF_TRIGGER_RISING |
                                IRQF_TRIGGER_FALLING;
                        break;
                case 4:
                        irqflags = IRQF_TRIGGER_HIGH;
                        break;
                case 8:
                        irqflags = IRQF_TRIGGER_LOW;
                        break;
                default:
                        pr_info("[%s %d]gpio_irq_type error!\n",
                                        __func__, __LINE__);
                        return -1;
        }

        pr_info("[%s %d]gpio_irq_type = %d\n", __func__, __LINE__, gpio_irq_type);

        /* IRQF_SHARED:這個中斷標志經常能遇見,這個標志意思就是多個中斷處理程序之間可以共享中斷線,概括起來就是沒有這個標志就只能獨自一個人占用,標志了,就是很多人可以占用這個中斷號來 */
        irqflags |= IRQF_SHARED;
		//根據GPIO編號映射中斷號
        irq_num = gpio_to_irq(gpio_num);
		//注冊中斷
        /*
            irq_num:由gpio_to_irq()函數獲取的中斷號
            gpio_dev_test_isr:中斷觸發函數
            irqflags:中斷觸發類型
            "gpio_dev_test":設置中斷名稱,通常是設備驅動程序的名稱  在cat /proc/interrupts中可以看到此名稱。
            dev_id:最后一個參數,看到第三個參數中IRQF_SHARED時候,你會不會有這樣的疑問,假如現在我要釋放當前共享的指定這個中斷程序時候,我如何釋放?會不會把其他占用也會刪除掉。
                    這就是第五個參數的意義,如果中斷線是共享的,那么就必須傳遞能夠代表當前設備的唯一信息。

            函數返回值:成功返回0。如果返回非0,就表示有錯誤發生,這個時候你可以考慮當前中斷是否被占用了,所以可以加上IRQF_SHARED標志
        */
        if (request_irq(irq_num, gpio_dev_test_isr, irqflags,
                                "gpio_dev_test", &gpio_irq_type)) {
                pr_info("[%s %d]request_irq error!\n", __func__, __LINE__);
                return -1;
        }

        return 0;
}

static void gpio_dev_test_irq_exit(unsigned int gpio_num)
{
        unsigned long flags;

        pr_info("[%s %d]\n", __func__, __LINE__);
		//釋放注冊的中斷
        spin_lock_irqsave(&lock, flags);
        free_irq(gpio_to_irq(gpio_num), &gpio_irq_type);
        spin_unlock_irqrestore(&lock, flags);
}
static int gpio_dev_test_out(unsigned int gpio_num, unsigned int gpio_out_val)
{
		//設置方向為輸出,並輸出一個初始值
        if (gpio_direction_output(gpio_num, !!gpio_out_val)) {
                pr_err("[%s %d]gpio_direction_output fail!\n",
                                __func__, __LINE__);
                return -EIO;
        }

        pr_info("[%s %d]gpio%d_%d out %d\n", __func__, __LINE__,
                                gpio_num / 8, gpio_num % 8, !!gpio_out_val);
        return 0;
}

static int __init gpio_dev_test_init(void)
{
        unsigned int gpio_num;
        int status = 0;

        pr_info("[%s %d]\n", __func__, __LINE__);

        //初始化自旋鎖lock
        spin_lock_init(&lock);

        gpio_num = gpio_chip_num * 8 + gpio_offset_num;
		//注冊要操作的GPIO編號

        /* 一般gpio_request封裝了mem_request(),起保護作用,最后要調用mem_free之類的。主要是告訴內核這地址被占用了。當其它地方調用同一地址的gpio_request就會報告錯誤,該地址已被申請。在/proc/mem應該會有地址占用表描述。
        這種用法的保護作用前提是大家都遵守先申請再訪問,有一個地方沒遵守這個規則,這功能就失效了。好比進程互斥,必需大家在訪問臨界資源的時候都得先獲取鎖一樣,其中一個沒遵守約定,代碼就廢了。 */
        if (gpio_request(gpio_num, NULL)) {
                pr_err("[%s %d]gpio_request fail! gpio_num=%d \n", __func__, __LINE__, gpio_num);
                return -EIO;
        }

        status = gpio_dev_test_irq(gpio_num);

        if (status)
                gpio_free(gpio_num);

        return status;
}


static void __exit gpio_dev_test_exit(void)
{
        unsigned int gpio_num;

        pr_info("[%s %d]\n", __func__, __LINE__);

        gpio_num = gpio_chip_num * 8 + gpio_offset_num;

        if (gpio_irq_type)
                gpio_dev_test_irq_exit(gpio_num);
		//釋放注冊的GPIO編號
        gpio_free(gpio_num);
}

module_init(gpio_dev_test_init);
module_exit(gpio_dev_test_exit);

MODULE_DESCRIPTION("GPIO device test Driver sample");
MODULE_LICENSE("GPL");

/* cat /proc/devices    只顯示驅動的主設備號,且是分類顯示  arm-himix200-linux */

  

3.編譯安裝

3.1 編譯並安裝成對應的ko

  參考:https://www.cnblogs.com/zhuangquan/p/13375806.html 中的第四小節

   編譯指定位置的驅動文件:

 make ARCH=arm CROSS_COMPILE=arm-himix200-linux- M=drivers/char/mydrv/

  編譯成功后將ko放在開發板上執行insmod。

  cat /proc/interrupts

 

 

 

 詳細可參考:(14條消息) Exynos4412 中斷驅動開發(三)—— 設備樹中中斷節點的創建_知秋一葉-CSDN博客

(14條消息) Exynos4412 中斷驅動開發(一)—— 中斷基礎及中斷的注冊過程_知秋一葉-CSDN博客

 

 

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 


免責聲明!

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



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