• 《中断学习—— 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!
    ",
                                    __func__, __LINE__);
                    return -EIO;
            }
    		//读出GPIO输入值
            pr_info ("[%s %d]gpio%d_%d in %d
    ", __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]
    ", __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!
    ",
                                    __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!
    ",
                                            __func__, __LINE__);
                            return -1;
            }
    
            pr_info("[%s %d]gpio_irq_type = %d
    ", __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!
    ", __func__, __LINE__);
                    return -1;
            }
    
            return 0;
    }
    
    static void gpio_dev_test_irq_exit(unsigned int gpio_num)
    {
            unsigned long flags;
    
            pr_info("[%s %d]
    ", __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!
    ",
                                    __func__, __LINE__);
                    return -EIO;
            }
    
            pr_info("[%s %d]gpio%d_%d out %d
    ", __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]
    ", __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 
    ", __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]
    ", __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博客

      

      

  • 相关阅读:
    Shell中调用、引用、包含另一个脚本文件的三种方法
    mysql基础
    传智博客(JavaWeb方面的所有知识)听课记录(经典)
    nginx配置负载均衡与反向代理
    nginx 详解
    iOS开发之集成ijkplayer视频直播
    Nginx配置文件nginx.conf中文详解(总结)
    WorldWind源码剖析系列:数学引擎类MathEngine
    WorldWind源码剖析系列:二维点类Point2d和三维点类Point3d
    WorldWind源码剖析系列:枚举类型
  • 原文地址:https://www.cnblogs.com/zhuangquan/p/13801801.html
Copyright © 2020-2023  润新知