• 正文
    • 修改設備樹
    • 驅動源碼myirq_key.c編譯
    • 完整的驅動myirq_key.c示例源碼
    • 編譯
    • 測試
  • 相關推薦
申請入駐 產業(yè)圖譜

飛凌嵌入式ElfBoard ELF 1板卡-Linux系統(tǒng)中的中斷之按鍵中斷驅動

03/28 10:45
714
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

例程代碼路徑:ELF 1開發(fā)板資料包3-例程源碼3-2 驅動例程源碼5_按鍵中斷驅動

上一節(jié)LED驅動中,使用了GPIO子系統(tǒng)的API函數將引腳配置為輸出來控制LED的亮滅,本節(jié)講解將引腳配置為輸入,來獲取按鍵狀態(tài)。并且還使用到了中斷的概念。

接下來編寫一個K1按鍵的驅動。

修改設備樹

(一)查看原理圖和引腳復用表格,可以得到K1由GPIO5_4控制,所以我們需要配置GPIO5_4引腳為輸入,而且能夠在用戶空間來獲取它的電平狀態(tài)。

(二)在NXP內核源碼的設備樹中查找GPIO5_4,將用到的地方屏蔽掉,避免資源申請失敗。打開arch/arm/boot/dts/imx6ull-elf1-emmc.dts可以看到sound節(jié)點下有用到GPIO5_4,所以先需要把這部分屏蔽掉。

(三)編譯設備樹

. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/linux-imx-imx_4.1.15_2.0.0_ga$ make dtbs

編譯生成的設備樹文件為imx6ull-elf1-emmc.dtb,參考《01-0 ELF1、ELF1S開發(fā)板_快速啟動手冊_V1》4.4節(jié)單獨更新設備樹。

驅動源碼myirq_key.c編譯

(一)頭文件引用

#include <linux/module.h> ??????// 包含模塊相關函數的頭文件

#include <linux/fs.h> ??????????// 包含文件系統(tǒng)相關函數的頭文件

#include <linux/uaccess.h> ?????// 包含用戶空間數據訪問函數的頭文件

#include <linux/cdev.h> ????????//包含字符設備頭文件

#include <linux/device.h> ??????//包含設備節(jié)點相關的頭文件

#include <linux/gpio.h> ????????//包含gpio操作函數的相關頭文件

#include <linux/interrupt.h> ????//包含中斷函數相關頭文件

(二)創(chuàng)建相關宏定義和變量

#define DEVICE_NAME "button_irq" ?// 設備名稱

#define GPIO_KEY_PIN_NUM 132 ???//定義操作的GPIO編號

#define BUTTON_IRQ ?gpio_to_irq(GPIO_KEY_PIN_NUM)//GPIO引腳中斷號

?

static dev_t dev_num; ??//分配的設備號

struct ?cdev my_cdev; ?????????//字符設備指針

int major; ?//主設備號

int minor; ?//次設備號

static struct class *button_irq;

static struct device *my_device;

?

GPIO編號

在imx6ull上確定GPIO編號的公式為:GPIOn_IOx=(n-1)*32+x;因為選擇的引腳為GPIO5_IO4,所以通過(n-1)*32+x計算得到的編號為132。

gpio_to_irq()函數用于將GPIO引腳編號轉換為對應的中斷號。函數原型如下:

int gpio_to_irq(unsigned int gpio);

參數gpio是要轉換的GPIO引腳號。該函數返回與給定GPIO引腳相關聯(lián)的中斷號。如果轉換失敗或引腳沒有關聯(lián)的中斷,函數將返回一個負值。

(三)mydevice_init(void)函數的實現

static int __init mydevice_init(void)

{

????int ret;

&nbsp

????gpio_free(GPIO_KEY_PIN_NUM);

????// 在這里執(zhí)行驅動程序的初始化操作

&nbspif (gpio_request(GPIO_KEY_PIN_NUM, "button_irq")) {

?&nbsp&nbspprintk("request %s gpio faile n", "button_irq");

&nbsp&nbsp?return -1;

&nbsp?}

&nbsp?//將引腳設置為輸入模式

&nbsp?gpio_direction_input(GPIO_KEY_PIN_NUM);

?

&nbsp?// 注冊中斷處理函數

????ret = request_irq(BUTTON_IRQ, button_interrupt_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "button_irq", NULL);

????if (ret < 0) {

????????printk(KERN_ALERT "Failed to register interrupt handlern");

????????gpio_free(GPIO_KEY_PIN_NUM);

????????return ret;

????}

&nbsp

????// 注冊字符設備驅動程序

????ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME);

