正点原子有几个版本的QT移植教程。给的资料的10.用户手册中就有2个版本的移植手册,我主要参考的5.12.9的移植说明,但移植的是5.15.5版本的QT。
QT移植前要对应版本,我发现所谓的对应版本其实就是对应ldd的版本,也就是开发板上系统对应ldd和编译主机的ldd版本。可以用下面的命令查看ldd版本:
ldd --version
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
可以看到我的ubuntu16.04主机上的ldd版本是2.23,同样的方法确定开发板上的ldd版本也需要是一样的2.23。不然就要更换主机编译环境,或开发板系统环境。由于开发板的速度有限,更换文件系统也不是那么方便,因此编译环境选择ubuntu16.04就是唯一比较合理的方案。
因为是开发板上使用QT,所以会需要下面的几个步骤才能正常使用:
第一步就是一个比较麻烦的事。有2个方案1. 使用虚拟机安装一个16.04的系统; 2. 使用docker搭建一个编译环境就可以了
其实我虚拟机和ubuntu16.04的境像都准备好了,但最后还是用的第二个方案。简单说下2个方案区别,第1个方案有个好处是可以使用qt-creator开发,在虚拟机中安装好qt-creator就可以了。第2个方案有个优点是下载东西少,占用空间小些,但是命令行,也不能在docker中安装一个qt-creator。应该也是有方法,只是可能又引入了新的问题。这里就不自找麻烦了,虽然不能用qt-creator,但还是可以用命令行编译,具体来说就是在具体项目下qmake, 然后直接make就可以了。
之前没有用过docker,所以这里还是不得不学习下docker使用,还好比较简单,就几个命令:
# 安装
sudo apt install docker docker.io
# 开启
sudo systemctl start docker
# 拉镜像
sudo docker pull ubuntu:16.04
# 建立一个容器,也就是建立一个ubuntu16.04的主机
# 这里的命令还是简单说下
# -it 是前台运行
# --name 是主机名字,后面可用它来打开容器或进入容器
# -v 将主机目录映射到容器中,相当于目录共享。要编译的源文件和编译后安装的目录都可以放在这里
# 后面2个就简单了,一个是镜像名,一个是使用的shell
sudo docker run -it --name imx_qt -v Host_Dir:Slave_Dir ubuntu:16.04 /bin/bash
# 看下有那些容器
sudo docker ps -a
# 进入容器
sudo docker attach imx_qt
平时基本就用上面几个命令就够用了,要是有其它需求就自己搜索一下。
ubuntu16.04安装好后,还要安装下基本的编译环境什么的
apt update
apt install build-essential make git cmake python
# 我的进入默认已经安装了一个版本的qmake,但后面编译出来还会有一个会和这个冲突,因此要删除
apt remove qmake-*
# 可以使用下面命令确定是否完全删除
dpkg --list|grep qt5
正点原子提供了一个arm-linux-gnueabihf的编译链工具,但我用它编译qt5.12.9的源码,编译通过了但make install的时候有问题,不知道是那里设置有问题。读者可以尝试下应当也是可以的。使用正点原子提供的编译链方法也比较简单,就下面几个步骤:
# 复制gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar到ubuntu16.04的/usr/local目录
cd /usr/local
tar xvf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar
vi ~/.bashrc
# 加入一行
export PATH=$PATH:/usr/local/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin
# 用下面命令确认下版本是不是4.9.4就OK了
arm-linux-gnueabihf-gcc --version
我因为编译出错的原因就用的ubuntu16.04的apt源中的5.4.0的版本,编译出来的程序开发板上也是可以直接运行的
apt search arm-linux-gnueabihf
# 这里的gcc和g++都要安装,不要只安装gcc,QT编译也会用到g++
apt install gobjc-5-multilib-arm-linux-gnueabihf gobjc++-5-multilib-arm-linux-gnueabihf
正点原子的教程中有tslib的移植教程,但我在使用过程中有遇到了问题。我使用的不是官方推的LCD+I2C的触摸屏。而是自己之前在淘宝买的一个hdmi+usb触摸的屏幕。正点原子默认有一个GRB转hdmi的模块,开机的时候改下使用的设备树就可以直接驱动了,但USB触摸就没有官方的教程了。下面说下我的做法给后面的人一个参考:
# 正点原子使用hdmi屏幕
# 开发板连接上电源,使用一个USB线连接开发板的USB_TTL和你的电脑
# 电脑上打开串中,115200波特率。如果是ubuntu系统使用命令sudo minicom -D /dev/ttyUSB0。如果是window系统使用putty就可以了
# 打开开发板电源,一直按空格键进uboot
# 设置bootargs,bootcmd, bootcmd的bootz 80800000 和 - 之间一定要有一个空格。表示错过,如果没有空格进不了rootfs
setenv bootargs 'console=tty1 console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
setenv bootcmd 'mmc dev 1;fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-14x14-emmc-hdmi.dtb;bootz 80800000 - 83000000;'
USB触摸屏需要kernel支持,因此需要编译linux内核。这就又是一个大工程,这里我就不详细说了。如果你是使用的正点原子自带的LCD就简单多了,官方应当已经给你移植好了tslib,已经支持触摸了。就不用像我这样自己折腾了。这里就说下我的一些简单步骤:
# 进入正点原子提供的linux源码文件,解压
# 改架构和编译链
make imx_alientek_emmc_defconfig
make -j8 zImage
# 一切OK,/arch/arm/boot下面就会有一个zImage就是需要的内核
# 但这个时候的内核还不支持USB触摸,这里只是测试下编译是否有错误
make menuconfig
# 打开下面的选项,主要是第2个,第1个有没有用我也不知道
Device Drivers--->Input Device Support--->Touchscreens--->USB TouchScreen Driver--->USB Touchscreen Driver
Device Drivers--->HID support--->Special HID drivers--->HID Multitouch pannels
# 打开这2个选项后,再次
make -j8 zImage
# 将编译生成的/arch/arm/boot/zImage复制到开发板的/boot/目录下
# 复制时要确定替换原来的zImage,可能需要挂载boot文件系统
mount /dev/mmcblk1p1 /boot
再次开机,插入我的触摸屏USB和HDMI,看到/dev/input/下面多了2个文件,我的是/dev/input/event1和/dev/input/mouse0。表示USB触摸驱动成功。
tslib的编译与移植正点原子官方教程是有的,但对于我而言只有一半是正确的。
我的USB触摸好像只能用1.19版本的tslib。但官方提供的是1.21版本的,不知道是不是我设置不正确,就是不能正确使用。因此如果一个版本不行还请您更换一个版本,更换版本方法我也一并给出:
# 打开gitee.com,搜索tslib,选一个时间较新的进入,点标签xx,下载一个想要的版本就可以了
# 解压
tar xjvf tslib-1.19.tar.bz2
cd tslib-1.19
sudo apt install autoconf automake libtool
./autogen.sh
# prefix后面是你自己的安装目录,这里一定要有数,不然后面QT源码安装时你找不到了
./configure --host=arm-linux-gnueabihf --prefix=/home/IMX6ULL/tool/tslib
make -j8
make install
得到tslib的安装文件后,最好先移植到开发板上运行下,试试能不能运行ts_test_mt
命令,防止QT编译白忙活。
# 编译好的文件打包
tar cvf tslib.tar /home/IMX6ULL/tool/tslib/*
# 开发板中进入
cd /usr/local/
# 解压tslib
tar xvf tslib
# 这里要确定你的触摸事件设备号,有可能因为插入先后不同,需要插拔确认是/dev/input/下面的eventx
# 文件/etc/profile或~/.bashrc
export TSLIB_ROOT=/usr/local/tslib-1.19
export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
# 输入事件设备,对于我就是USB触摸屏设备event1
export TSLIB_TSDEVICE=/dev/input/event1
export TSLIB_CONFFILE=$TSLIB_ROOT/etc/ts.conf
export TSLIB_PLUGINDIR=$TSLIB_ROOT/lib/ts
export TSLIB_CALIBFILE=/etc/pointercal
export LD_PRELOAD=$TSLIB_ROOT/lib/libts.so
export PATH=$PATH:$TSLIB_ROOT/bin
# 生效文件
source /etc/profile
source ~/.bashrc
# 确定设定生效,不为空
echo $TSLIB_ROOT
# 使用屏幕校准命令,不报错
ts_calibrate
# 还可以用下面的测试命令,测试下
ts_test_mt
下面可以进行QT源码编译了,难得啊。
先要下载QT源码,下载网站download.qt.io
。我下载了5.12.9和5.15.5 2个版本,但5.12.9安装有问题。因此只有5.15.5安装成功了。
下载好后放到电脑与docker中的ubuntu16.04共享的目录。解压缩
xz -d qt-everywhere-src-5.15.5.tar.xz
tar xvf qt-everywhere-src-5.15.5.tar
# 建立一个文件保存编译中间件
mkdir qt
cd qt
vi auto.sh
# 将下面内容加入这个脚本文件,它是用来指定编译选项的,QT这种选项不算特别多,又不算少的项目就是麻烦,不能像kernel那样用kconfig,选项还不少,只能自己建立一个脚本指定,太不智能了。
/root/repo/qt-everywhere-src-5.15.5/configure \
-xplatform linux-arm-gnueabi-g++ \
-prefix /root/repo/qt-builds/imx-qt-5.15.5 \
-opensource \
-confirm-license \
-release \
-strip \
-shared \
-optimized-qmake \
-c++std c++11 \
-no-pch \
-skip qt3d \
-skip qtandroidextras \
-skip qtconnectivity \
-skip qtdoc \
-skip qtgamepad \
-skip qtlocation \
-skip qtmacextras \
-skip qtnetworkauth \
-skip qtpurchasing \
-skip qtremoteobjects \
-skip qtscript \
-skip qtscxml \
-skip qtsensors \
-skip qtspeech \
-skip qtsvg \
-skip qttools \
-skip qttranslations \
-skip qtwayland \
-skip qtwebengine \
-skip qtwebview \
-skip qtwinextras \
-skip qtx11extras \
-skip qtxmlpatterns \
-make libs \
-make examples \
-nomake tools -nomake tests \
-gui \
-widgets \
-dbus-runtime \
--glib=no \
--iconv=no \
--pcre=qt \
--zlib=qt \
-no-openssl \
--freetype=qt \
--harfbuzz=qt \
-no-opengl \
-linuxfb \
--xcb=no \
-tslib \
--libpng=qt \
--libjpeg=qt \
--sqlite=qt \
-plugin-sql-sqlite \
-I/root/repo/tslib-1.19/include \
-L/root/repo/tslib-1.19/lib \
-recheck-all
# 看到这么多选项,我也很无语,QT这坑货
# 开头这3行要根据自己源码位置,需要安装位置,使用编译链工具自己更改
/root/repo/qt-everywhere-src-5.15.5/configure \
-xplatform linux-arm-gnueabi-g++ \
-prefix /root/repo/qt-builds/imx-qt-5.15.5 \
# 最后这2行也要根据自己编译的tslib位置自己更改,说到这里又有一个坑,前面tslib编译已经有介绍了
-I/root/repo/tslib-1.19/include \
-L/root/repo/tslib-1.19/lib \
Qt编译就是麻烦,还要改下下面这个文件
# Qt源码/qtbase/mkspecs/linux-arm-gnueabi-g++/qmake.conf
# 加入这行
QMAKE_CXXFLAGS += -std=c++11
# 后面的编译链全部加上hf,因为我使用的编译链有这2个字母
# modifications to g++.conf
QMAKE_CC = arm-linux-gnueabihf-gcc
QMAKE_CXX = arm-linux-gnueabihf-g++
QMAKE_LINK = arm-linux-gnueabihf-g++
QMAKE_LINK_SHLIB = arm-linux-gnueabihf-g++
# modifications to linux.conf
QMAKE_AR = arm-linux-gnueabihf-ar cqs
QMAKE_OBJCOPY = arm-linux-gnueabihf-objcopy
QMAKE_NM = arm-linux-gnueabihf-nm -P
QMAKE_STRIP = arm-linux-gnueabihf-strip
load(qt_config)
终于快要开始编译了,难得啊
cd qt
./auto.sh
# 还有一个坑,如果你是下载的QT源码,很可能别人已经用它编译过了,而他编译使用的编译器版本大概率是9.0以上的版本。而9.0以上的版本会使用2个编译选项,但我们要使用的4.9.4或5.4.0是不支持这2个编译选项的,因此编译器会编译通不过。再次说一次QT这垃圾玩意儿。
# 这里除了要关注上面的版本问题外,还要确认./auto.sh进行配置时,最后的输出提示不要报tslib的错误,像下面这样
# 下面这个表示没有找到tslib库在那里,所以你的主机里面还要有一个开发板中支持的tslib的备份,给QT编译的时候引用。看来还是需要将tslib编译写在QT编译之前
ERROR: Feature 'tslib' was enabled, but the pre-condition 'libs.tslib' failed.
# 前面说了编译链工具版本的问题,这里说下处理方案。运行./auto.sh 后会生成一个.qmake.stash文件,里面有记录上次编译使用的编译链工具的版本。./auto.sh后直接手动更改就可以了
vi .qmake.stash
# 像下面这样,MAJOR_VERSION和MINOR_VERSION改为你自己的编译链版本。一定不能是太高的版本,使用的编译选项会不同
QMAKE_CXX.QT_COMPILER_STDCXX = 199711L
QMAKE_CXX.QMAKE_GCC_MAJOR_VERSION = 5
QMAKE_CXX.QMAKE_GCC_MINOR_VERSION = 4
QMAKE_CXX.QMAKE_GCC_PATCH_VERSION = 0
# 下面就是版本太高后报的有2个不支持选项的错误
arm-linux-gnueabihf-g++: error: unrecognized command line option '-Wshift-overflow=2'
arm-linux-gnueabihf-g++: error: unrecognized command line option '-Wduplicated-cond'
# 编译完成后安装就可以了,我的使用5.12.9到这一步都还能报错我也是服了。因此只能使用5.15.5版本.一定要确定安装正确,无报错
make install
QT移植到开发板步骤和移植tslib类似,有几个要注意的地方
# 打包安装到ubuntu16.04的编译文件
tar zcvf qt.tar.gz /root/repo/qt-builds/imx-qt-5.15.5
# 传到开发板,可以用scp或tftp
# 进入开发板/usr/local
cd /usr/local
# 解压qt包
tar zxvf qt.tar.gz
# 设置QT环境变量,同样也是/etc/profile或~/.bashrc
export QT_ROOT=/usr/local/imx-qt-5.15.5
# 这里的事件设备同tslib中的事件设备,是同一个
export QT_QPA_GENERIC_PLUGINS=tslib:/dev/input/event1
# 这个是用于显示中文字符的,你的系统中可能没有。因为我的开发板用的debian根文件系统,可以直接使用sudo apt install fonts-wqy-zenhei安装
# 如果你使用的正点原子默认系统,那你只能自己移植一个字库到板子上了
export QT_QPA_FONTDIR=/usr/share/fonts/truetype/wqy/
export QT_QPA_PLATFORM_PLUGIN_PATH=$QT_ROOT/plugins
# 显示设备,一般不用更改
export QT_QPA_PLATFORM=linuxfb:tty=/dev/fb0
export QT_PLUGIN_PATH=$QT_ROOT/plugins
export LD_LIBRARY_PATH=$QT_ROOT/lib:$QT_ROOT/plugins/platforms
export QML2_IMPORT_PATH=$QT_ROOT/qml
# 开启tslib支持
export QT_QPA_FB_TSLIB=1
# 更改后同样的使之生效
source /etc/profile
source ~/.bashrc
至此其实移植工作就完成了。最后还可以试下在docker的ubuntu16.04中编译一个QT程序,然后复制到开发板中运行。这里还是说下,防止读者不知道不使用qt-creator的情况下怎么使用这交叉编译环境:
# 打开docker中的imx_qt
sudo systemctl start docker
sudo docker start imx_qt
sudo docker attach imx_qt
cd /root/repo/qt-demo
cat dialog_type.cpp
/**********************dialog_type.cpp ***********************/
#include
int main(int argc, char **argv){
QApplication app(argc, argv);
QWidget widget;
QPushButton *button1, *button2, *button3;
QDialog *dialog1 = new QDialog(&widget);
QDialog *dialog2 = new QDialog(&widget);
QDialog *dialog3 = new QDialog(&widget, Qt::SubWindow);
button1 = new QPushButton("按键1", dialog1);
button2 = new QPushButton("按键2", dialog2);
button3 = new QPushButton("按键3", dialog3);
dialog1->move(22, 22);
dialog2->move(22, 77);
dialog3->move(22,111);
//明确调用dialog2的显示函数
dialog2->show();
widget.resize(222,333);
widget.show();
return app.exec();
}
/************************dialog_type.cpp end *************************/
# dialog_type.pro文件
######################################################################
# Automatically generated by qmake (3.1) Tue Feb 21 23:01:43 2023
######################################################################
TEMPLATE = app
TARGET = dialog_type
INCLUDEPATH += .
QT += core gui widgets
# The following define makes your compiler warn you if you use any
# feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
# Input
SOURCES += dialog_type.cpp
上面就是2个简单的qt项目,其实它也是使用qt-creator生成的。只是是使用我电脑上原来的qt-creator。而不是docker中的ubuntu16.04中安装qt-creator。
# qmake 之前需要将前面QT的安装目录下的bin加入到PATH
# ~/.bashrc
export PATH=$PATH:/root/repo/qt-builds/imx-qt-5.15.5/bin
# 生效设置
source ~/.bashrc
qmake
make
# 生成dialog_type
# 最后看下可执行文件格式
file dialog_type
dialog_type: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=1eebbf5217d2e007aca6c0a69d7eb6c425cdb723, not stripped
# 可以看到是32位的ELF可执行文件,架构是ARM,/lib/ld-linux-armhf.so.3就对应了ldd的版本
# 看下qmake可执行文件格式
which qmake
/root/repo/qt-builds/imx-qt-5.15.5/bin/qmake
file /root/repo/qt-builds/imx-qt-5.15.5/bin/qmake
/root/repo/qt-builds/imx-qt-5.15.5/bin/qmake: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=16e8bb71c7843c16459b64b9152ee56cb69cdd0f, not stripped
# 可以看到qmake和生成的dialog_type格式就不一样,qmake是64位ELF 可执行文件,架构是x86_64,ldd对应库版本是/lib64/ld-linux-x86-64.so.2。
# 因此我们编译QT生成的qmake和使用qmake编译生成的可执行文件是2种不同架构下运行的可执行文件,因此才把它叫做交叉编译环境。这里的qmake版本是不能直接在开发板中运行的,只能在主机环境,也就是docker中的ubuntu16.04中运行。但使用这个qmake生成的makefile编译出来的可执行文件,又可以在开发板中运行。是不是有点神奇!
最后将可执行文件传到开发板,加入执行权限就可以运行了。一切正常就会看到一个界面上有3个按钮,可以用触摸屏点击,就表示OK了。