完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本帖最后由 zxl_zxl 于 2016-7-11 11:16 编辑 【创龙AM4379 Cortex-A9试用体验】之I/O中断异步通知驱动程序+QT捕获Linux系统信号+测试信号通知 之前在写过一篇按键中断+去抖的试用报告,那篇报告只是在中断处理程序中做了LED灯亮灭操作,而没有将中断信息通知到用户层应用程序中,如果用户应用程序需要获取底层的I/O状态时,还必须通过轮询或阻塞的方式读取,显然,这种读取I/O状态信息的方式,效率非常低下,浪费了大量的系统执行时间。 这篇试用报告,我们主要介绍GPIO外部中断的驱动程序开发,在内核的中断处理程序中完按键的“按下”、“松开”判断,并将按键状态信息通知应用层。本报告应用层采用QT图形界面的形式展示,当按键被按下时,QT界面上对应的“复选框”被选中,当按键被松开时,QT界面上对应的“复选框”勾选消失。 1. 搭建硬件 查看TL-4379开发板的硬件手册,结合原理图,我们选择GPIO3-14,GPIO3-16,GPIO4-2,GPIO4_3作为外部按键中断源,如图所示: 硬件连接如图所示: 简单起见,我这里只连接了一个按键,驱动程序中实现了4个按键的功能。 由于扩展的I/O接口中没有DC3.3V电源,我们从IIC接口引入DC3.3V电源。 2. 驱动程序 安装字符设备驱动程序开发流程开发。 2.1 资源定义 定义按键I/O端口号、I/O中断号,以及字符设备的主设备号变量: #define GPIO_KEY1_PIN_NUM (3*32 + 14) /*gpio 3_14 */ #define GPIO_KEY2_PIN_NUM (3*32 + 16) /*gpio 3_16 */ #define GPIO_KEY3_PIN_NUM (4*32 + 3) /*gpio 4_3 */ #define GPIO_KEY4_PIN_NUM (4*32 + 2) /*gpio 4_2 */ #define KEY1_IRQ gpio_to_irq(GPIO_KEY1_PIN_NUM) /*获取KEY1中断号*/ #define KEY2_IRQ gpio_to_irq(GPIO_KEY2_PIN_NUM) /*获取KEY2中断号*/ #define KEY3_IRQ gpio_to_irq(GPIO_KEY3_PIN_NUM) /*获取KEY3中断号*/ #define KEY4_IRQ gpio_to_irq(GPIO_KEY4_PIN_NUM) /*获取KEY4中断号*/ #define DEVICE_NAME "keys" static int major; static int minor; struct cdev *key_irq; /* cdev 数据结构 */ static dev_t devno; /* 设备编号 */ static struct class *key_irq_class; static struct class *key_async_class; static struct class_device *key_async_class_dev; 2.2 定义等待队列、互斥锁 本驱动程序支持阻塞读取、异步通知,所以设计了相应的等待队列,和互斥锁结构体,以及按键完成标志位: *定义按键等待队列*/ static DECLARE_WAIT_QUEUE_HEAD(key_waitq); /* 中断事件标志, 中断服务程序将它置1,key_drv_read将它清0 */ static volatile int ev_press = 0; static struct fasync_struct *key_async; struct pin_desc{ unsignedint pin; unsignedint key_val; }; /* 键值: 按下时, 0x01, 0x02 */ /* 键值: 松开时, 0x81, 0x82 */ static unsigned char key_val; struct pin_desc pins_desc[4] = { {GPIO_KEY1_PIN_NUM,0x01}, {GPIO_KEY2_PIN_NUM,0x02}, {GPIO_KEY3_PIN_NUM,0x03}, {GPIO_KEY4_PIN_NUM,0x04}, }; /*按键驱动同一时间只能由一个应用程序打开*/ static DEFINE_SEMAPHORE(key_lock); //定义互斥锁 2.3 中断处理函数 在中断处理函数中,我们判断触发该中断的device编号,然后读取该编号对应按键的高、低电平,生产keycode,设置按键中断完成标志,唤醒等待队列中的进程,并将中断信息异步通知应用层。 static irqreturn_t keys_irq(int irq, void*dev_id) { structpin_desc * pindesc = (struct pin_desc *)dev_id; unsignedint pinval; //printk("Interruptproduced!n"); pinval= gpio_get_value(pindesc->pin); if(pinval) //高电平代表按键松开 { /*松开 */ key_val= 0x80 | pindesc->key_val; } else //低电平代表按键按下 { /*按下 */ key_val= pindesc->key_val; } ev_press = 1; /* 表示中断发生了 */ wake_up_interruptible(&key_waitq); /* 唤醒休眠的进程 */ kill_fasync(&key_async, SIGIO, POLL_IN); /*向应用层发送IO信号*/ returnIRQ_HANDLED; } 2.4 open函数 系统的中断资源时非常宝贵的,我们一般在真正开始使用中断时才申请中断,文件关闭时立即释放中断。 static int key_drv_open(struct inode*inode, struct file *file) { inti; intret; if(file->f_flags & O_NONBLOCK) //非阻塞式打开文件 { if(down_trylock(&key_lock)) return-EBUSY; } else //阻塞式打开文件 { /*获取信号量 */ down(&key_lock); } ret= gpio_request_one(pins_desc[0].pin, GPIOF_IN, "KEY1 IRQ"); /* 申请 IO ,为输入*/ if(ret < 0) { printk(KERN_ERR"Failed to request GPIO for KEY1n"); } ret= gpio_request_one(pins_desc[1].pin, GPIOF_IN, "KEY2 IRQ"); /* 申请 IO ,为输入*/ if(ret < 0) { printk(KERN_ERR"Failed to request GPIO for KEY2n"); } ret= gpio_request_one(pins_desc[2].pin, GPIOF_IN, "KEY3 IRQ"); /* 申请 IO ,为输入*/ if(ret < 0) { printk(KERN_ERR"Failed to request GPIO for KEY3n"); } ret= gpio_request_one(pins_desc[3].pin, GPIOF_IN, "KEY4 IRQ"); /* 申请 IO ,为输入*/ if(ret < 0) { printk(KERN_ERR"Failed to request GPIO for KEY4n"); } /*设置 GPIO 为输入 */ for(i=0;i<4;i++) gpio_direction_input(pins_desc.pin); /*为GPIO3_14,GPIO3_16,GPIO4_2,GPIO4_3申请中断 */ if(request_irq(KEY1_IRQ,keys_irq, IRQ_TYPE_EDGE_BOTH, "K1", &pins_desc[0])) { printk(KERN_ERR"Failed to request IRQ for KEY1n"); } if(request_irq(KEY2_IRQ,keys_irq, IRQ_TYPE_EDGE_BOTH, "K2", &pins_desc[1])) { printk(KERN_ERR"Failed to request IRQ for KEY2n"); } if(request_irq(KEY3_IRQ,keys_irq, IRQ_TYPE_EDGE_BOTH, "K3", &pins_desc[2])) { printk(KERN_ERR"Failed to request IRQ for KEY3n"); } if(request_irq(KEY4_IRQ,keys_irq, IRQ_TYPE_EDGE_BOTH, "K4", &pins_desc[3])) { printk(KERN_ERR"Failed to request IRQ for KEY4n"); } return0; } 2.5 read函数 ssize_t key_drv_read(struct file *file,char __user *buf, size_t size, loff_t *ppos) { if(size != 1) return-EINVAL; if(file->f_flags & O_NONBLOCK) { if(!ev_press) return-EAGAIN; } else { /*如果没有按键动作, 休眠,等待ev_press变为1,即等待按键触发中断 */ wait_event_interruptible(key_waitq,ev_press); } /*如果有按键动作, 返回键值 */ copy_to_user(buf,&key_val, 1); ev_press= 0; return1; } 2.6 close函数 文件关闭时,释放中断资源。 int key_drv_close(struct inode *inode,struct file *file) { free_irq(KEY1_IRQ,&pins_desc[0]); free_irq(KEY2_IRQ,&pins_desc[1]); free_irq(KEY3_IRQ,&pins_desc[2]); free_irq(KEY4_IRQ,&pins_desc[3]); up(&key_lock); return0; } 2.7 查询与异步函数与文件操作结构体 static unsigned key_drv_poll(struct file*file, poll_table *wait) { unsignedint mask = 0; poll_wait(file,&key_waitq, wait); // 不会立即休眠 if(ev_press) mask|= POLLIN | POLLRDNORM; returnmask; } static int key_drv_fasync (int fd, structfile *filp, int on) { printk("driver:key_drv_fasyncn"); returnfasync_helper (fd, filp, on, &key_async); } static struct file_operations key_drv_fops= { .owner = THIS_MODULE, .open = key_drv_open, .read = key_drv_read, .release= key_drv_close, .poll = key_drv_poll, .fasync = key_drv_fasync, }; 2.8 内核模块初始化函数 内核模块初始化函数,负责申请驱动程序主设备号,创建设备类,和/dev目录下的设备。 static int key_drv_init(void) { intret; ret= alloc_chrdev_region(&devno, minor, 1, DEVICE_NAME); /* 从系统获取主设备号 */ major= MAJOR(devno); if(ret < 0) { printk(KERN_ERR"cannot get major %d n", major); return-1; } key_irq= cdev_alloc(); /* 分配 key_irq 结构 */ if(key_irq != NULL) { cdev_init(key_irq,&key_drv_fops); /* 初始化 key_irq 结构 */ key_irq->owner= THIS_MODULE; if(cdev_add(key_irq, devno, 1) != 0) { /* 增加 key_irq 到系统中 */ printk(KERN_ERR"add cdev error!n"); gotoerror; } } else{ printk(KERN_ERR"cdev_alloc error!n"); return-1; } key_irq_class= class_create(THIS_MODULE, "key_irq_class"); if(IS_ERR(key_irq_class)) { printk(KERN_INFO"create class errorn"); return-1; } device_create(key_irq_class,NULL, devno, NULL, DEVICE_NAME); printk("Initcompleted!n"); return0; error: unregister_chrdev_region(devno,1); /* 释放已经获得的设备号 */ returnret; return0; } 2.8 内核模块退出函数 释放初始化内核模块时申请的资源: static void key_drv_exit(void) { inti; for(i=0;i<4;i++) gpio_free(pins_desc.pin); /*释放为按键KEY申请的GPIO资源*/ cdev_del(key_irq);/* 移除字符设备 */ unregister_chrdev_region(devno,1); /* 释放设备号 */ device_destroy(key_irq_class,devno); class_destroy(key_irq_class); printk(KERN_INFO"Exit completed!n"); return0; } 3 驱动程序Makefile与编译 在编译驱动之前首选要编译与TL-4379开发板当前正在运行的系统版本相一致的内核,然后编写该驱动模块的Makefile。我们在本系列使用报告的第三批就已完成了内核的编译。 Makefile如下: 上图中,一定要注意内核源码的路径。 执行命令编译内核模块: make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 编译结果如图所示: 执行命令,将key_async.ko驱动模块拷贝到NFS共享目录: cp key_async.ko /nfsshare 4 编程QT测试程序 我们需要QT应用程序能够捕获系统的SIGNAL信号,但是该信号是用普通的C语言函数实现的,而QT是基于C++的面向对象的开发模式,信号拦击函数只能以界面类的静态成员函数存在。但是问题又来了,我们必须在信号处理函数中操作界面元素,而C++中静态成员函数是无法直接调用非静态成员变量的,我们采用一种全局变量保存界面实体对象的方法绕过去。 1)创建界面如图所示: 当按键按下时,相应的key复选框被勾选,按键松开时,勾选取消。keycode显示当前按键的状态码。 2)创建主窗口全局变量 3)定义异步通知静态函数及文件句柄 4)注册信号异步通知函数 5)实现信号异步通知与状态显示函数 6)编译QT测试程序 编译结果如图所示: 将qt_key_ansync测试程序拷贝到NFS共享目录 5. TL-4379上电测试 给TL-4379上电,并执行命令挂载NFS: mount -t nfs 192.168.1.103:/nfsshare /mnt-o nolock cd /mnt ls 命令执行如图所示: 执行命令,关闭TL-4379自带的图形界面: /etc/init.d/matrix-gui-2.0 stop 执行命令,加载key_async.ko驱动程序, insmod key_async.ko 执行结果如图所示: 执行命令,启动QT测试程序: ./qt_key_async -plugins tslib:/dev/input/touchscreen0 命令执行结果如图示: 按下“KEY2”,图形界面中对应的Key2复选框被选中,如图所示: 按下“KEY4”,图形界面中对应的Key4复选框被选中,如图所示: 6. 小结 本试用报告的内容有两个难点,一个是按键驱动程序的异步通知,另一个是QT如何正确拦截Linux系统信号,我们这里采用了全局变量保存Widget窗口对象,然后在系统信号处理函数中调用主窗口Widget容器内的相应复选框元素。通过对功能的测试,完全达到了无需轮询、阻塞,QT程序即可被系统高效通知,处理完通知消息后,QT程序又可继续执行其他任务,大大提高了系统工作效率。
评分
|
||
相关推荐
8 个讨论
|
||
挺复杂的,楼主应该是Linux的大牛吧。
|
|
|
|
|
|
你正在撰写讨论
如果你是对讨论或其他讨论精选点评或询问,请使用“评论”功能。
迅为RK3568开发板篇OpenHarmony配置HDF驱动控制LED-配置创建私有配置文件
1270 浏览 0 评论
飞凌嵌入式ElfBoard ELF 1板卡-初识设备树之Makefile修改
1365 浏览 0 评论
飞凌嵌入式-ELFBOARD-ELF 2硬件知识分享之Debug
1057 浏览 1 评论
飞凌嵌入式ElfBoard ELF 1板卡-烧录流程介绍之单独更新内核
2680 浏览 1 评论
飞凌嵌入式ElfBoard ELF 1板卡-TF卡烧录流程之烧写过程
1106 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-1-13 23:31 , Processed in 0.854798 second(s), Total 83, Slave 66 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (威廉希尔官方网站 图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号