【QT5】编译移植与开发环境搭建

参考文章:

https://blog.csdn.net/vickycheung3/article/details/82182136

https://blog.csdn.net/lizuobin2/article/details/52673494

前面移植了LittlevGL到嵌入式ARM系统,想到LittlevGL毕竟是一个小型的图形库,无法完成复杂的应用,同时之前还没有接触过QT应用开发,这里想试一下QT。QT号称是一次编写处处编译,叼叼哒,但是相比较java(C#)的一次编译处处运行而言,似乎也没有那么闪亮,不过就运行速度而言,原生指令集的QT程序肯定比在虚拟机上运行的java和C#程序要快。

QT有一套比较特殊的机制(信号与槽机制),其核心原理参考文章:https://blog.51cto.com/devbean/355100https://blog.csdn.net/hyongilfmmm/article/details/83015045

QT使用qmake进行工程的构建和管理,也可以使用cmake或者Qbs进行工程构建,不过好像qmake更加好用,而且qmake不仅仅只能针对QT应用程序,其他工程也能使用qmake进行构建,qmake的man介绍为“qmake - cross-platform makefile generator for Qt”,qmake的语法这里不做介绍,参考文章:https://blog.51cto.com/9291927/2112865

下面是QT移植(不论是X86/64还是ARM平台)的主要步骤(主要说原理,很多细节可能没有):

1、下载QT源码并解压,我使用的是qt-everywhere-opensource-src-5.4.1版本的源码。

2、在源码文件夹中创建一个脚本文件build,写入下面的内容:

./configure -prefix /opt/qt5.4.1 -confirm-license -opensource -release -make libs -xplatform linux-arm-gnueabi-g++ -optimized-qmake -pch -qt-sql-sqlite -qt-libjpeg -qt-libpng -qt-zlib -tslib -no-opengl -no-sse2 -no-openssl -no-nis -no-cups -no-glib -no-dbus -no-xcb -no-xcursor -no-xfixes -no-xrandr -no-xrender -no-separate-debug-info -make examples -nomake tools -nomake tests -no-iconv

这个文件就是为了方便配置QT创建的,调用源码中的configure脚本来配置QT,其中-no-xxx表示配置qt不支持xxx, 而-qt-xxx表示qt支持xxx,没有指定的保持默认值。

3、上一步配置QT的时候有一个选项 -xplatform linux-arm-gnueabi-g++ ,表示生成的目标文件的平台是arm-linux,当然也可以使用其它平台。不过这个配置选项没有实际意义,它只是一个路径前缀而已,我们可以在源码目录下的 qtbase/mkspec/ 文件夹下找到 linux-arm-gnueabi-g++ 文件夹,并修改其中的 qmake.conf 文件,下面是我参考其它资料修改的内容(带注释,关于tslib的移植这里就不介绍了,原理上很简单):

#
# qmake configuration for building with arm-linux-gnueabi-g++
#

MAKEFILE_GENERATOR      = UNIX
CONFIG                 += incremental
QMAKE_INCREMENTAL_STYLE = sublib

QT_QPA_DEFAULT_PLATFORM = linuxfb    #使用Linux的frame buffer作为显示接口
QMAKE_CFLAGS_RELEASE	+= -O2 -march=armv7-a    #设置编译优化级别以及CPU类型
QMAKE_CXXFLAGS_RELEASE	+= -O2 -march=armv7-a

include(../common/linux.conf)
include(../common/gcc-base-unix.conf)
include(../common/g++-unix.conf)

QMAKE_INCDIR += /opt/tslib/include    #设置tslib的头文件路径
QMAKE_LIBDIR += /opt/tslib/lib    #设置tslib的库文件路劲

# modifications to g++.conf
QMAKE_CC                = arm-linux-gnueabi-gcc -lts    #设置gcc编译器,lts表示在链接时链接tslib库
QMAKE_CXX               = arm-linux-gnueabi-g++ -lts
QMAKE_LINK              = arm-linux-gnueabi-g++ -lts
QMAKE_LINK_SHLIB        = arm-linux-gnueabi-g++ -lts

# modifications to linux.conf
QMAKE_AR                = arm-linux-gnueabi-ar cqs
QMAKE_OBJCOPY           = arm-linux-gnueabi-objcopy
QMAKE_NM                = arm-linux-gnueabi-nm -P
QMAKE_STRIP             = arm-linux-gnueabi-strip
load(qt_config)

4、现在已经配置好QT的源码了,准备编译:

export –n QMAKESPEC    #-n 删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。
./build

5、经过上一步的build操作生成了一个Makefile文件,直接 make 编译时间会很长,为了加快编译速度:make -j4,top指令查看了一下CPU状态,笔记本CPU的4个核满满的在运行。

6、编译完成之后(如果没有错误),可以执行make install,记得脚本文件build中有一个 -prefix /opt/qt5.4.1 ,安装路径就是/opt/qt5.4.1,完成之后本机的opt文件夹下多了一个qt5.4.1文件夹,这就是我们要的结果。看看里面有什么:

其中bin、lib、plugin文件夹是最重要的,lib和plugin中的是QT程序运行所依赖的库文件,bin文件夹中的文件则是在编写QT应用程序的时候会用到的工具,例如 qmake 工具。所以我们需要将安装好的qt5.4.1文件夹拷贝到QT运行的目标平台的 /opt 文件夹中去(只需要lib和plugin文件夹即可,example文件夹中的例程也可以拷贝过去进行测试使用)。到此,QT的运行环境已经搭建好了。

6、假设现在tslib已经安装好了,并且路径是我们在 qmake.conf 文件中配置的 /opt/tslib/lib 和 /opt/tslib/include。创建一个脚本文件qtenv.sh,用于配置QT和tslib的环境变量,使用点命令(source)运行:

export T_ROOT=/opt/tslib
export LD_LIBRARY_PATH=$T_ROOT/lib:$LD_LIBRARY_PATH
export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
export TSLIB_TSDEVICE=/dev/input/event3
export TSLIB_PLUGINDIR=$T_ROOT/lib/ts
export TSLIB_CONFFILE=$T_ROOT/etc/ts.conf
export POINTERCAL_FILE=/etc/pointercal
export TSLIB_CALIBFILE=/etc/pointercal

export QTEDIR=/opt/qt5.4.1
export LD_LIBRARY_PATH=$QTEDIR/lib:$LD_LIBRARY_PATH
export QT_QPA_GENERIC_PLUGINS=tslib
export QT_QPA_FONTDIR=$QTEDIR/lib/fonts
export QT_QPA_PLATFORM_PLUGIN_PATH=$QTEDIR/plugins 
export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0:size=800x480:mmSize=480x272:offset=0x0:tty=/dev/ttyS0
export QT_QPA_FB_TSLIB=1

7、进入到 /opt/qt5.4.1/examples/touch/pinchzoom/ 文件夹中,执行ldd查看一下文件的动态链接库的依赖:

[root@dragonboard pinchzoom]# ldd pinchzoom
	libts-1.0.so.0 => /opt/tslib/lib/libts-1.0.so.0 (0xb6fcc000)
	libQt5Widgets.so.5 => /opt/qt5.4.1/lib/libQt5Widgets.so.5 (0xb6bec000)
	libQt5Gui.so.5 => /opt/qt5.4.1/lib/libQt5Gui.so.5 (0xb68f4000)
	libQt5Core.so.5 => /opt/qt5.4.1/lib/libQt5Core.so.5 (0xb64cd000)
	libpthread.so.0 => /lib/libpthread.so.0 (0xb64ad000)
	libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb63d6000)
	libm.so.6 => /lib/libm.so.6 (0xb6331000)
	libgcc_s.so.1 => /lib/libgcc_s.so.1 (0xb631e000)
	libc.so.6 => /lib/libc.so.6 (0xb61d7000)
	libdl.so.2 => /lib/libdl.so.2 (0xb61ca000)
	librt.so.1 => /lib/librt.so.1 (0xb61ba000)
	/lib/ld-linux.so.3 (0xb6fd6000)

