移植Qt5.12.9

  • 参考正点原子的Qt移植教程,整理自己实际的应用步骤
  • 同样的步骤测试Qt5.9.5也适用

一、安装虚拟机环境

  • 当前使用版本:Ubuntu16.04.7
$ lsb_release -a	# 查看当前系统信息

请添加图片描述

二、安装交叉编译器

2.1 下载交叉编译器

  • 下载地址:Linaro Releases
  • 交叉编译器版本:4.9.4
    • 【TIPS】不推荐选择过高版本,建议采用实测版本
    • 根据芯片类型选择,不带
  • 根据当前系统版本选择对应的文件
$ uname -a 
Linux ubuntu 4.15.0-112-generic #113~16.04.1-Ubuntu SMP Fri Jul 10 04:37:08 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

# x86_64: 当前系统为64位系统,选择x86_64版本的交叉编译器

请添加图片描述

2.2 安装交叉编译器

  • 新建arm文件
$ sudo mkdir /usr/local/arm
  • 解压交叉编译器压缩包到arm路径下
$ sudo tar -xvf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi.tar.xz

## 建议重新修改名称
$ mv gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi /usr/local/arm/linaro-4.9.4
  • 配置环境变量
$ sudo vim /etc/profile
## 将下行内容添加到文件末尾
export PATH=$PATH:/usr/local/arm/linaro-4.9.4/bin

## /etc/profile 开机加载;通过source指令立即生效
$ source /etc/profile

2.3 验证交叉编译器

## 使用该交叉编译器前,需要在Ubuntu安装一些库 【必要!】
$ sudo apt-get install lsb-core lib32stdc++6	

## 验证交叉编译器,有版本信息输出则配置完成
$ arm-linux-gnueabi-gcc -v
...
gcc version 4.9.4 (Linaro GCC 4.9-2017.01)

三、获取和编译tslib

  • Qt支持触摸屏,还需要编译tslib,以产生相关插件

3.1 下载tslib

  • 下载地址:tslib
  • 项目页面的右侧,在release中选择1.21版本

请添加图片描述请添加图片描述

3.2 编译tslib

  • 安装辅助软件
$ sudo apt-get update
$ sudo apt-get install autoconf automake libtool
  • 调用脚本
$ tar -xvf tslib-1.21	## 解压
$ cd tslib-1.21
$ ./autogen.sh	# 软件文件夹自带脚本
  • 执行configure指令
$ ./configure \
--host=arm-linux-gnueabi \ # 指定编译器
ac_cv_func_malloc_0_nonnull=yes \
--cache-file=arm-linux.cache \
-prefix=${PWD}/arm-tslib # 指定编译输出路径;根据自己的实际路径设置
  • 编译
$ make 			
$ make install 

3.3 验证编译生成文件

$ file /arm-tslib/bin/ts_calibrate	# 查看编译生成文件的类型; 32-bit  ARM 即为正确
bin/ts_calibrate: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),
dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 2.6.31,
BuildID[sha1]=235a90a36a321ae53ee3f6f63f44d7aed8e83e64, not stripped

四、编译Qt5.12.9源码

4.1 下载源码

请添加图片描述

  • 解压源码
$ tar -xvf qt-everywhere-src-5.12.9.tar.xz

## 建议修改文件名
$ mv qt-everywhere-src-5.12.9 qt-5.12.9
$ cd qt-5.12.9

4.2 修改qmake.conf

$ gedit qtbase/mkspecs/linux-arm-gnueabi-g++/qmake.conf

## 添加下列内容;位置在 include(../common/linux.conf) 之前
QT_QPA_PLATFORM = linuxfb:fb=/dev/fb0
QMAKE_CFLAGS_RELEASE += -O2 -march=armv7-a
QMAKE_CXXFLAGS_RELEASE += -O2 -march=armv7-a

4.3 修改qlinuxfbscreen

  • 在嵌入式中运行,Qt4采用QWS系统,Qt5采用QPA方案
  • 在Qt4中可设置QWS_DISPLAY旋转软件
  • 在Qt5中只能通过修改qlinuxfbscreen源码处理【通过QGraphicsScene的方案,事件无法往子控件传递】
$ gedit qtbase/src/plugins/platforms/linuxfb/qlinuxfbscreen.h	# 修改头文件
$ gedit qtbase/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp # 修改源文件
class QLinuxFbScreen : public QFbScreen
{
    Q_OBJECT
public:
    QLinuxFbScreen(const QStringList &args);
    ~QLinuxFbScreen();

    bool initialize() override;

