Qt/Embedded在嵌入式Linux系统下的移植与应用
1 Qt/Embedded开发环境介绍
Qt/Embedded应用程序的开发可以在安装了一个跨平台开发工具链的不同的平台上编译。系统采用的是在Linux平台下开发,在Linux平台下以虚拟缓冲帧的方式来运行,其实是有一个X11的应用程序虚拟了一个缓冲帧。通过指定显示设备的宽度、高度和颜色深度,虚拟出来的缓冲帧将和物理的显示设备在每个像素上保持一致。这样每次调试应用时开发人员就不用总是刷新嵌入式设备的FLASH 存储空间,从而加速了应用的编译、链接和运行周期。
运行Qt 的虚拟缓冲帧工具的方法是:在Linux 的图形模式下运行命令:
qvfb(回车)
当Qt 嵌入式的应用程序要把显示结果输出到虚拟缓冲帧时,我们在命令行运行这个程序时,在程序名后加上-qws的选项。例如:$>hello-qws
Qt包含了许多支持嵌入式系统开发的工具,其中一些工具我们会在别的地方介绍。最实用的工具(除了上面我们提到的虚拟缓冲帧)有 tmake、Qt designer(图形设计器)、uic和moc。
(1)tmake
tmake 是一个为编译Qt/Embedded 库和应用而提供的Makefile 生成器。它能够根据一个工程文件(.pro)产生不同平台下的Makefile 文件。
开发者可以使用Qt 图形设计器可视化地设计对话框而不需编写一行代码。使用Qt图形设计器的布局管理可以生成具有平滑改变尺寸的对话框,tmake 和Qt 图形设计器是完全集成在一起的。
(2)Qt designer
Qt Designer是设计窗口组件(Widget)的应用程序,在安装Qt的bin目录下输入./designer命令,就启动一个包含很多Qt组件的可视化界面。在此组织应用程序的各组建分布很方便,最后生成一个file.ui和main.cpp文件;file.ui是用XML语言编写的一个文本。
(3)uic(user interface compiler)
uic是从XML文件生成代码的用户界面编译器,用来将file.ui文件生成file.h和file.cpp文件(命令如:uic -o file.h file.ui uic -o file.cpp file.ui),但生成的这两个文件不是标准的纯C++代码,通常称为Qt的C++扩展,因为Qt的对象间运用了信号和插槽的通信机制,在文件中用Q_OBJECT宏来标识。
(4)moc(元对象编译器)
moc用来解析一个C++文件中的类声明并且生成初始化对象的C++代码,moc在读取C++源文件,如果发现其中一个或多个类的声明中含有Q_OBJECT宏,就给出这个使用Q_OBJECT宏的类生成另外一个包含元对象代码的C++元文件;元对象代码对信号插槽机制、运行时的类型信息和动态属性系统是需要的。
2 Qt/Embedded库的移植
2.1 Qt/Embedded开发环境的安装[1]
一般来说,嵌入式应用程序都是先在装有Linux操作系统的PC机或工作站来完成Qt/Embedded的开发,然后再把应用程序交叉编译后发布到嵌入式Linux系统下。在一台装有Linux操作系统的机器上建立Qt/Embedded开发环境,需要三个软件安装包:tmake工具安装包,Qt/Embedded安装包,Qt的X11版安装包。
由于上述软件安装包有许多不同版本,要注意版本不同可能导致的冲突。Qt for X11安装包的版本要比Qt/Embedded的版本旧。因为Qt for X11安装包的两个工具uic和designer产生的源文件会和Qt/Embedded的库一起被编译链接,本着“向前兼容”的原则,Qt for X11的版本应比Qt/Embedded的版本旧。本系统采用的是tmake 1.11;Qt/Embedded 2.3.7;Qt 2.3.2 for X11。
2.1.1安装tmake
在Linux命令模式下运行以下命令:
#tar xfz tmake-1.11.tar.gz
#export TMAKEDIR=$PWD/tmake-1.11
#export TMAKEPATH=$TMAKEDIR/lib/qws/linux-x86-g++
#export PATH=$TMAKEDIR/bin:$PATH
2.1.2安装Qt/Embedded 2.3.7
在Linux命令模式下运行以下命令:
#tar xfz qt-embedded-2.3.7.tar.gz
#cd qt-2.3.7
#export QTDIR=$PWD
#export QTEDIR=$QTDIR
#export PATH=$QTDIR/bin:$PATH
#export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
#./configure -qconfig -qvfb -depths 4,8,16,32
#make sub-src
上述命令./configure-qconfig-qvfb-depths 4,8,16,32指定Qt嵌入式开发包生成虚拟缓冲帧工具qvfb,并支持4,8,16,32位的显示颜色深度。另外我们也可以在configure的参数中添加-system-jpeg和gif,使Qt/Embedded平台能够支持jpeg、gif格式的图形。
2.1.3安装Qt/X11 2.3.2
在Linux命令模式下运行以下命令:
#tar xfz qt-x11-2.3.2.tar.gz
#cd qt-2.3.2
#export QTDIR=$PWD
#export PATH=$QTDIR/bin:$PATH
#export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
#./configure -no-opengl
#make
#make -C tools/qvfb
#make tools/qvfb/qvfb bin
#cp bin/uic $QTEDIR/bin
根据开发者本身的开发环境,可以在configure的参数中添加别的参数,比如,-no-opengl或-no-xfs,可以通过输入./configure-help来获得一些帮助信息。
2.2交叉编译Qt/Embedded的库[2-3]
Qt/Embedded应用程序最终是要在嵌入式Linux系统下运行,所以要把Qt嵌入式应用程序编译成支持在开发板上运行的目标代码之前,需要两样东西,一是基于目标板的交叉编译器,另一个是交叉编译后的Qt/Embedded库。
2.2.1安装交叉编译工具
交叉编译是指一个处理器平台上编译产生一个工程代码的另一个处理器的目标代码。通过下载并修改toolchain作为交叉编译工具,要求最好使用cross-3.3.2及其以后的版本,这样才能对Qt/Embedded有良好支持。
2.2.2交叉编译Qt/Embedded库
当有了ARM的linux编译器后,就可以使用这个编译器进行交叉编译Qt/Embedded库的源代码,从而产生一个以ARM为目标代码的Qt/Embedded库。具体过程如下:
(1)解压Qt/Embedded
在Linux命令模式下运行如下命令:
#tar xfz qt-embedded-2.3.7.tar.gz
(2)配置Qt/Embedded的安装
#cd qt-2.3.7
#export QTDIR=$PWD
#export QTEDIR=$QTDIR
#.cp /配置文件所在路径/qconfig-local.h ./src/tools
#make clean
#./configure -xplatform linux-arm-g++ -shared -debug
-qconfig local -qvfb -depths 4,8,16,32
#make
Qt/Embedded的安装选项有很多个,可以直接输入“”来运行配置。Qt/Embedded的安装选项还允许自己定制一个配置文件,来有选择的编译Qt/Embedded库,这个安装选项是“-qconfig local”;当指定这个选项时,Qt/Embedded库中安装过程会寻找qt-2.3.7/src/tools/qconfig-local.h这个文件来编译链接Qt/Embedded库。在定制中添加了对触摸屏显示的支持。
2.3 Qt/Embedded支持触摸屏显示[4][5]
Qt/Embedded 2.x系列中,触摸屏设备和键盘设备需要根据具体的驱动程序接口中Qt/Embedded中设备实现对应的设备操作类。其中对应于鼠标类设备的实现位于src/kernel/qwsmouse_qws.cpp中。
在文件qwsmouse_qws.cpp中添加对触摸屏的支持。具体修改如下:
(1)定义和Linux内核文件driver/input/tsdev.c中数据结构ts_event相一致的TS_EVENT数据结构,定义如下:
#if defined(QT_QWS_IPAQ)
typedef struct {
short pressure;
short x;
short y;
short millisecs;
} TS_EVENT;
(2)修改校准文件的位置
在函数void QCalibratedMouseHandler::writeCalibration()和
void QCalibratedMouseHandler::readCalibration()中修改如下:
Qstring calFile = “/tmp/pointercal”;
(3)对打开的设备文件进行修改
在函数QTPanelHandlerPrivate::QTPanelHandlerPrivate中,修改如下:
mouseFD=open(“/dev/input/ts0”,O_RDONLY | O_NDELAY);
(4)由于内核TS_EVENT结构中,当触摸屏按下时对pressure的设置为1,因此在void QTPanelHandlerPrivate::readMouseData()函数中把
if(data->pressure>=QT_QWS_TP_PRESSURE_THRESHOLD)
修改为:
if(data->pressure)
2.4移植Qt/Embedded库
在ARM板的根文件系统的tmp目录下创建新目录qt,在qt目录下创建新目录lib。把交叉编译后生成的Qt/Embedded库拷贝到ARM板上的根文件系统下的/tmp/qt/lib目录下。在使用QT库之前要设置环境变量。命令如下:
#export QTDIR=/tmp/qt
#export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
这样Qt/Embedded应用程序就可以在ARM板上移植了。
3 Qt/Embedded应用程序的开发
开发一个Qt应用程序的流程如下[6]:
3.1生成一个工程文件(.pro文件)
一个应用通常对应一个工程文件,生成一个工程文件,并对它做一些简单的编辑,然后使用一个专门的工具(例如tmake)处理这个工程文件,就可以生成一个Makefile文件。
产生一个工程文件的其中一个方法是使用progen命令(progen程序可以在tmake的安装路径下找到)。下面是使用progen产生一个名为hello的工程文件的命令:
progen -t app.t -o hello.pro
产生的hello.pro工程文件并不完整,开发者还需手动往里添加工程所包含的头文件,源文件等信息。
3.2新建一个窗体
在qt-2.3.2的安装路径的bin目录下运行“./designer”命令,就启动了一个Qt图形编辑器。点击编辑器的“new”菜单,弹出了一个“new Form”对话框,在这个对话框里我们选择“Widget”,然后点击“OK”按钮,这样我们就新建了一个窗体。接着,我们可以对这个窗体进行设置。
设置完成后,将其保存为hello.ui文件,这个文件就是project窗体的界面存储文件。
3.3生成窗体类的头文件和实现文件
界面文件使用uic工具产生出窗体类的头文件和实现文件,例如hello.ui节目文件产生hello窗体类的头文件和实现文件,具体方法如下:
#cd qt-2.3.7/bin
#uic -o hello.h hello.ui
#uic -o hello.cpp -impl hello.h hello.ui
这样我们就得到hello窗体类的头文件hello.h和实现文件hello.cpp。接下来根据我们要实现的具体功能,在hello.cpp文件里添加相应的代码。
3.4编写主函数main()
一个Qt/Embedded应用程序应该包含一个主函数,主函数所在的文件名是main.cpp。主函数是应用程序执行的入口点。
3.5编辑工程文件
在工程文件中添加窗体类的头文件、实现文件和主函数文件。
3.6生成Makefile文件
编译器是根据Makefile文件内容来进行编译的,所以需要生成Makefile文件。Qt提供的tmake工具可以帮助我们从一个工程文件(.pro文件)中产生Makefile文件。从工程文件生成一个Makefile文件的具体做法如下:
把环境变量$TMAKEPATH所指的目录设置为arm编译器的配置目录,把当前QTDIR环境变量指向Qt/Embedded的安装路径,这样就可以使用tmake来生成Makefile文件。
#export TMAKEPATH=/tmake安装路径/qws/linux-arm-g++
#export QTDIR=…/qt-2.3.7
#tmake -o Makefile hello.pro
当前目录生成的Makefile文件,需要进行一些修改,具体修改如下:
将LINK = arm-linux-gcc 改为LINK = arm-linux-g++
3.7编译链接整个工程
在命令行下输入make命令对整个工程进行编译链接。
#make
这样make生成的二进制文件就可以在ARM上允许了。
4 实验结果
实现了简单的qt应用程序,主要有按钮和标签,点击按钮display就会在标签上显示“Hello World!”,点击按钮clear就会清空标签。应用程序首先在pc上调试通过后,再重新交叉编译后移植到ARM板上。
下图是该qt应用程序在pc上运行的结果。
下图是该应用程序在嵌入式ARM板上运行后在LCD上显示的结果。
结束语:对Qt/Embedded提供的开发工具进行分析,同时叙述了Qt/Embedded在pc上的安装和在嵌入式ARM板上的移植。同时通过一个简单的应用程序对Qt/Embedded应用程序的开发流程进行了详细介绍。