依赖路径都正确,运行pinchzoom:

运行没有问题,但是触摸没有反应并报下面的问题。测试了很多应用都没有用,然后又试了tslib自带的工具 ts_test ,发现会也会出现下面的问题:

tslib: Selected device is not a touchscreen (must support ABS_X and ABS_Y events)

意思是我选的这个设备不支持ABS_X和ABS_Y,参考文章:https://blog.csdn.net/a694543965/article/details/79935086里面关于ABS的说明。之前使用 ts_calibrate、ts_test 等工具都是能够正常使用的,但是我当前使用的这个tslib是我使用tslib源码编译出来的,就出现这个问题了,硬件和驱动程序都没有改动,说明可能是tslib软件版本兼容性的问题,因为使用我之前移植LittlevGL的Demo程序的时候是可以正常使用触摸运行的。先测试一下使用鼠标,将上面的环境变量配置文件qtenv.sh中的QT_QPA_GENERIC_PLUGINS改成evdevmouse:/dev/input/event6,这里event6是通过查看 cat /proc/bus/input/devices 看出来的,Handlers指向的是mouse0和event6:

I: Bus=0003 Vendor=1532 Product=001c Version=0111
N: Name="Razer  Razer Abyssus"
P: Phys=usb-sunxi-ehci-1.2/input0
S: Sysfs=/devices/platform/sunxi-ehci.1/usb1/1-1/1-1.2/1-1.2:1.0/input/input6
U: Uniq=
H: Handlers=mouse0 event6 
B: PROP=0
B: EV=17
B: KEY=7f0000 0 0 0 0 0 0 0 0
B: REL=103
B: MSC=10

