• 正文
    • 修改设备树
    • 驱动源码myirq_key.c编译
    • 完整的驱动myirq_key.c示例源码
    • 编译
    • 测试
  • 相关推荐
申请入驻 产业图谱

飞凌嵌入式ElfBoard ELF 1板卡-Linux系统中的中断之按键中断驱动

03/28 10:45
612
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

例程代码路径:ELF 1开发板资料包3-例程源码3-2 驱动例程源码5_按键中断驱动

上一节LED驱动中,使用了GPIO子系统的API函数将引脚配置为输出来控制LED的亮灭,本节讲解将引脚配置为输入,来获取按键状态。并且还使用到了中断的概念。

接下来编写一个K1按键的驱动。

修改设备树

(一)查看原理图和引脚复用表格,可以得到K1由GPIO5_4控制,所以我们需要配置GPIO5_4引脚为输入,而且能够在用户空间来获取它的电平状态。

(二)在NXP内核源码的设备树中查找GPIO5_4,将用到的地方屏蔽掉,避免资源申请失败。打开arch/arm/boot/dts/imx6ull-elf1-emmc.dts可以看到sound节点下有用到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开发板_快速启动手册_V1》4.4节单独更新设备树。

驱动源码myirq_key.c编译

(一)头文件引用

#include <linux/module.h>       // 包含模块相关函数的头文件

#include <linux/fs.h>           // 包含文件系统相关函数的头文件

#include <linux/uaccess.h>      // 包含用户空间数据访问函数的头文件

#include <linux/cdev.h>         //包含字符设备头文件

#include <linux/device.h>       //包含设备节点相关的头文件

#include <linux/gpio.h>         //包含gpio操作函数的相关头文件

#include <linux/interrupt.h>     //包含中断函数相关头文件

(二)创建相关宏定义和变量

#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引脚相关联的中断号。如果转换失败或引脚没有关联的中断,函数将返回一个负值。

(三)mydevice_init(void)函数的实现

static int __init mydevice_init(void)

{

    int ret;

&nbsp

    gpio_free(GPIO_KEY_PIN_NUM);

    // 在这里执行驱动程序的初始化操作

&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);

       // 创建设备类

    button_irq = class_create(THIS_MODULE, "button_irq");

    if (IS_ERR(button_irq)) {

        pr_err("Failed to create classn");

        return PTR_ERR(button_irq);

}

  // 创建设备节点并关联到设备类

    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驱动的区别主要是使用gpio_direction_input函数将引脚配置为了输入模式,使用request_irq函数申请了中断,触发方式为:IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING。表示上升沿和下降沿都会触发中断。

(四)中断服务函数irqreturn_t button_interrupt_handler()实现

//中断服务函数

static irqreturn_t button_interrupt_handler(int irq, void *dev_id)

{

    // 在此处执行按键中断处理代码

 

&nbsp// 检查按键状态

    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引脚的电平状态。函数原型如下:

int gpio_get_value(unsigned int gpio);

参数gpio是要获取值的GPIO引脚号。该函数返回GPIO引脚的当前值,如果引脚处于高电平状态,则返回1;如果引脚处于低电平状态,则返回0。如果读取GPIO值失败,函数将返回一个负值。通过判断电平引脚的电平状态,来输出对应的打印信息。

完整的驱动myirq_key.c示例源码

#include <linux/module.h>       // 包含模块相关函数的头文件

#include <linux/fs.h>           // 包含文件系统相关函数的头文件

#include <linux/uaccess.h>      // 包含用户空间数据访问函数的头文件

#include <linux/cdev.h>         //包含字符设备头文件

#include <linux/device.h>       //包含设备节点相关的头文件

#include <linux/gpio.h>         //包含gpio子系统相关函数头文件

#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)

{

    // 在此处执行按键中断处理代码

 

&nbsp// 检查按键状态

    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);

    // 在这里执行驱动程序的初始化操作

&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);

       // 创建设备类

    button_irq = class_create(THIS_MODULE, "button_irq");

    if (IS_ERR(button_irq)) {

        pr_err("Failed to create classn");

        return PTR_ERR(button_irq);

}

  // 创建设备节点并关联到设备类

    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);

// 在这里执行驱动程序的清理操作

 gpio_free(GPIO_KEY_PIN_NUM);

// 销毁设备节点

 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文件拷贝到开发板。

测试

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。

点赞
收藏
评论
分享
加入交流群
举报

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录