????if (ret < 0) {

????????printk(KERN_ALERT "Failed to allocate device number: %dn", ret);

????????return ret;

}

???major=MAJOR(dev_num);

???minor=MINOR(dev_num);

&nbspprintk(KERN_INFO "major number: %dn",major);

&nbspprintk(KERN_INFO "minor number: %dn",minor);

?

&nbspmy_cdev.owner = THIS_MODULE;

????????cdev_init(&my_cdev,&fops);

????????cdev_add(&my_cdev,dev_num,1);

???????// 創(chuàng)建設備類

????button_irq = class_create(THIS_MODULE, "button_irq");

????if (IS_ERR(button_irq)) {

????????pr_err("Failed to create classn");

????????return PTR_ERR(button_irq);

}

??// 創(chuàng)建設備節(jié)點并關聯(lián)到設備類

????my_device = device_create(button_irq, NULL, MKDEV(major, minor), NULL, DEVICE_NAME);

????if (IS_ERR(my_device)) {

????????pr_err("Failed to create devicen");

????????class_destroy(button_irq);

????????return PTR_ERR(my_device);

????}

???????????printk(KERN_INFO "Device registered successfully.n");

????return 0;

}

與前面LED驅動的區(qū)別主要是使用gpio_direction_input函數將引腳配置為了輸入模式,使用request_irq函數申請了中斷,觸發(fā)方式為:IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING。表示上升沿和下降沿都會觸發(fā)中斷。

(四)中斷服務函數irqreturn_t button_interrupt_handler()實現

//中斷服務函數

static irqreturn_t button_interrupt_handler(int irq, void *dev_id)

{

????// 在此處執(zhí)行按鍵中斷處理代碼

?

&nbsp// 檢查按鍵狀態(tài)

????int button_state = gpio_get_value(GPIO_KEY_PIN_NUM);

????if (button_state) {

????????printk(KERN_INFO "Button releasedn");

????} else {

????????printk(KERN_INFO "Button pressedn");

????}

?

????return IRQ_HANDLED;

}

使用gpio_get_value()函數獲取gpio引腳的電平狀態(tài)。函數原型如下:

int gpio_get_value(unsigned int gpio);

參數gpio是要獲取值的GPIO引腳號。該函數返回GPIO引腳的當前值,如果引腳處于高電平狀態(tài),則返回1;如果引腳處于低電平狀態(tài),則返回0。如果讀取GPIO值失敗,函數將返回一個負值。通過判斷電平引腳的電平狀態(tài),來輸出對應的打印信息。

完整的驅動myirq_key.c示例源碼

#include <linux/module.h> ??????// 包含模塊相關函數的頭文件

#include <linux/fs.h> ??????????// 包含文件系統(tǒng)相關函數的頭文件

#include <linux/uaccess.h> ?????// 包含用戶空間數據訪問函數的頭文件

#include <linux/cdev.h> ????????//包含字符設備頭文件

#include <linux/device.h> ??????//包含設備節(jié)點相關的頭文件

#include <linux/gpio.h> ????????//包含gpio子系統(tǒng)相關函數頭文件

#include <linux/interrupt.h> ???//包含中斷函數相關頭文件

?

#define DEVICE_NAME "button_irq" ?// 設備名稱

#define GPIO_KEY_PIN_NUM 132

#define BUTTON_IRQ ?gpio_to_irq(GPIO_KEY_PIN_NUM)//GPIO引腳中斷號

?

static dev_t dev_num; ??//分配的設備號

struct ?cdev my_cdev; ?????????//字符設備指針

int major; ?//主設備號

int minor; ?//次設備號

static struct class *button_irq;

static struct device *my_device;

?

?

//中斷服務函數

static irqreturn_t button_interrupt_handler(int irq, void *dev_id)

{

????// 在此處執(zhí)行按鍵中斷處理代碼

?

&nbsp// 檢查按鍵狀態(tài)

????int button_state = gpio_get_value(GPIO_KEY_PIN_NUM);

????if (button_state) {

????????printk(KERN_INFO "Button releasedn");

????} else {

????????printk(KERN_INFO "Button pressedn");

????}

?

????return IRQ_HANDLED;

}