    QPixmap grabWindow(WId wid, int x, int y, int width, int height) const override;

    QRegion doRedraw() override;

private:
    QStringList mArgs;
    int mFbFd;
    int mTtyFd;
    
	// add by immortal start
	int mRotation;
	// add by immortal end

    QImage mFbScreenImage;
    int mBytesPerLine;
    int mOldTtyMode;

    struct {
        uchar *data;
        int offset, size;
    } mMmap;

    QPainter *mBlitter;
};
QLinuxFbScreen::QLinuxFbScreen(const QStringList &args)
 //   : mArgs(args), mFbFd(-1), mTtyFd(-1), mBlitter(0)  // modify by immortal
 : mArgs(args), mFbFd(-1), mTtyFd(-1), mBlitter(0),mRotation(0)
{
    mMmap.data = 0;
}
 
QLinuxFbScreen::~QLinuxFbScreen()
{
    if (mFbFd != -1) {
        if (mMmap.data)
            munmap(mMmap.data - mMmap.offset, mMmap.size);
        close(mFbFd);
    }
 
    if (mTtyFd != -1)
        resetTty(mTtyFd, mOldTtyMode);
 
    delete mBlitter;
}
 
bool QLinuxFbScreen::initialize()
{
    QRegularExpression ttyRx(QLatin1String("tty=(.*)"));
    QRegularExpression fbRx(QLatin1String("fb=(.*)"));
    QRegularExpression mmSizeRx(QLatin1String("mmsize=(\\d+)x(\\d+)"));
    QRegularExpression sizeRx(QLatin1String("size=(\\d+)x(\\d+)"));
    QRegularExpression offsetRx(QLatin1String("offset=(\\d+)x(\\d+)"));
	
	// add by immorta start
	QRegularExpression rotationRx(QLatin1String("rotation=(0|90|180|270)"))
	// add by immorta end
 
    QString fbDevice, ttyDevice;
    QSize userMmSize;
    QRect userGeometry;
    bool doSwitchToGraphicsMode = true;
 
    // Parse arguments
    foreach (const QString &arg, mArgs) {
        QRegularExpressionMatch match;
        if (arg == QLatin1String("nographicsmodeswitch"))
            doSwitchToGraphicsMode = false;
        else if (arg.contains(mmSizeRx, &match))
            userMmSize = QSize(match.captured(1).toInt(), match.captured(2).toInt());
        else if (arg.contains(sizeRx, &match))
            userGeometry.setSize(QSize(match.captured(1).toInt(), match.captured(2).toInt()));
        else if (arg.contains(offsetRx, &match))
            userGeometry.setTopLeft(QPoint(match.captured(1).toInt(), match.captured(2).toInt()));
        else if (arg.contains(ttyRx, &match))
            ttyDevice = match.captured(1);
        else if (arg.contains(fbRx, &match))
            fbDevice = match.captured(1);
		// add by immortal start
		else if (arg.contains(rotationRx, &match))
			mRotation = match.captured(1).toInt();
		// add by immortal end
    }
 
    if (fbDevice.isEmpty()) {
        fbDevice = QLatin1String("/dev/fb0");
        if (!QFile::exists(fbDevice))
            fbDevice = QLatin1String("/dev/graphics/fb0");
        if (!QFile::exists(fbDevice)) {
            qWarning("Unable to figure out framebuffer device. Specify it manually.");
            return false;
        }
    }
 
    // Open the device
    mFbFd = openFramebufferDevice(fbDevice);
    if (mFbFd == -1) {
        qErrnoWarning(errno, "Failed to open framebuffer %s", qPrintable(fbDevice));
        return false;
    }
 
    // Read the fixed and variable screen information
    fb_fix_screeninfo finfo;
    fb_var_screeninfo vinfo;
    memset(&vinfo, 0, sizeof(vinfo));
    memset(&finfo, 0, sizeof(finfo));
 
    if (ioctl(mFbFd, FBIOGET_FSCREENINFO, &finfo) != 0) {
        qErrnoWarning(errno, "Error reading fixed information");
        return false;
    }
 
    if (ioctl(mFbFd, FBIOGET_VSCREENINFO, &vinfo)) {
        qErrnoWarning(errno, "Error reading variable information");
        return false;
    }
 
    mDepth = determineDepth(vinfo);
    mBytesPerLine = finfo.line_length;
    QRect geometry = determineGeometry(vinfo, userGeometry);
	// add by immortal start
	QRect originalGeometry = geometry;
	if( 90 == mRotation  || 270 == mRotation )
    {
		int tmp = geometry.width();
        geometry.setWidth(geometry.height());
        geometry.setHeight(tmp);
    }
	// add by immortal end
    mGeometry = QRect(QPoint(0, 0), geometry.size());
    mFormat = determineFormat(vinfo, mDepth);
	
	// modify by immortal start
	//   mPhysicalSize = determinePhysicalSize(vinfo, userMmSize, geometry.size());
	mPhysicalSize = determinePhysicalSize(vinfo, userMmSize, originalGeometry.size());
	// modify by immortal end
	
    // mmap the framebuffer
    mMmap.size = finfo.smem_len;
    uchar *data = (unsigned char *)mmap(0, mMmap.size, PROT_READ | PROT_WRITE, MAP_SHARED, mFbFd, 0);
    if ((long)data == -1) {
        qErrnoWarning(errno, "Failed to mmap framebuffer");
        return false;
    }
	// modify by immortal start
//   mMmap.offset = geometry.y() * mBytesPerLine + geometry.x() * mDepth / 8;
	mMmap.offset = originalGeometry.y() * mBytesPerLine + originalGeometry.x() * mDepth / 8;
	// modify by immortal end
	
    mMmap.data = data + mMmap.offset;
 
    QFbScreen::initializeCompositor();
	
    // modify by immortal start
	// mFbScreenImage = QImage(mMmap.data, geometry.width(), geometry.height(), mBytesPerLine, mFormat);
	mFbScreenImage = QImage(mMmap.data, originalGeometry.width(), originalGeometry.height(), mBytesPerLine, mFormat);
	// modify by immortal end
	
    mCursor = new QFbCursor(this);
 
    mTtyFd = openTtyDevice(ttyDevice);
    if (mTtyFd == -1)
        qErrnoWarning(errno, "Failed to open tty");
 
    switchToGraphicsMode(mTtyFd, doSwitchToGraphicsMode, &mOldTtyMode);
    blankScreen(mFbFd, false);
 
    return true;
}
 