运行 /opt/qt5.4.1/examples/widgets/mainwindows/mainwindow/mainwindow 应用,可以使用正常使用鼠标,但是流畅性好差劲:

至于tslib的问题,后面再说,先看看怎么编写自己的QT程序。编写一个能够在上面搭建的目标板上运行的QT应用主要有下面几个步骤:

1、http://download.qt.io/official_releases/qtcreator/4.9/4.9.2/下载qt-creator-opensource-linux-x86_64-4.9.2.run文件。

2、执行qt-creator-opensource-linux-x86_64-4.9.2.run安装Qt Creator,Qt Creator可运行文件在: <安装目录>/bin/qtcreator。

3、打开Qt Creator,进入Tools->Options->Kits,我们需要创建一个新的 Kit(工具箱),这其实就是创建一个针对目标板的编译调试环境,不过创建 Kit 之前,我们先把 Kit 里面的工具都准备好。

4、Tools->Options->Kits->Qt Versions,手动添加一个Qt Version,位置就是我们在QT源码中make install 的时候生成目标路径,这里就是 /opt/qt5.4.1/bin/qmake ,新建的Qt Version名为Qt 5.4.1(自动命名),添加完这个qmake之后相当于配置了一个版本的QT系统。

5、Tools->Options->Kits->Comilers,手动添加一个GCC编译器,包括C和C++编译器,并重命名,这里使用我们编译QT和tslib源码的时候使用的交叉编译器的路径即可。

6、Tools->Options->Kits->Debuggers,手动添加上诉交叉编译器的gdb,并重命名。

7、其它的选项卡中的配置,还没有测试,现在进入Tools->Options->Kits->Kits,新建一个 Kit 并重命名为AllwinnerA33,然后将其中的Qt Version、Comiler、Debugger等设置为上面新建的工具的名字。

8、新建Qt Widget Application命名为qttest,选择Kits为我们创建的AllwinnerA33,其它保持默认,然后在mainwindow.ui界面文件中拖拽一个Push Button,右击按键选择Go to slot,双击 clicked() 自动跳入事件处理函数,添加下面内容:

