• i.MX 6ULL 驱动开发 九:中断


    一、GICv2 基本概念

    GICv2 整理_lqonlylove的博客-CSDN博客

    二、Linux 中断子系统和中断上下部

    Linux 驱动开发 三十九:Linux 中断(一)_lqonlylove的博客-CSDN博客_linux msi中断

    三、设备树关于中断属性说明

    Linux 驱动开发 九:《Power_ePAPR_APPROVED_v1.12》翻译_lqonlylove的博客-CSDN博客

    Linux 驱动开发 十:《Devicetree Specification》翻译_lqonlylove的博客-CSDN博客

    Linux 驱动开发 三十七:《gic.txt》翻译_lqonlylove的博客-CSDN博客

    四、imx6ull在设备树中添加中断属性说明

    Linux 驱动开发 三十八:《fsl-imx-gpio.txt》翻译_lqonlylove的博客-CSDN博客

    五、原理图

    在这里插入图片描述
    通过原理图分析可以得到,当按键按下KEY0低电平,当按键释放KEY0高电平。通过原理图可以确定 KEY0 连接在 UART1_CTS 引脚上。

    六、添加设备树

    1、确定引脚

    通过原理图可以确定 key 使用 UART1_CTS 引脚。

    2、查找引脚定义是否冲突

    3、添加 pinctrl 子系统相关配置

    pinctrl_key: keygrp {
    	fsl,pins = <
    		MX6UL_PAD_UART1_CTS_B__GPIO1_IO18		0xF080	/* KEY0 */
    	>;
    };
    

    4、添加 gpio 子系统相关配置

    key {
    	#address-cells = <1>;
    	#size-cells = <1>;
    	compatible = "lq-key";
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_key>;
    	key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */
    	status = "okay";
    };
    

    5、添加 interrupts 相关配置

    key {
    	#address-cells = <1>;
    	#size-cells = <1>;
    	compatible = "lq-key";
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_key>;
    	key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */
    	interrupt-parent = <&gpio1>;
    	interrupts = <18 IRQ_TYPE_LEVEL_LOW>;  /* 低电平触发 */
    	status = "okay";
    };
    

    主要添加 interrupt-parentinterrupts 属性。

    6、测试

    1、编译设备树

    onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ make dtbs
      CHK     include/config/kernel.release
      CHK     include/generated/uapi/linux/version.h
      CHK     include/generated/utsrelease.h
    make[1]: 'include/generated/mach-types.h' is up to date.
      CHK     include/generated/bounds.h
      CHK     include/generated/asm-offsets.h
      CALL    scripts/checksyscalls.sh
      DTC     arch/arm/boot/dts/imx6ull-alientek-emmc.dtb
      DTC     arch/arm/boot/dts/imx6ull-alientek-nand.dtb
    onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$
    

    2、拷贝编译成功的设备树文件

    onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ ls /home/onlylove/my/tftp/ -l
    total 11528
    -rwxrwxr-x 1 onlylove onlylove 5901744 Sep 17 04:04 zImage
    -rwxrwxr-x 1 onlylove onlylove 5901752 Aug 20 01:24 zImage.bak
    onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ cp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb /home/onlylove/my/tftp
    onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ ls /home/onlylove/my/tftp/ -l
    total 11568
    -rw-rw-r-- 1 onlylove onlylove   39084 Sep 23 21:18 imx6ull-alientek-emmc.dtb
    -rwxrwxr-x 1 onlylove onlylove 5901744 Sep 17 04:04 zImage
    -rwxrwxr-x 1 onlylove onlylove 5901752 Aug 20 01:24 zImage.bak
    onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$
    

    3、启动 linux 查看设备树解析是否成功

    / # cd /proc/
    /proc # ls
    1              41             65             driver         mounts
    10             46             66             execdomains    mtd
    11             47             7              fb             net
    12             48             8              filesystems    pagetypeinfo
    13             49             83             fs             partitions
    14             5              84             interrupts     self
    15             50             9              iomem          softirqs
    16             51             95             ioports        stat
    17             52             asound         irq            swaps
    18             53             buddyinfo      kallsyms       sys
    19             54             bus            key-users      sysrq-trigger
    2              55             cgroups        keys           sysvipc
    20             56             cmdline        kmsg           thread-self
    21             57             consoles       kpagecount     timer_list
    22             58             cpu            kpageflags     tty
    23             59             cpuinfo        loadavg        uptime
    24             6              crypto         locks          version
    3              60             device-tree    meminfo        vmallocinfo
    4              61             devices        misc           vmstat
    40             62             diskstats      modules        zoneinfo
    /proc # cd device-tree/
    /sys/firmware/devicetree/base # ls
    #address-cells                 key
    #size-cells                    memory
    aliases                        model
    alphaled                       name
    backlight                      pxp_v4l2
    beep                           regulators
    chosen                         reserved-memory
    clocks                         sii902x-reset
    compatible                     soc
    cpus                           sound
    gpioled                        spi4
    interrupt-controller@00a01000
    /sys/firmware/devicetree/base # cd key/
    /sys/firmware/devicetree/base/key # ls
    #address-cells    interrupt-parent  name              status
    #size-cells       interrupts        pinctrl-0
    compatible        key-gpio          pinctrl-names
    /sys/firmware/devicetree/base/key # cat compatible
    lq-key
    /sys/firmware/devicetree/base/key #
    /sys/firmware/devicetree/base/key #
    

    七、按键消抖

    使用内核定时器进行按键消抖。

    Linux 驱动开发 三十四:Linux 内核定时器原理_lqonlylove的博客-CSDN博客_linux内核定时器原理

    八、代码逻辑

    1、设置按键中断为低电平触发。

    2、触发按键中断后,保存按键状态。

    3、使用原子变量保存按键状态。

    4、在驱动的 read 接口中将按键状态返回给应用程序,并设置按键状态变量值。

    九、驱动编写

    #include "linux/init.h"
    #include "linux/module.h"
    #include "linux/kdev_t.h"
    #include "linux/fs.h"
    #include "linux/cdev.h"
    #include "linux/device.h"
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define NEWCHRDEV_MAJOR 0   		/* 主设备号(如果为0则让系统自动分配,如果大于0则使用指定设备号) */
    #define NEWCHRDEV_MINOR 0   		/* 次设备号 */
    #define NEWCHRDEV_COUNT 1   		/* 设备号个数 */
    #define NEWCHRDEV_NAME  "key"       /* 名子 */
    #define TIMEOUT 1000                 /* 定时器超时时间 */
    
    typedef struct{
        struct device_node	*nd;        /* 设备节点 */
        int gpio;                       /* key gpio */
        int irqnum;                     /* 中断号 */
        struct tasklet_struct tasklet;  /* tasklet 变量(中断下半部) */
        unsigned long tasklet_data;     /* 传递给 tasklet 处理函数的参数 */
        struct work_struct work;        /* 工作队列 变量(中断下半部) */
        struct timer_list timer;        /* key 消抖定时器 */
        atomic_t keyvalue;              /* 按键状态 */
    }lqkey_t;
    
    typedef struct{
        struct cdev dev;        /* cdev 结构体 */
        int major;              /* 主设备号 */
        int minor;              /* 次设备号 */
        dev_t devid;            /* 设备号 */
        struct class *class;    /* 类 */
        struct device *device;  /* 设备 */
        lqkey_t key_data;         /* key 相关数据 */
    }newchrdev_t;
    
    newchrdev_t newchrdev;
    
    /*
     * @description	: 按键IO初始化
     * @param 		: 无
     * @return 		: 无
     */
    static int keyio_init(void)
    {
        int ret = 0;
        /***** 处理设备树 *****/
        /* 1、获取设备树节点 */
        newchrdev.key_data.nd = of_find_node_by_path("/key");
    	if (newchrdev.key_data.nd== NULL){
    		printk("key node not find!\r\n");
    		goto keyio_init_error1;
    	}else {
    		printk("beep node find!\r\n");
    	}
        /* 1、获取设备树中的gpio属性 */
        newchrdev.key_data.gpio = of_get_named_gpio(newchrdev.key_data.nd, "key-gpio", 0);
    	if(newchrdev.key_data.gpio < 0) {
    		printk("can't get key-gpio");
    		goto keyio_init_error1;
    	}
    	printk("key num = %d\r\n", newchrdev.key_data.gpio);
    
        /***** 使用gpio子系统设置引脚 *****/
        /* 1、向 gpio 子系统申请 GPIO 管脚 */
        ret = gpio_request(newchrdev.key_data.gpio,"key");
        if(ret){
            printk("can't request key-gpio!\r\n");
            goto keyio_init_error1;
        }
        /* 2、设置key-gpio为输入 */
        gpio_direction_input(newchrdev.key_data.gpio);
        if(ret < 0) {
    		printk("can't set key-gpio!\r\n");
            goto keyio_init_error2;
    	}
        return 0;
    
    keyio_init_error2:
        gpio_free(newchrdev.key_data.gpio);
    keyio_init_error1:
        return 1;
    }
    
    static int keyio_exit(void)
    {
        /* 1、向gpio子系统请求释放gpio */
        gpio_free(newchrdev.key_data.gpio);
        return 0;
    }
    
    static void key_tasklet(unsigned long data)
    {
        printk("key_tasklet\r\n");
    }
    
    static void key_work(struct work_struct *work)
    {
        printk("key_work\r\n");
        mod_timer(&newchrdev.key_data.timer, jiffies + msecs_to_jiffies(TIMEOUT));
    }
    
    /* @description		: 中断服务函数
     *				  	  定时器用于按键消抖
     * @param - irq 	: 中断号 
     * @param - dev_id	: 设备结构
     * @return 			: 中断执行结果
     */
    static irqreturn_t key0_handler(int irq, void *dev_id)
    {
        printk("key0_handler\r\n");
    //    tasklet_schedule(&newchrdev.key_data.tasklet);
        schedule_work(&newchrdev.key_data.work);
    	return IRQ_RETVAL(IRQ_HANDLED);
    }
    
    /*
     * @description	: key中断初始化
     * @param 		: 无
     * @return 		: 无
     */
    static int keyirq_init(void)
    {
        int ret = 0;
        /***** 使用中断子系统设置 *****/
        /* 1、从设备树获取key-gpio对于中断号 */
        newchrdev.key_data.irqnum = irq_of_parse_and_map(newchrdev.key_data.nd, 0);
        if (!newchrdev.key_data.irqnum){
            goto keyirq_init_error1;
        }
        /* 2、向中断子系统请求中断号 */
        ret = request_irq(newchrdev.key_data.irqnum, key0_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,"KEY0",&newchrdev);
        if(ret < 0){
            printk("can't request irq!\r\n");
            goto keyirq_init_error1;
        }
        /***** 使用中断上下部(tasklet和工作队列选择一个) *****/
        /* 1、初始化中断下半部 */
    //    tasklet_init(&newchrdev.key_data.tasklet,key_tasklet,newchrdev.key_data.tasklet_data);
        INIT_WORK(&newchrdev.key_data.work, key_work);
        return 0;
    keyirq_init_error1:
        return 1;
    }
    
    static int keyirq_exit(void)
    {
        /* 1、向中断子系统请求释放中断号 */
        free_irq(newchrdev.key_data.irqnum,&newchrdev);
        /***** 使用中断上下部(tasklet和工作队列选择一个) *****/
        /* 1、销毁中断下半部 */
    //    tasklet_kill(&newchrdev.key_data.tasklet);
        flush_work(&newchrdev.key_data.work);
        return 0;
    }
    
    void key_timer_function(unsigned long arg)
    {
        printk("key_timer_function\r\n");
        /* 1、设置按键状态(按键按下) */
        atomic_set(&newchrdev.key_data.keyvalue,0x01);
    }
    
    /*
     * @description	: key消抖定时器初始化
     * @param 		: 无
     * @return 		: 无
     */
    static int keytimer_init(void)
    {
        /* 1、创建定时器 */
        init_timer(&newchrdev.key_data.timer);
        newchrdev.key_data.timer.function = key_timer_function;
        newchrdev.key_data.timer.expires = jiffies + msecs_to_jiffies(TIMEOUT);
        newchrdev.key_data.timer.data = (unsigned long)&newchrdev;
        return 0;
    }
    
    static int keytimer_exit(void)
    {
        /* 删除、创建定时器 */
        del_timer(&newchrdev.key_data.timer);
        return 0;
    }
    
    /*
     * @description		: 打开设备
     * @param - inode 	: 传递给驱动的inode
     * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
     * 					  一般在open的时候将private_data指向设备结构体。
     * @return 			: 0 成功;其他 失败
     */
    static int newchrdev_open(struct inode *inode, struct file *filp)
    {
        printk("newchrdev_open!\r\n");
        filp->private_data = &newchrdev; /* 设置私有数据 */
        return 0;
    }
    
    /*
     * @description		: 从设备读取数据 
     * @param - filp 	: 要打开的设备文件(文件描述符)
     * @param - buf 	: 返回给用户空间的数据缓冲区
     * @param - cnt 	: 要读取的数据长度
     * @param - offt 	: 相对于文件首地址的偏移
     * @return 			: 读取的字节数,如果为负值,表示读取失败
     */
    static ssize_t newchrdev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
    {
        int ret = 0;
        unsigned char keystatus = 0;
    //    printk("newchrdev_read!\r\n");
        keystatus = atomic_read(&newchrdev.key_data.keyvalue);
        if(keystatus == 0x01){
            ret = copy_to_user(buf, &keystatus, sizeof(keystatus));
            atomic_set(&newchrdev.key_data.keyvalue,0xFF);
        }else{
            goto data_error;
        }
    	return 0;
    data_error:
    	return -EINVAL;
    }
    
    /*
     * @description		: 向设备写数据 
     * @param - filp 	: 设备文件,表示打开的文件描述符
     * @param - buf 	: 要写给设备写入的数据
     * @param - cnt 	: 要写入的数据长度
     * @param - offt 	: 相对于文件首地址的偏移
     * @return 			: 写入的字节数,如果为负值,表示写入失败
     */
    static ssize_t newchrdev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
    {
        printk("newchrdev_write!\r\n");
        return 0;
    }
    
    /*
     * @description		: 关闭/释放设备
     * @param - filp 	: 要关闭的设备文件(文件描述符)
     * @return 			: 0 成功;其他 失败
     */
    static int newchrdev_release(struct inode *inode, struct file *filp)
    {
        printk("newchrdev_release!\r\n");
    	return 0;
    }
    
    static const struct file_operations newchrdevops = {
        .owner   = THIS_MODULE,
        .open = newchrdev_open,
    	.read = newchrdev_read,
    	.write = newchrdev_write,
    	.release = newchrdev_release,
    };
    
    /* 驱动入口函数 */
    static int __init newchrdev_init(void)
    {
        /* 驱动入口函数具体内容 */
        /* 1、字符设备号分配 */
        int ret;
        newchrdev.major = NEWCHRDEV_MAJOR;
        if(newchrdev.major){
            newchrdev.minor = NEWCHRDEV_MINOR;
            newchrdev.devid = MKDEV(newchrdev.major, newchrdev.minor);
            ret = register_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);
        }else{
            ret = alloc_chrdev_region(&newchrdev.devid,0,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);
            newchrdev.major = MAJOR(newchrdev.devid);
            newchrdev.minor = MINOR(newchrdev.devid);
        }
        if(ret < 0){
            printk("newchrdev xxx_chrdev_region failed!\r\n");
            goto newchrdev_chrdev_region_failed;
        }
        printk("newchrdev major=%d,minor=%d\r\n",newchrdev.major,newchrdev.minor);
    
        /* 2、注册字符设备 */
        newchrdev.dev.owner = THIS_MODULE;
        cdev_init(&newchrdev.dev,&newchrdevops);
        ret = cdev_add(&newchrdev.dev,newchrdev.devid,NEWCHRDEV_COUNT);
        if(ret < 0){
            printk("newchrdev cdev_add failed!\r\n");
            goto newchrdev_cdev_add_failed;
        }
    
        /* 3、创建类 */
        newchrdev.class = class_create(THIS_MODULE,NEWCHRDEV_NAME);
        if(IS_ERR(newchrdev.class)) {
            printk("newchrdev class_create failed!\r\n");
            goto newchrdev_class_create_failed;
        }
    
        /* 4、创建设备 */
        newchrdev.device = device_create(newchrdev.class,NULL,newchrdev.devid,NULL,NEWCHRDEV_NAME);
        if(IS_ERR(newchrdev.device)){
            printk("newchrdev device_create failed!\r\n");
            goto neschrdev_device_creat_failed;
        }
    
        ret = keyio_init();
        if(ret != 0){
            goto keyio_init_failed;
        }
    
        ret = keyirq_init();
        if(ret != 0){
            goto keyirq_init_failed;
        }
    
        keytimer_init();
        /* 初始化按键状态值 */
        atomic_set(&newchrdev.key_data.keyvalue,0xFF);
    
        return 0;
    keyirq_init_failed:
        keyio_exit();
    keyio_init_failed:
    neschrdev_device_creat_failed:
        class_destroy(newchrdev.class);
    newchrdev_class_create_failed:
        cdev_del(&newchrdev.dev);
    newchrdev_cdev_add_failed:
        unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
    
    newchrdev_chrdev_region_failed:   /* 字符设备号分配失败处理函数(未分配资源,因此不做处理) */
        return ret;
    }
    
    /* 驱动卸载函数 */
    static void __exit newchrdev_exit(void)
    {
        keytimer_exit();
        keyirq_exit();
        keyio_exit();
        /* 驱动卸载函数具体内容 */
        /* 4、删除设备 */
        device_destroy(newchrdev.class,newchrdev.devid);
        /* 3、删除类 */
        class_destroy(newchrdev.class);
        /* 2、注销字符设备 */
        cdev_del(&newchrdev.dev);
        /* 1、释放设备号 */
        unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
    }
    
    module_init(newchrdev_init);
    module_exit(newchrdev_exit);
    
    MODULE_LICENSE("GPL");
    

    十、应用程序编写

    #include 
    #include 
    #include 
    #include "stdio.h"
    
    int main(int argc, char *argv[])
    {
        int fd = 0, retvalue = 0;
        char readbuf = 0;
        if(argc != 2){
    		printf("Error Usage!\r\n");
    		return -1;
    	}
    
        fd = open(argv[1],O_RDWR);
        if(fd < 0){
            printf("Can't open file %s\r\n", argv[1]);
            return -1;
        }
        printf("sizeof(readbuf) = %d\r\n",sizeof(readbuf));
        while(1){
            retvalue = read(fd, &readbuf, sizeof(readbuf));
            if (retvalue < 0){
    
            }else{
                if (readbuf)
                    printf("key value = %#X\r\n", readbuf);
            }
        }
        close(fd);
        return 0;
    }
    

    十一、测试

    1、加载驱动

    / # ls
    beep.ko        home           lib            newchrdev_app  tmp
    beep_app       include        linuxrc        proc           usr
    bin            key.ko         minicom.log    root           var
    dev            key_app        mnt            sbin           video
    drivers        led.ko         music          share
    etc            led_app        newchrdev.ko   sys
    / # insmod key.ko
    newchrdev major=248,minor=0
    beep node find!
    key num = 18
    / # rmmod key.ko
    / # insmod key.ko
    newchrdev major=248,minor=0
    beep node find!
    key num = 18
    / # rmmod key.ko
    / # insmod key.ko
    newchrdev major=248,minor=0
    beep node find!
    key num = 18
    / # rmmod key.ko
    

    2、确定中断是否注册成功

    / # ls
    beep.ko        home           lib            newchrdev_app  tmp
    beep_app       include        linuxrc        proc           usr
    bin            key.ko         minicom.log    root           var
    dev            key_app        mnt            sbin           video
    drivers        led.ko         music          share
    etc            led_app        newchrdev.ko   sys
    / # cat /proc/interrupts
               CPU0
     16:       2578       GPC  55 Level     i.MX Timer Tick
     18:          0       GPC  33 Level     2010000.ecspi
     19:        769       GPC  26 Level     2020000.serial
     20:          0       GPC  98 Level     sai
     21:          0       GPC  50 Level     2034000.asrc
     37:          0  gpio-mxc   9 Edge      edt-ft5426
     47:          0  gpio-mxc  19 Edge      2190000.usdhc cd
    196:          0       GPC   4 Level     20cc000.snvs:snvs-powerkey
    197:       9778       GPC 120 Level     20b4000.ethernet
    198:          0       GPC 121 Level     20b4000.ethernet
    199:          0       GPC  80 Level     20bc000.wdog
    202:          0       GPC  49 Level     imx_thermal
    207:          0       GPC  19 Level     rtc alarm
    213:          0       GPC   2 Level     sdma
    218:          0       GPC  43 Level     2184000.usb
    219:         35       GPC  42 Level     2184200.usb
    220:          0       GPC 118 Level     2188000.ethernet
    221:          0       GPC 119 Level     2188000.ethernet
    222:         34       GPC  22 Level     mmc0
    223:         64       GPC  23 Level     mmc1
    224:          1       GPC 100 Level     2198000.adc
    225:          0       GPC  36 Level     21a0000.i2c
    226:        196       GPC  37 Level     21a4000.i2c
    229:          2       GPC   5 Level     21c8000.lcdif
    232:          1       GPC 107 Level
    233:          0       GPC  28 Level     21ec000.serial
    IPI0:          0  CPU wakeup interrupts
    IPI1:          0  Timer broadcast interrupts
    IPI2:          0  Rescheduling interrupts
    IPI3:          0  Function call interrupts
    IPI4:          0  Single function call interrupts
    IPI5:          0  CPU stop interrupts
    IPI6:          0  IRQ work interrupts
    IPI7:          0  completion interrupts
    Err:          0
    / # insmod key.ko
    newchrdev major=248,minor=0
    beep node find!
    key num = 18
    / # cat /proc/interrupts
               CPU0
     16:       2718       GPC  55 Level     i.MX Timer Tick
     18:          0       GPC  33 Level     2010000.ecspi
     19:        903       GPC  26 Level     2020000.serial
     20:          0       GPC  98 Level     sai
     21:          0       GPC  50 Level     2034000.asrc
     37:          0  gpio-mxc   9 Edge      edt-ft5426
     46:          0  gpio-mxc  18 Edge      KEY0
     47:          0  gpio-mxc  19 Edge      2190000.usdhc cd
    196:          0       GPC   4 Level     20cc000.snvs:snvs-powerkey
    197:       9851       GPC 120 Level     20b4000.ethernet
    198:          0       GPC 121 Level     20b4000.ethernet
    199:          0       GPC  80 Level     20bc000.wdog
    202:          0       GPC  49 Level     imx_thermal
    207:          0       GPC  19 Level     rtc alarm
    213:          0       GPC   2 Level     sdma
    218:          0       GPC  43 Level     2184000.usb
    219:         35       GPC  42 Level     2184200.usb
    220:          0       GPC 118 Level     2188000.ethernet
    221:          0       GPC 119 Level     2188000.ethernet
    222:         34       GPC  22 Level     mmc0
    223:         64       GPC  23 Level     mmc1
    224:          1       GPC 100 Level     2198000.adc
    225:          0       GPC  36 Level     21a0000.i2c
    226:        196       GPC  37 Level     21a4000.i2c
    229:          2       GPC   5 Level     21c8000.lcdif
    232:          1       GPC 107 Level
    233:          0       GPC  28 Level     21ec000.serial
    IPI0:          0  CPU wakeup interrupts
    IPI1:          0  Timer broadcast interrupts
    IPI2:          0  Rescheduling interrupts
    IPI3:          0  Function call interrupts
    IPI4:          0  Single function call interrupts
    IPI5:          0  CPU stop interrupts
    IPI6:          0  IRQ work interrupts
    IPI7:          0  completion interrupts
    Err:          0
    / #
    

    通过以上日志可以确定,按键中断加载成功后会出现 46: 0 gpio-mxc 18 Edge KEY0 行。

    3、测试驱动程序和应用程序

    / # ls
    beep.ko        home           lib            newchrdev_app  tmp
    beep_app       include        linuxrc        proc           usr
    bin            key.ko         minicom.log    root           var
    dev            key_app        mnt            sbin           video
    drivers        led.ko         music          share
    etc            led_app        newchrdev.ko   sys
    / # insmod key.ko
    newchrdev major=248,minor=0
    beep node find!
    key num = 18
    / # ls -l /dev/key
    crw-rw----    1 root     0         248,   0 Jan  1 10:46 /dev/key
    / # rmmod key.ko
    / # ls -l /dev/key
    ls: /dev/key: No such file or directory
    / # insmod key.ko
    newchrdev major=248,minor=0
    beep node find!
    key num = 18
    / # ls -l /dev/key
    crw-rw----    1 root     0         248,   0 Jan  1 10:46 /dev/key
    / # ./key_app /dev/key
    newchrdev_open!
    sizeof(readbuf) = 1
    key0_handler
    key_work
    key0_handler
    key_work
    key_timer_function
    key value = 0X1
    key0_handler
    key_work
    key0_handler
    key0_handler
    key_work
    key_timer_function
    key value = 0X1
    key0_handler
    key_work
    key0_handler
    key0_handler
    key_work
    key_timer_function
    key value = 0X1
    ^Cnewchrdev_release!
    
    / # rmmod key.ko
    / # ls -l /dev/key
    ls: /dev/key: No such file or directory
    / #
    

    十二、系统资源使用情况

    在这里插入图片描述

    十三、特别说明

    在驱动相关接口中一定要处理异常情况。如果在驱动接口中缺少异常相关处理,会出现未定义现象,极难调试。

    记一次失败经历:应用程序读取按键值时,未对 newchrdev_read 进行异常处理,出现以下现象:

    • 应用程序中 read 始终返回 0,无法读取到数据。
    • 应用程序中 read 始终可以读取到数据,但驱动中未向应用程序传输数据。
  • 相关阅读:
    数字三角形模型相关题目
    Android Service介绍
    Linux 之 Ubuntu 代码开发工具 Visual Studio Code(VSCode) 的安装与一些常用插件配置的简单整理
    遇见未来的你——过程中真是苦了你妈妈
    基于JavaWeb+SpringBoot+Vue超市管理系统的设计和实现
    无人机通信协议MAVLink简介
    uniapp 解决请求出现 /sockjs-node/info?t=问题
    [附源码]计算机毕业设计springboot创意摄影交流平台
    MyBatis—Spring 动态数据源事务的处理
    14.baseline与bias/variance2
  • 原文地址:https://blog.csdn.net/OnlyLove_/article/details/127033725