QRegion QLinuxFbScreen::doRedraw()
{
    QRegion touched = QFbScreen::doRedraw();
 
    if (touched.isEmpty())
        return touched;
 
    if (!mBlitter)
        mBlitter = new QPainter(&mFbScreenImage);
 
    const QVector<QRect> rects = touched.rects();
    mBlitter->setCompositionMode(QPainter::CompositionMode_Source);
 
	for (int i = 0; i < rects.size(); ++i) 	
	// add by immortal start		
	{
		if( 90 == mRotation || 270 == mRotation )
        {
			mBlitter->translate(mGeometry.height()/2, mGeometry.width()/2);
        }
        else if( 180 == mRotation )
        {
            mBlitter->translate(mGeometry.width()/2, mGeometry.height()/2);
        }
        if( mRotation != 0 )
        {
            mBlitter->rotate(mRotation);
            mBlitter->translate(-mGeometry.width()/2, -mGeometry.height()/2);
        }
	// add by immortal end	
	
        mBlitter->drawImage(rects[i], *mScreenImage, rects[i]);
		
	// add by immortal start
	mBlitter->resetTransform();
	// add by immortal end
    }
    return touched;
}
 
// grabWindow() grabs "from the screen" not from the backingstores.
// In linuxfb's case it will also include the mouse cursor.
QPixmap QLinuxFbScreen::grabWindow(WId wid, int x, int y, int width, int height) const
{
    if (!wid) {
        if (width < 0)
            width = mFbScreenImage.width() - x;
        if (height < 0)
            height = mFbScreenImage.height() - y;
        return QPixmap::fromImage(mFbScreenImage).copy(x, y, width, height);
    }
    QFbWindow *window = windowForId(wid);
    if (window) {
        const QRect geom = window->geometry();
        if (width < 0)
            width = geom.width() - x;
        if (height < 0)
            height = geom.height() - y;
        QRect rect(geom.topLeft() + QPoint(x, y), QSize(width, height));
        rect &= window->geometry();
        return QPixmap::fromImage(mFbScreenImage).copy(rect);
    }
    return QPixmap();
}

4.4 配置编译选项

## 配置选项保存为脚本文件
$ gedit autoconfigure.sh
$ sudo chmod a+x autoconfigure.sh	# 添加执行权限
$ ./autoconfigure.sh
...
Qt is now configure for building, Just run 'make' # 看到此条信息,可直接编译
...
## autoconfigure.sh文件内容
#!/bin/bash

