jerrywu75的笔记 https://bbs.21ic.com/?62511 [收藏] [复制] [RSS]

日志

嵌入式linux新手入门手记-解决QT应用程序不响应触摸屏的问题2 ...

已有 1673 次阅读2015-8-7 11:18 |个人分类:嵌入式开发|系统分类:嵌入式系统| qt应用程序, 触摸屏, linux, 嵌入式

  在前面的几篇手记中,已经完成了基本工作平台的搭建。Qt的应用程序已经能够在am335xlinux3.2.0平台上运行,点击触摸屏也能够进行操作了。但是现在有一个小的bug,在运行qt应用程序前如果USB接口的触摸屏断开,或者运行中触摸屏断开,之后如果触摸屏设备又自动恢复,应用程序是不能自动恢复触摸屏的工作,只能退出应用程序然后再重新启动应用程序,才能继续使用触摸屏。这个bug对于工控机就是一个比较严重的问题,设备正在工作,不可能强行退出程序重新启动来恢复对触摸屏的响应,所以我们需要修改qt以及tslib的源程序,来增加自动检测触摸屏设备,并自动恢复应用程序对触摸屏的响应的功能。

  通过分析qt5.4.1的源程序发现,qt gui中初始化触摸屏事件是使用的QTsLibMouseHandler这个类,在这个类的构造函数中:

        m_dev =ts_open(device.constData(), 1);

        if (!m_dev) {

            qErrnoWarning(errno,"ts_open() failed.");

            return ;

        }

  上面的代码我们可以分析出来,启动qt应用程序时如果不能正确打开tslib触摸屏,则会错误退出并继续运行qt应用程序,但是如果正确打开了tslib设备并正确设置参数后,会注册signal-slot以及notifier

        if(fd >= 0) {

            m_notify = new QSocketNotifier(fd, QSocketNotifier::Read, this);

            connect(m_notify, SIGNAL(activated(int)), this, SLOT(readMouseData()));

        } else {

           qWarning("Cannot open mouse input device '%s': %s",device.constData(), strerror(errno));

        }

  上述代码执行成功后,corelib会通过qeventdispatcherunix.cpp中的doSelect来检查注册的notifier以及调度signal-slot。对于工控的要求,这里的doSelect有一个小小的缺陷,就是当select返回错误时,doSelect将会禁止出错的notifiersignal-slot。这样,当触摸屏设备出现错误时,doSelect将会禁止QTsLibMouseHandler注册的notifiersignal-slot,即使后续触摸屏设备自动恢复了,qt也不能正常继续使用触摸屏,必须重新启动qt程序,重新执行上述的QTsLibMouseHandler构造函数,才能够继续使用触摸屏设备。

  我做了两个大的修改,来解决这个问题:

  A、修改tslib,增加一个ts_status函数,用来提供给用户程序获取USB触摸屏的设备状态,该函数返回-1表示USB触摸屏出现问题,不能读取数据。返回0表示USB触摸屏工作正常。

    同时在tslib的插件中添加相应的读取状态的函数,顶层的ts_status是通过下一层的接口函数来获取设备信息,所以需要在每一层都增加相应的函数。

    tslib/src下添加一个ts_status.c的文件,添加ts_status函数(省事,我将这个函数增加到了ts_read.c中,没有增加ts_status.c文件,并需要在tslib.h中添加函数申明)

        int ts_status(struct tsdev *ts)

        {

            int result;

            result=ts->list->ops->stat(ts->list);

            return result;

        }

    修改ts_filter.h,增加获取状态的函数到结构中:

        struct tslib_ops {

               int (*read)(struct tslib_module_info *inf, struct ts_sample *samp, int nr);

               int (*fini)(struct tslib_module_info *inf);

               int (*stat)(struct tslib_module_info *inf); //增加获取状态的函数

        };

    修改tslib/plugins下的dejitterinput-rawlinearpthresvariance,其中input-raw与触摸屏设备有关,我这里使用linuxusb设备,所以使用这个模块,其他四个是灵敏度,滤波等模块。input-raw.c

        static int ts_input_stat(struct tslib_module_info *inf)

        {

            struct tslib_input *i = (struct tslib_input *)inf;

            int ret=check_fd(i);

            return ret;

        }

        static const struct tslib_ops __ts_input_ops = {

               .read       = ts_input_read,

               .fini = ts_input_fini,

               .stat   = ts_input_stat, //增加函数

        };

    修改dejitter.clinear.cpthres.cvariance.c,添加相应的函数。这里只列出pthres的修改,其他三个模块的修改和pthres相同:

        static int pthres_stat(struct tslib_module_info *info)

        {

               return info->next->ops->stat(info->next);

        }

        static const struct tslib_ops pthres_ops =

        {

               .read       = pthres_read,

               .fini = pthres_fini,

               .stat   = pthres_stat,

        };

  B、修改qt5.4.1的源程序QTsLibMouseHandler中添加一个守护进程用来通过ts_status不断获取USB触摸屏的状态当状态为不能读取数据时注销注册的触摸屏事件管理器并不断尝试重新打开触摸屏设备。

    如果守护进程成功的重新打开了触摸屏设备,则重新注册触摸屏事件管理器。守护进程是一个1秒钟的定时器。

    QTsLibMouseHandler类中添加一个devTsLib变量,用于保存Tslib设备的信息。修改构造函数,增加下面红色的两行代码:

        if (device.isEmpty())device = QByteArrayLiteral("/dev/input/event1");

           devTslib=device; //保存设备信息

           m_Timer=startTimer(1000); //增加一个1秒的定时器,用作守护进程

           m_dev = ts_open(device.constData(), 1);

           if (!m_dev) {

                qErrnoWarning(errno, "ts_open() failed.");

                return ;

            }

    然后实现守护进程:

        void QTsLibMouseHandler::timerEvent(QTimerEvent *event)

        {

            if(m_dev){

                if(ts_status(m_dev)==-1){

                    qDebug()<<"the tslib device was disconnect.";

                    ts_close(m_dev);

                    m_dev=0;

                    disconnect(m_notify, SIGNAL(activated(int)), this, SLOT(readMouseData()));

                    delete(m_notify);

            }

        } else {

            qDebug()<<"re-open the tslib device.";

            m_dev =ts_open(devTslib.constData(), 1);

            if(m_dev){

                ts_config(m_dev);

                int fd =ts_fd(m_dev);

                if (fd >= 0) {

                    m_notify = new QSocketNotifier(fd,QSocketNotifier::Read, this);

                    connect(m_notify, SIGNAL(activated(int)), this, SLOT(readMouseData()));

                    qDebug()<<"open the tslib device sucessful.";

                } else {

                    qWarning("Cannot open mouse input device '%s': %s", devTslib.constData(), strerror(errno));

                }

            } else {

                qDebug()<<"could not open the tslib device.";

            }

        }

    }

    先重新编译tslib,并将编译后的tslib安装到需要的位置。

    然后编译qt5.4.1gui,并将编译后的libqt5gui相关的6个文件拷贝到需要的位置。

    通过上述的修改源程序,解决了两个问题:

    1、启动qt程序前,触摸屏设备没有准备好,导致的qt程序启动后,即使触摸屏设备正常了,qt程序也不能恢复使用触摸屏的问题。

    2、qt程序启动后,能够正常使用触摸屏设备,但是由于触摸屏设备意外异常后,即使触摸屏设备自动恢复正常,qt程序也不能自动恢复使用触摸屏的问题。

 

   现在,就完成了开发平台的基础搭建工作,一共做了以下的工作:

    1、安装ubuntu14.04,并安装相应的软件,交叉编译工具,nfs和tftp服务;

    2、在ti官网下载uboot,linux kernel,并配置交叉编译环境,进行正确编译,生成MLO、u-boot.img、uImage;

    3、修改uboot,和kernel,尤其是kernel,支持定制am335x平台的各硬件;

    4、下载tslib1.6.0,并交叉编译;

    5、下载mkfs工具包,并编译生成mkfs.ubifs工具,用于制作ubi文件镜像;

    6、下载qt5.4.1,并交叉编译,生成用于开发am335x平台qt程序的开发包;

    7、修改tslib和qt5.4.1,用于支持特殊功能;

    8、编写一个qt应用程序,交叉编译并正确运行于am335x上。

  后续将进行的工作:

    1、UART1将使用为RS485通信,修改omap-serail.c,增加一个GPIO,用于控制MAX487的数据发送和接收;

    2、业余时间移植SGX Graphics_SDK_5010102到kernel3.2.0,实现图形加速和3D,准备自己开发一款3D游戏机;


(原创文章,转载请注明出处)

(未经本人同意,不得用于商业用途)


路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)