?

static int device_open(struct inode *inode, struct file *file)

{

// 在這里處理設備打開的操作

printk(KERN_INFO "This is device_open.n");

????return 0;

}

?

static int device_release(struct inode *inode, struct file *file)

{

// 在這里處理設備關閉的操作

printk(KERN_INFO "This is device_release.n");

?

????return 0;

}

?

?

?

static struct file_operations fops = {

????.owner = THIS_MODULE,

????.open = device_open,

????.release = device_release,

};

?

static int __init mydevice_init(void)

{

????int ret;

&nbsp//申請GPIO前先釋放資源

????gpio_free(GPIO_KEY_PIN_NUM);

????// 在這里執(zhí)行驅動程序的初始化操作

&nbspif (gpio_request(GPIO_KEY_PIN_NUM, "button_irq")) {

?&nbsp&nbspprintk("request %s gpio faile n", "button_irq");

&nbsp&nbsp?return -1;

&nbsp?}

&nbsp?//將引腳設置為輸入模式

&nbsp?gpio_direction_input(GPIO_KEY_PIN_NUM);

?

&nbsp?

&nbsp?// 注冊中斷處理函數

????ret = request_irq(BUTTON_IRQ, button_interrupt_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "button_irq", NULL);

????if (ret < 0) {

????????printk(KERN_ALERT "Failed to register interrupt handlern");

????????gpio_free(GPIO_KEY_PIN_NUM);

????????return ret;

????}

&nbsp

????// 注冊字符設備驅動程序

????ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME);

????if (ret < 0) {

????????printk(KERN_ALERT "Failed to allocate device number: %dn", ret);

????????return ret;

}

???major=MAJOR(dev_num);

???minor=MINOR(dev_num);

&nbspprintk(KERN_INFO "major number: %dn",major);

&nbspprintk(KERN_INFO "minor number: %dn",minor);

?

&nbspmy_cdev.owner = THIS_MODULE;

????????cdev_init(&my_cdev,&fops);

????????cdev_add(&my_cdev,dev_num,1);

???????// 創(chuàng)建設備類

????button_irq = class_create(THIS_MODULE, "button_irq");

????if (IS_ERR(button_irq)) {

????????pr_err("Failed to create classn");

????????return PTR_ERR(button_irq);

}

??// 創(chuàng)建設備節(jié)點并關聯(lián)到設備類

????my_device = device_create(button_irq, NULL, MKDEV(major, minor), NULL, DEVICE_NAME);

????if (IS_ERR(my_device)) {

????????pr_err("Failed to create devicen");

????????class_destroy(button_irq);

????????return PTR_ERR(my_device);

????}

???????????printk(KERN_INFO "Device registered successfully.n");

????return 0;

}

?

static void __exit mydevice_exit(void)

{

// 釋放中斷

??free_irq(BUTTON_IRQ, NULL);

// 在這里執(zhí)行驅動程序的清理操作

?gpio_free(GPIO_KEY_PIN_NUM);

// 銷毀設備節(jié)點

?device_destroy(button_irq, MKDEV(major, minor));

// 銷毀設備類

?class_destroy(button_irq);

// 刪除字符設備

?cdev_del(&my_cdev);

????// 注銷字符設備驅動程序

????unregister_chrdev(0, DEVICE_NAME);

????printk(KERN_INFO "Device unregistered.n");

}

?

module_init(mydevice_init);

module_exit(mydevice_exit);

?

MODULE_LICENSE("GPL"); ?????// 指定模塊的許可證信息

MODULE_AUTHOR("Your Name"); // 指定模塊的作者信息

MODULE_DESCRIPTION("A simple character device driver"); // 指定模塊的描述信息

編譯

復制7.5.4驅動中的Makefile文件,將其中的myled.o修改為myirq_key.o,效果如下:

. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/test/05_按鍵中斷驅動/myirq_key$ make

將生成的.ko文件拷貝到開發(fā)板。

測試

root@ELF1:~#?insmod myirq_key.ko

major number: 247

minor number: 0

Device registered successfully.

root@ELF1:~# Button pressed

Button released

Button pressed

Button released

Button pressed

Button released

Button pressed

Button released

root@ELF1:~#?rmmod myirq_key.ko

Device unregistered.

加載驅動后,按下K1按鍵,打印Button pressed,抬起按鍵,打印Button released。

相關推薦