linux. qt信号崩溃,【创龙AM4379 Cortex-A9试用体验】之I/O中断异步通知驱动程序+QT捕获Linux系统信号+测试信号通知...

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 KEY1\n");

}

ret= gpio_request_one(pins_desc[1].pin, GPIOF_IN, "KEY2 IRQ"); /* 申请 IO ,为输入*/

if(ret < 0) {

printk(KERN_ERR"Failed to request GPIO for KEY2\n");

}

ret= gpio_request_one(pins_desc[2].pin, GPIOF_IN, "KEY3 IRQ"); /* 申请 IO ,为输入*/

if(ret < 0) {

printk(KERN_ERR"Failed to request GPIO for KEY3\n");

}

ret= gpio_request_one(pins_desc[3].pin, GPIOF_IN, "KEY4 IRQ"); /* 申请 IO ,为输入*/

if(ret < 0) {

printk(KERN_ERR"Failed to request GPIO for KEY4\n");

}

/*设置 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 KEY1\n");

}

if(request_irq(KEY2_IRQ,keys_irq, IRQ_TYPE_EDGE_BOTH, "K2", &pins_desc[1]))

{

printk(KERN_ERR"Failed to request IRQ for KEY2\n");

}

if(request_irq(KEY3_IRQ,keys_irq, IRQ_TYPE_EDGE_BOTH, "K3", &pins_desc[2]))

{

printk(KERN_ERR"Failed to request IRQ for KEY3\n");

}

if(request_irq(KEY4_IRQ,keys_irq, IRQ_TYPE_EDGE_BOTH, "K4", &pins_desc[3]))

{

printk(KERN_ERR"Failed to request IRQ for KEY4\n");

}

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_fasync\n");

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 error\n");

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如下:

993d5be4b93fad92a5cc6b25a09e80d9.gif

上图中,一定要注意内核源码的路径。

执行命令编译内核模块:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

编译结果如图所示:

993d5be4b93fad92a5cc6b25a09e80d9.gif

执行命令,将key_async.ko驱动模块拷贝到NFS共享目录:

cp key_async.ko /nfsshare

4编程QT测试程序

我们需要QT应用程序能够捕获系统的SIGNAL信号,但是该信号是用普通的C语言函数实现的,而QT是基于C++的面向对象的开发模式,信号拦击函数只能以界面类的静态成员函数存在。但是问题又来了,我们必须在信号处理函数中操作界面元素,而C++中静态成员函数是无法直接调用非静态成员变量的,我们采用一种全局变量保存界面实体对象的方法绕过去。

1)创建界面如图所示:

993d5be4b93fad92a5cc6b25a09e80d9.gif

当按键按下时,相应的key复选框被勾选,按键松开时,勾选取消。keycode显示当前按键的状态码。

2)创建主窗口全局变量

993d5be4b93fad92a5cc6b25a09e80d9.gif

3)定义异步通知静态函数及文件句柄

993d5be4b93fad92a5cc6b25a09e80d9.gif

4)注册信号异步通知函数

993d5be4b93fad92a5cc6b25a09e80d9.gif

5)实现信号异步通知与状态显示函数

993d5be4b93fad92a5cc6b25a09e80d9.gif

6)编译QT测试程序

编译结果如图所示:

993d5be4b93fad92a5cc6b25a09e80d9.gif

将qt_key_ansync测试程序拷贝到NFS共享目录

5. TL-4379上电测试

给TL-4379上电,并执行命令挂载NFS:

mount -t nfs 192.168.1.103:/nfsshare /mnt-o nolock

cd /mnt

ls

命令执行如图所示:

993d5be4b93fad92a5cc6b25a09e80d9.gif

执行命令,关闭TL-4379自带的图形界面:

/etc/init.d/matrix-gui-2.0 stop

执行命令,加载key_async.ko驱动程序,

insmod key_async.ko

执行结果如图所示:

993d5be4b93fad92a5cc6b25a09e80d9.gif

执行命令,启动QT测试程序:

./qt_key_async  -plugins tslib:/dev/input/touchscreen0

命令执行结果如图示:

993d5be4b93fad92a5cc6b25a09e80d9.gif

993d5be4b93fad92a5cc6b25a09e80d9.gif

按下“KEY2”,图形界面中对应的Key2复选框被选中,如图所示:

993d5be4b93fad92a5cc6b25a09e80d9.gif

按下“KEY4”,图形界面中对应的Key4复选框被选中,如图所示:

993d5be4b93fad92a5cc6b25a09e80d9.gif

6.小结

本试用报告的内容有两个难点,一个是按键驱动程序的异步通知,另一个是QT如何正确拦截Linux系统信号,我们这里采用了全局变量保存Widget窗口对象,然后在系统信号处理函数中调用主窗口Widget容器内的相应复选框元素。通过对功能的测试,完全达到了无需轮询、阻塞,QT程序即可被系统高效通知,处理完通知消息后,QT程序又可继续执行其他任务,大大提高了系统工作效率。

699ba7046c51816a17b33a7caa85f179.png

1

48ae8843d4dfd60df807111f01dff977.gif

下载积分:

积分 -1 分

111.81 KB

, 下载次数: 39

, 下载积分:

积分 -1 分

评分

积分 +10

收起

理由

+ 10

您的帖子很精彩,期待您分享的下一个帖子!.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值