void MainWindow::on_pushButton_clicked()
{
    QMessageBox::information(NULL, "message", "qt test",
                             QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
}

功能就是按下按键的时候弹出一个消息框,点击Qt Creator最左下角的小锤子Build Project,然后在创建工程的会生成一个build-qttest-AllwinnerA33-Debug文件夹,里面有一个可执行文件qttest ,在开发板上执行这个文件即可:

好复古的界面,和WIN98既视感。

下面来解决一下tslib的问题。前面说过出现了如下问题:

tslib: Selected device is not a touchscreen (must support ABS_X and ABS_Y events)

找到自己编译的tslib源码目录并执行命令:grep "Selected device is not a touchscreen" -rnsw ,结果如下:

root@tq:/home/tangquan/baidunetdiskdownload/02_qt_sdk/tslib-src# grep "Selected device is not a touchscreen" -rnsw
Binary file plugins/.libs/input-raw.o matches
Binary file plugins/.libs/input.so matches
Binary file plugins/.libs/input.soT matches
plugins/input-raw.c:94:		fprintf(stderr, "tslib: Selected device is not a touchscreen (must support ABS and KEY event types)\n");
plugins/input-raw.c:101:		fprintf(stderr, "tslib: Selected device is not a touchscreen (must support ABS_X and ABS_Y events)\n");
plugins/input-raw.c:116:			fprintf(stderr, "tslib: Selected device is not a touchscreen (must support BTN_TOUCH or BTN_LEFT events)\n");

查看plugin/input-raw.c:101行内容:

static int check_fd(struct tslib_input *i)
{
	struct tsdev *ts = i->module.dev;
	int version;
	long evbit[BITS_TO_LONGS(EV_CNT)];
	long absbit[BITS_TO_LONGS(ABS_CNT)];
	long keybit[BITS_TO_LONGS(KEY_CNT)];

	if (ioctl(ts->fd, EVIOCGVERSION, &version) < 0) {
		fprintf(stderr, "tslib: Selected device is not a Linux input event device\n");
		return -1;
	}

	if (version < EV_VERSION) {
		fprintf(stderr, "tslib: Selected device uses a different version of the event protocol than tslib was compiled for\n");
		return -1;
	}

	if ( (ioctl(ts->fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) ||
		!(evbit[BIT_WORD(EV_ABS)] & BIT_MASK(EV_ABS)) ||
		!(evbit[BIT_WORD(EV_KEY)] & BIT_MASK(EV_KEY)) ) {
		fprintf(stderr, "tslib: Selected device is not a touchscreen (must support ABS and KEY event types)\n");
		return -1;
	}

	if ((ioctl(ts->fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit)) < 0 ||
		!(absbit[BIT_WORD(ABS_X)] & BIT_MASK(ABS_X)) ||
		!(absbit[BIT_WORD(ABS_Y)] & BIT_MASK(ABS_Y))) {
		fprintf(stderr, "tslib: Selected device is not a touchscreen (must support ABS_X and ABS_Y events)\n");
		return -1;
	}

......

发现该版本的tslib程序中检查了设备的ABS_X和ABS_Y位,如果这两位没有置位,说明该设备不支持该功能。我的板子带的触摸屏的信息为:

I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="ft5x_ts"
P: Phys=
S: Sysfs=/devices/virtual/input/input4
U: Uniq=
H: Handlers=ddrfreq_dsm event4 
B: PROP=0
B: EV=b
B: KEY=400 0 0 0 0 0 0 0 0 0 0
B: ABS=2650000 0

ABS_X、ABS_Y的掩码定义在 input-event-codes.h 文件中:

#define ABS_X			0x00
#define ABS_Y			0x01
#define ABS_Z			0x02
#define ABS_RX			0x03
#define ABS_RY			0x04
#define ABS_RZ			0x05
#define ABS_THROTTLE		0x06
#define ABS_RUDDER		0x07
#define ABS_WHEEL		0x08
#define ABS_GAS			0x09
#define ABS_BRAKE		0x0a
#define ABS_HAT0X		0x10
#define ABS_HAT0Y		0x11
#define ABS_HAT1X		0x12
#define ABS_HAT1Y		0x13
#define ABS_HAT2X		0x14
#define ABS_HAT2Y		0x15
#define ABS_HAT3X		0x16
#define ABS_HAT3Y		0x17
#define ABS_PRESSURE		0x18
#define ABS_DISTANCE		0x19
#define ABS_TILT_X		0x1a
#define ABS_TILT_Y		0x1b
#define ABS_TOOL_WIDTH		0x1c

#define ABS_VOLUME		0x20

#define ABS_MISC		0x28

#define ABS_MT_SLOT		0x2f	/* MT slot being modified */
#define ABS_MT_TOUCH_MAJOR	0x30	/* Major axis of touching ellipse */
#define ABS_MT_TOUCH_MINOR	0x31	/* Minor axis (omit if circular) */
#define ABS_MT_WIDTH_MAJOR	0x32	/* Major axis of approaching ellipse */
#define ABS_MT_WIDTH_MINOR	0x33	/* Minor axis (omit if circular) */
#define ABS_MT_ORIENTATION	0x34	/* Ellipse orientation */
#define ABS_MT_POSITION_X	0x35	/* Center X touch position */
#define ABS_MT_POSITION_Y	0x36	/* Center Y touch position */
#define ABS_MT_TOOL_TYPE	0x37	/* Type of touching device */
#define ABS_MT_BLOB_ID		0x38	/* Group a set of packets as a blob */
#define ABS_MT_TRACKING_ID	0x39	/* Unique ID of initiated contact */
#define ABS_MT_PRESSURE		0x3a	/* Pressure on contact area */
#define ABS_MT_DISTANCE		0x3b	/* Contact hover distance */
#define ABS_MT_TOOL_X		0x3c	/* Center X tool position */
#define ABS_MT_TOOL_Y		0x3d	/* Center Y tool position */

显然我的触摸屏设置是不支持ABS_X、ABS_Y的,但是支持ABS_HAT0X、ABS_HAT0Y以及ABS_DISTANCE,然后我的LittlevGL移植的测试程序里面读取的时候是读取到了ABS_MT_POSITION_X和ABS_MT_POSITION_Y数据,有点混乱了。算了,把之前的旧版本(或者说是更新版本)的tslib库移植回来试试(原来版本的ts.conf文件在/etc目录下,现在放在tslib中的etc文件夹),按道理这个版本的的源码也有判断设备是否支持该设备的部分,但是这里只有动态库文件,没有源码,不过库文件里面照样会存放字符串常量的,不妨 grep 查找一下试试,发现在 lib/ts/input.so 中有搜索到,查看lib/ts/input.so并搜索:

tslib: Selected device is not a touchscreen (must support ABS_MT_POSITION_X and ABS_MT_POSITION_Y events)

嗯哼,这个库中判断的是设备是否支持ABS_MT_POSITION_X和ABS_MT_POSITION_Y,而不是ABS_X和ABS_Y,说明前后两个库的源码是有区别的。那我们我们可以使用这个库试试了,修改 qtenv.sh 脚本并点命令执行修改环境变量:

export T_ROOT=/mnt/tslib
export LD_LIBRARY_PATH=$T_ROOT/lib:$LD_LIBRARY_PATH
export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
export TSLIB_TSDEVICE=/dev/input/event4
export TSLIB_PLUGINDIR=$T_ROOT/lib/ts
export TSLIB_CONFFILE=$T_ROOT/etc/ts.conf
export POINTERCAL_FILE=/etc/pointercal
export TSLIB_CALIBFILE=/etc/pointercal

......

运行 ts_test 工具进行测试:

原先版本的tslib是可以识别我的触摸屏设备的,再修改qtenv.sh 脚本设置输入设备为tslib,测试qt例程,没有问题,可以正常使用触摸。关于tslib的差异问题,我去tslib的官方github工程:https://github.com/libts/tslib,看了看最新的tslib源码,其中 plugins/input-raw.c 有做修改:

	if ((ioctl(ts->fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit)) < 0 ||
	    !(absbit[BIT_WORD(ABS_X)] & BIT_MASK(ABS_X)) ||
	    !(absbit[BIT_WORD(ABS_Y)] & BIT_MASK(ABS_Y))) {
		if (!(absbit[BIT_WORD(ABS_MT_POSITION_X)] & BIT_MASK(ABS_MT_POSITION_X)) ||
		    !(absbit[BIT_WORD(ABS_MT_POSITION_Y)] & BIT_MASK(ABS_MT_POSITION_Y))) {
			fprintf(stderr,
				"tslib: Selected device is not a touchscreen (must support ABS_X/Y or ABS_MT_POSITION_X/Y events)\n");
			return -1;
		}
	}

这里判断的是设备是否支持ABS_MT_POSITION_X和ABS_MT_POSITION_Y或者ABS_X和ABS_Y,只要支持一样就可以啦,移植最新的tslib吧还是。

猜你喜欢

转载自blog.csdn.net/tq384998430/article/details/98485556