./configure -prefix ${PWD}/arm-qt \
-opensource \
-confirm-license \
-release \
-strip \
-shared \
-xplatform linux-arm-gnueabi-g++ \
-optimized-qmake \
-c++std c++11 \
--rpath=no \
-pch \
-make libs \
-nomake examples -nomake tools \
-nomake tests \
-gui \
-widgets \
-dbus-runtime \
--glib=no \
--iconv=no \
--pcre=qt \
--zlib=qt \
-no-openssl \
-no-opengl \
--freetype=qt \
--harfbuzz=qt \
-linuxfb \
--xcb=no \
-tslib \
--libpng=qt \
--libjpeg=qt \
--sqlite=qt \
-plugin-sql-sqlite \
-I/usr/local/arm/arm-tslib/include \	# 设定为实际路径
-L/usr/local/arm/arm-tslib/lib \		# 设置为实际路径
-recheck-all

4.5 编译Qt源码

$ make # 等 1-2 h完成,也可以使用 make -j n;全速编译
make[1]: Leaving directory ...

$ make install

五、移植Qt到示教器

5.1 移植tslib

## 压缩编译输出文件
$ tar -zcvf arm-tslib.tar.gz arm-tslib
  • 通过scp、sftp或U盘将文件传送到示教器中
$ tar -zxvf arm-tslib.tar.gz	# 解压文件
  • 配置环境变量
## 示教器使用 vim.tiny;显示有时有问题,改用nano编辑
$ nano /etc/bash.bashrc
## 将之间的配置信息注释
# export T_ROOT=/usr/local/tslib
# export TSLIB_CONFFILE=$T_ROOT/etc/ts.conf
# export TSLIB_TSEVENTTYPE=H3600
# export TSLIB_CONSOLEDEVICE=none
# export TSLIB_FBDEVICE=/dev/fb0
# export TSLIB_PLUGINDIR=$T_ROOT/lib/ts
# export TSLIB_CALIBFILE=/etc/pointercal

## 修改环境变量
export TSLIB_ROOT=/usr/local/arm/tslib-1.21 # 实际存放路径
export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
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
if [ -e /dev/input/mouse1 ]; then
export TSLIB_TSDEVICE=/dev/input/event2
else
export TSLIB_TSDEVICE=/dev/input/event1
rm -f /dev/input/mouse0
fi
  • 测试tslib
$ source /etc/bash.bashrc
$ /usr/local/arm/tslib-1.21/bin/ts_test # 运行ts_test测试触摸屏是否正常

5.2 移植Qt

## 压缩编译输出文件
$ tar -zcvf arm-qt.tar.gz arm-qt
  • 传输方式同上即可
$ tar -zxvf arm-qt.tar.gz
  • 配置环境变量
$ nano /etc/bash.bashrc
## Qt5 使用 QPA 环境变量;Qt4 使用 QWS 环境变量
export QT_ROOT=/usr/local/arm/qt-5.12 # 实际路径
export QT_QPA_GENERIC_PLUGINS=tslib:/dev/input/event1
export QT_QPA_FONTDIR=/usr/share/fonts # 如果没有,找到系统自带的;指定为实际路径
export QT_QPA_PLATFORM_PIUGIN_PATH=$QT_ROOT/plugins
export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0:rotation=90 # 指定旋转度数
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
export QT_QPA_FB_TSLIB=1
  • 测试Qt运行
$ source /etc/bash.bashrc
## 由于编译Qt源码跳过了examples 和 tests,无现成的测试应用;需要新建工程测试,结合下一节实现

六、搭建ARM平台的Qt Creator环境

6.1 安装Qt Creator

  • 下载地址:Qt5.12.9
    请添加图片描述

  • 下载完成,在虚拟机环境双击安装

6.2 配置Qt Creator Kit

  • 打开选项
    请添加图片描述

  • 配置qmake:找到编译生成arm-qt下的qmake
    请添加图片描述

  • 配置gcc编译器:安装的linaro
    请添加图片描述
    请添加图片描述

  • 配置Kits:以实际版本名称,或自定义命名
    请添加图片描述

6.3 编译程序

  • 选择Arm Kit编译Qt工程

七、运行程序

  • 将文件传输到示教器
  • 添加可执行权限
  • 直接执行

7.1 旋转tslib

  • 软件旋转后,触摸的坐标系没有匹配;导致点击屏幕的位置不对
$ /usr/local/arm/tslib-1.21/bin/ts_calibrate -h	# 查看帮助信息
$ ts_calibrate -r 1	# 旋转90°,调出屏幕校准;文字阅读方向即为当前正方向

请添加图片描述

  • 校准屏幕后直接运行程序
$ ./Icon	# 直接运行即可
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值