• 20150217 IMX257实现GPIO-IRQ中断按键驱动程序


    IMX257实现GPIO-IRQ中断按键驱动程序

    2015-02-17 李海沿

        昨天我们已经实现了中断查询的方式实现GPIO按键驱动程序,但是,有一个缺点就是,当我们把应用程序放在后台执行时,即便没有按键,应用程序while循环中的read函数也不断的在运行,严重的导致了CPU资源的浪费。

        本文中,我们在前面按键查询驱动程序的基础上来修改。

        大概介绍一下设计思路吧:

        和前面的差不多,当我们加载驱动时,首先在init函数中,对GPIO功能进行模式设置,都设置为GPIO模式,然后申请GPIO引脚的内存,

    接着,当我们应用程序使用ioctl函数的gpio_input命令时,将所有的GPIO引脚设置为输入,并且22K上拉模式,当应用程序使用read函数读取时,如果按键没有按下,则会在read函数中睡眠(可被中断唤醒),交出CPU的使用权,当按键按下时,首先会在中断程序中进行唤醒操作,在read函数中接着运行,读取GPIO引脚的电平,并且将数据从内核空间传递到用户空间中,并且打印出来。

        最后,当我们不要用驱动程序时,rsmod会调用exit函数,释放所有我们已经申请的GPIO资源。

        好了,大概的思路就是这样,和前面的查询驱动程序,差不多,只不过就加了一个中断,和睡眠而已。

    附上驱动程序代码:

      1 /******************************
      2     linux key_query
      3  *****************************/
      4 #include <linux/module.h>
      5 #include <linux/init.h>
      6 #include <linux/kernel.h>
      7 #include <linux/delay.h>
      8 #include <linux/types.h>
      9 #include <linux/ioctl.h>
     10 #include <linux/gpio.h>
     11 #include <linux/fs.h>
     12 #include <linux/device.h>
     13 #include <linux/uaccess.h>
     14 #include <linux/irq.h>
     15 #include <linux/wait.h>
     16 #include <linux/sched.h>//error: 'TASK_INTERRUPTIBLE' undeclared 
     17 #include <linux/interrupt.h>
     18 
     19 #include "mx257_gpio.h"
     20 #include "mx25_pins.h"
     21 #include "iomux.h"
     22 
     23 #define Driver_NAME "key_interrupt"
     24 #define DEVICE_NAME "key_interrupt"
     25 
     26 #define GPIO2_21    MX25_PIN_CLKO
     27 #define GPIO3_15    MX25_PIN_EXT_ARMCLK
     28 #define GPIO2_10    MX25_PIN_A24
     29 #define GPIO2_11    MX25_PIN_A25
     30 #define GPIO2_8     MX25_PIN_A22
     31 #define GPIO2_9     MX25_PIN_A23
     32 #define GPIO2_6     MX25_PIN_A20
     33 #define GPIO2_7     MX25_PIN_A21
     34 //command
     35 #define key_input     0
     36 #define version        1
     37 
     38 //interrupt head
     39 static DECLARE_WAIT_QUEUE_HEAD(key_interrupt_wait);
     40 static volatile unsigned char ev_press;  
     41 
     42 static int major=0;
     43 
     44 //auto to create device node
     45 static struct class *drv_class = NULL;
     46 static struct class_device *drv_class_dev = NULL;
     47 
     48 
     49 /* 应用程序对设备文件/dev/key_query执行open(...)时,
     50  * 就会调用key_open函数*/
     51 static int key_open(struct inode *inode, struct file *file)
     52 {
     53     printk("<0>function open!
    
    ");
     54     
     55     return 0;
     56 }
     57 
     58 /* 中断程序key_irq */
     59 static irqreturn_t key_irq(int irq, void *dev_id)
     60 {
     61     //发生了中断
     62     printk("<0>function interrupt key_irq!
    
    ");
     63     ev_press = 1;
     64     wake_up_interruptible(&key_interrupt_wait);
     65 
     66     return IRQ_RETVAL(IRQ_HANDLED);
     67 }
     68 
     69 
     70 static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
     71 {
     72     int ret;
     73     //nt cnt=0;
     74     unsigned char key_vals[8];
     75 
     76     //如果按键没有按下,没有中断,休眠
     77     wait_event_interruptible(key_interrupt_wait,ev_press);
     78 
     79     // reading the pins value
     80     key_vals[0] = gpio_get_value(IOMUX_TO_GPIO(GPIO2_6)) ? 1 : 0;
     81     key_vals[1] = gpio_get_value(IOMUX_TO_GPIO(GPIO2_7)) ? 1 : 0;
     82     key_vals[2] = gpio_get_value(IOMUX_TO_GPIO(GPIO2_8)) ? 1 : 0;
     83     key_vals[3] = gpio_get_value(IOMUX_TO_GPIO(GPIO2_9)) ? 1 : 0;
     84     key_vals[4] = gpio_get_value(IOMUX_TO_GPIO(GPIO2_10)) ? 1 : 0;
     85     key_vals[5] = gpio_get_value(IOMUX_TO_GPIO(GPIO2_11)) ? 1 : 0;
     86     key_vals[6] = gpio_get_value(IOMUX_TO_GPIO(GPIO2_21)) ? 1 : 0;
     87     key_vals[7] = gpio_get_value(IOMUX_TO_GPIO(GPIO3_15)) ? 1 : 0;
     88     
     89     ret = copy_to_user(buff,key_vals,sizeof(key_vals));
     90     if(ret){
     91         ;
     92     }
     93     ev_press = 0;
     94     //printk("<0>%04d key pressed: %d %d %d %d %d %d %d %d
    ",cnt++,key_vals[0],key_vals[1],key_vals[2],key_vals[3],key_vals[4],key_vals[5],key_vals[6],key_vals[7]); 
     95 
     96     return sizeof(key_vals);
     97 }
     98 
     99 static ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
    100 {
    101     printk("<0>function write!
    
    ");
    102     
    103     return 1;
    104 }
    105 
    106 static int  key_release(struct inode *inode, struct file *filp)
    107 {
    108     printk("<0>function release!
    
    ");
    109     //释放中断
    110     free_irq(IOMUX_TO_IRQ(GPIO2_21), (void *)1);
    111     //free_irq(IOMUX_TO_IRQ(GPIO3_15), (void *)1);
    112     free_irq(IOMUX_TO_IRQ(GPIO2_11), (void *)1);
    113     free_irq(IOMUX_TO_IRQ(GPIO2_10), (void *)1);
    114     free_irq(IOMUX_TO_IRQ(GPIO2_9), (void *)1);
    115     //free_irq(IOMUX_TO_IRQ(GPIO2_8), (void *)1);
    116     free_irq(IOMUX_TO_IRQ(GPIO2_7), (void *)1);
    117     free_irq(IOMUX_TO_IRQ(GPIO2_6), (void *)1);
    118     return 0;
    119 }
    120 
    121 static int key_ioctl(struct inode *inode,struct file *flip,unsigned int command,unsigned long arg)
    122 {
    123     int ret;
    124     printk("<0>function ioctl!
    
    ");
    125     switch (command) {
    126         case key_input:    
    127             //设置所有的引脚为输入
    128             gpio_direction_input(IOMUX_TO_GPIO(GPIO2_21));
    129             gpio_direction_input(IOMUX_TO_GPIO(GPIO3_15));
    130             gpio_direction_input(IOMUX_TO_GPIO(GPIO2_10));
    131             gpio_direction_input(IOMUX_TO_GPIO(GPIO2_11));
    132             gpio_direction_input(IOMUX_TO_GPIO(GPIO2_8));
    133             gpio_direction_input(IOMUX_TO_GPIO(GPIO2_9));
    134             gpio_direction_input(IOMUX_TO_GPIO(GPIO2_6));
    135             gpio_direction_input(IOMUX_TO_GPIO(GPIO2_7));
    136             printk("<0>have setting all pins to gpio input mod !
    ");
    137             //设置GPIO引脚为上拉模式
    138             mxc_iomux_set_pad(GPIO2_6, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU);
    139             mxc_iomux_set_pad(GPIO2_7, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU);
    140             //mxc_iomux_set_pad(GPIO2_8, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU);
    141             mxc_iomux_set_pad(GPIO2_9, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU);
    142             mxc_iomux_set_pad(GPIO2_10, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU);
    143             mxc_iomux_set_pad(GPIO2_11, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU);
    144             mxc_iomux_set_pad(GPIO2_21, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU);
    145             //mxc_iomux_set_pad(GPIO3_15, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU);
    146 
    147             //设置GPIO引脚中断  ,下降沿触发
    148             request_irq(IOMUX_TO_IRQ(GPIO2_7), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_7", (void *)1);
    149             request_irq(IOMUX_TO_IRQ(GPIO2_6), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_6", (void *)1);
    150             request_irq(IOMUX_TO_IRQ(GPIO2_9), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_9", (void *)1);
    151             request_irq(IOMUX_TO_IRQ(GPIO2_10), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_10", (void *)1);
    152             request_irq(IOMUX_TO_IRQ(GPIO2_11), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_11", (void *)1);
    153             request_irq(IOMUX_TO_IRQ(GPIO2_21), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_21", (void *)1);
    154             //request_irq(IOMUX_TO_IRQ(GPIO3_15), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO3_15", (void *)1);
    155             //request_irq(IOMUX_TO_IRQ(GPIO2_8), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_8", (void *)1);
    156             printk("<0>have setting all pins to gpio interrupt mod by IRQF_TRIGGER_FALLING !
    ");
    157 
    158             break;
    159         case version:
    160             printk("<0>hello,the version is 0.1.0
    
    ");
    161             break;
    162         default:
    163               printk("<0>command error 
    ");
    164             printk("<0>ioctl(fd, (unsigned int)command, (unsigned long) arg;
    ");
    165             printk("<0>command: <key_input> <version>
    
    ");
    166             return -1;
    167     }
    168     return 0;    
    169 }
    170 
    171 /* 这个结构是字符设备驱动程序的核心
    172  * 当应用程序操作设备文件时所调用的open、read、write等函数,
    173  * 最终会调用这个结构中指定的对应函数
    174  */
    175 static struct file_operations key_fops = {
    176     .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    177     .open   =   key_open,     
    178     .read    =    key_read,       
    179     .write    =    key_write,       
    180     .release=   key_release,
    181     .ioctl  =   key_ioctl,    
    182 };
    183     
    184 /*
    185  * 执行insmod命令时就会调用这个函数 
    186  */
    187 static int __init  key_irq_init(void)
    188 {
    189     printk("<0>
    Hello,this is %s module!
    
    ",Driver_NAME);
    190     //register and mknod
    191     major = register_chrdev(0,Driver_NAME,&key_fops);
    192     drv_class = class_create(THIS_MODULE,Driver_NAME);
    193     drv_class_dev = device_create(drv_class,NULL,MKDEV(major,0),NULL,DEVICE_NAME);    /*/dev/key_query*/
    194 
    195     //set all pins to GPIO mod  ALF5
    196     mxc_request_iomux(GPIO2_21, MUX_CONFIG_ALT5);
    197     mxc_request_iomux(GPIO3_15, MUX_CONFIG_ALT5);
    198     mxc_request_iomux(GPIO2_10, MUX_CONFIG_ALT5);
    199     mxc_request_iomux(GPIO2_11, MUX_CONFIG_ALT5);
    200     mxc_request_iomux(GPIO2_8, MUX_CONFIG_ALT5);
    201     mxc_request_iomux(GPIO2_9, MUX_CONFIG_ALT5);
    202     mxc_request_iomux(GPIO2_6, MUX_CONFIG_ALT5);
    203     mxc_request_iomux(GPIO2_7, MUX_CONFIG_ALT5);
    204     //request IOMUX GPIO
    205     gpio_request(IOMUX_TO_GPIO(GPIO2_21), "GPIO2_21");
    206     gpio_request(IOMUX_TO_GPIO(GPIO3_15), "GPIO3_15");
    207     gpio_request(IOMUX_TO_GPIO(GPIO2_10), "GPIO2_10");
    208     gpio_request(IOMUX_TO_GPIO(GPIO2_11), "GPIO2_11");
    209     gpio_request(IOMUX_TO_GPIO(GPIO2_8), "GPIO2_8");
    210     gpio_request(IOMUX_TO_GPIO(GPIO2_9), "GPIO2_9");
    211     gpio_request(IOMUX_TO_GPIO(GPIO2_6), "GPIO2_6");
    212     gpio_request(IOMUX_TO_GPIO(GPIO2_7), "GPIO2_7");
    213     
    214 
    215     return 0;
    216 }
    217 
    218 /*
    219  * 执行rmmod命令时就会调用这个函数 
    220  */
    221 static void __exit key_irq_exit(void)
    222 {
    223     printk("<0>
    Goodbye,%s!
    
    ",Driver_NAME);
    224 
    225     unregister_chrdev(major,Driver_NAME);
    226     device_unregister(drv_class_dev);
    227     class_destroy(drv_class);
    228 
    229     /* free gpios */
    230     mxc_free_iomux(GPIO2_21, MUX_CONFIG_ALT5);
    231     mxc_free_iomux(GPIO3_15, MUX_CONFIG_ALT5);
    232     mxc_free_iomux(GPIO2_10, MUX_CONFIG_ALT5);
    233     mxc_free_iomux(GPIO2_11, MUX_CONFIG_ALT5);
    234     mxc_free_iomux(GPIO2_8, MUX_CONFIG_ALT5);
    235     mxc_free_iomux(GPIO2_9, MUX_CONFIG_ALT5);
    236     mxc_free_iomux(GPIO2_6, MUX_CONFIG_ALT5);
    237     mxc_free_iomux(GPIO2_7, MUX_CONFIG_ALT5);
    238 
    239     gpio_free(IOMUX_TO_GPIO(GPIO2_21));
    240     gpio_free(IOMUX_TO_GPIO(GPIO3_15));
    241     gpio_free(IOMUX_TO_GPIO(GPIO2_10));
    242     gpio_free(IOMUX_TO_GPIO(GPIO2_11));
    243     gpio_free(IOMUX_TO_GPIO(GPIO2_8));
    244     gpio_free(IOMUX_TO_GPIO(GPIO2_9));
    245     gpio_free(IOMUX_TO_GPIO(GPIO2_6));
    246     gpio_free(IOMUX_TO_GPIO(GPIO2_7));
    247 
    248 }
    249 
    250 /* 这两行指定驱动程序的初始化函数和卸载函数 */
    251 module_init(key_irq_init);
    252 module_exit(key_irq_exit);
    253 
    254 /* 描述驱动程序的一些信息,不是必须的 */
    255 MODULE_AUTHOR("Lover雪");
    256 MODULE_VERSION("0.1.0");
    257 MODULE_DESCRIPTION("IMX257 key Driver");
    258 MODULE_LICENSE("GPL");
    key_interrupt.c

    大概讲解一下中断的实现:

    如上面的程序所示:

    1. 配置中断

    当我们要使用中断时,第一步肯定是要配置中断。所以,在我们的ioctl函数的gpio_input命令中,如下代码主要的功能就是配置各个引脚为中断模式,下降沿触发,中断的名称为key_GPIOm_n。

    还有一个就是注册中断函数,告诉内核,我们中断的处理函数为key_irq。

     
     1 Request_irq(IOMUX_TO_IRQ(GPIO2_7), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_7", (void *)1);
     2 
     3 request_irq(IOMUX_TO_IRQ(GPIO2_6), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_6", (void *)1);
     4 
     5 request_irq(IOMUX_TO_IRQ(GPIO2_9), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_9", (void *)1);
     6 
     7 request_irq(IOMUX_TO_IRQ(GPIO2_10), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_10", (void *)1);
     8 
     9 request_irq(IOMUX_TO_IRQ(GPIO2_11), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_11", (void *)1);
    10 
    11 request_irq(IOMUX_TO_IRQ(GPIO2_21), key_irq, IRQF_TRIGGER_FALLING, "key_GPIO2_21", (void *)1);
    1. read函数中睡眠

    中断配置好后,就开始使用中断,当我们用户程序read函数到来,而我们的按键却并没有按下时,为了节省资源,我们就可以让程序睡眠SLEEP,

    1 //如果按键没有按下,没有中断,休眠
    2 
    3 wait_event_interruptible(key_interrupt_wait,ev_press);
    4 
    5 ev_press = 0; //清除中断时设置的ev_pre = 1
    1. 中断唤醒程序

    当中断到来时,由于前面我们的程序在read函数中已经处于睡眠态了,所以我们中断的作用就是把我们的程序唤醒,在read函数中继续运行,读取相应的数据

     1 /* 中断程序key_irq */
     2 
     3 static irqreturn_t key_irq(int irq, void *dev_id)
     4 {
     5     //发生了中断
     6     printk("<0>function interrupt key_irq!
    
    ");
     7 
     8     ev_press = 1;
     9 
    10     wake_up_interruptible(&key_interrupt_wait);
    11     return IRQ_RETVAL(IRQ_HANDLED);
    12 }

    接下来,附上应用程序源代码;

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <fcntl.h>
     7 #include <termios.h>
     8 #include <errno.h>
     9 #include <limits.h>
    10 #include <asm/ioctls.h>
    11 #include <time.h>
    12 #include <pthread.h>
    13 
    14 #include "mx257_gpio.h"
    15 
    16 #define key_input     0
    17 #define version       1
    18 
    19 
    20 int main(int argc, char **argv)
    21 {
    22     int fd;
    23     int i=0,cnt=0;
    24     unsigned char key_vals[8];
    25     
    26     fd = open("/dev/key_interrupt",O_RDWR);
    27     if(fd < 0){
    28         printf("can't open !!!
    ");
    29     }
    30     ioctl(fd,version,NULL);
    31     ioctl(fd,key_input,NULL);
    32     while(1){
    33         read(fd, key_vals,sizeof(key_vals));
    34         printf("%04d key pressed: %d %d %d %d %d %d %d %d
    ",cnt++,key_vals[0],key_vals[1],key_vals[2],key_vals[3],key_vals[4],key_vals[5],key_vals[6],key_vals[7]);    
    35     }
    36     return 0;
    37 }
    key_test.c

    应用程序中,我们就不需要利用应用程序来检测是否按下,可以把检测的if判断语句去掉。

    接下来的编译工作就不赘述:

    Insmod
    
    ./test/key_test & (这里加 & 的意思是,在后台执行)
    
    cat /proc/interrupts

    结果如图所示:

    查看中断,可以看到我们前面设置的中断名字。

    当我们的按键按下,应用程序在后台检测到,就会打印出我们当前的引脚电平

    程序编写过程中碰到的错误:

    error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token

    1. : 很难想象,当我们的函数名字与头文件中的函数发生冲突时,gcc报错报的不是名字冲突,而是告诉我们前面缺少了;或者括号,

    因为这个问题,耗费了我足足一个下午加一个晚上的时间,事实证明,写程序时一定要仔细,否则查找错误的过程真的很痛苦,花的时间远比重新编写程序的时间多得多。

    分析:

    开始,我们的init函数名称为key_init,

    头文件包含为linux/inerrupt.h

    经过测试使用 linux/rtc.h 或者 linux/serial_reg.h 时效果一样。

    编译后结果如图所示:

    而当我们将函数名修改掉:

    编译通过,如图所示

    这里出现问题的原因时,我们的key_init函数,和我们内核中包含有free_irq的头文件中的函数发生冲突。

    总结一下第一个问题:

    首先,要声明并不是所有的这样的错误都是如上面的一样。

    有的时候,可能真的是因为前面的代码的语法错误而导致的,具体的情况要看具体的代码,具体分析。

    error: 'TASK_INTERRUPTIBLE' undeclared

    1. 第二个问题就是典型的头文件未包含的问题。

    因为在sched.h 头文件中包含了它的申明。

    #include <linux/sched.h>

    #include <linux/interrupt.h>

    例外附上:

    本人研究linux内核源代码时,发现很多文件中使用了 free_irq等中断申请和释放的函数,但是却未包含inrerrupt.h 的头文件,进过本人测试总结,

    可以使用interrupt.h,rtc.h, seria_reg.h 达到同样的效果,因为这些头文件中包含了interrupt.h。

    (ps:笔者认为,当我们碰到问题时,一定要有钻研精神,可能会花点时间,但这样当我们解决问题后,就会有一种豁然开朗的感觉,瞬间感觉自己又学到了知识,有助于自己的能力的提升,千万千万别一碰到问题就去问那些大神,大神会很无奈的,并且自己的查错能力也没法提升)

  • 相关阅读:
    WPF使用Mutex创建单实例程序失效
    招式百出的数组(js)
    js中,如何把一个混杂的字符串进行去重并按数字在左,字母在右组成的新字符串打印出来
    字符串对象(js)
    时间对象(js)
    数组迭代的5个小宝贝(js)
    线程的状态
    线程
    JAVA学习规划
    简单的反射实例
  • 原文地址:https://www.cnblogs.com/lihaiyan/p/4295619.html
Copyright © 2020-2023  润新知