00 Qt 6 embedded platformları
Qt 6, QPA (Qt Platform Abstraction) katmanı üzerinden farklı display backend'lerini destekler. Embedded sistemde doğru platform seçimi, performans ve özellik seti açısından kritiktir.
Platform karşılaştırması
QT_QPA_PLATFORM=eglfs ile etkinleştirilir.QT_QPA_PLATFORM=wayland.Platform seçim kararı
GPU sürücüsü + DRM/KMS mevcut?
↓ Evet
Tek uygulama kiosk mi?
├─ Evet → EGLFS (en az overhead)
└─ Hayır → Wayland + Qt wayland platform
↓ Hayır
linuxfb (performansı kabul edersen)
01 Cross-compile — Qt 6 kaynak derleme ve sysroot
Qt 6 cross-compile için host araçları (qmake, moc, rcc) host'ta, hedef kütüphaneler ise sysroot içinde derlenir. CMake, qt-configure-module yerine birleşik araç olarak kullanılır.
Sysroot hazırlama (RPi4 örneği)
# Hedef sistemden sysroot kopyala (rsync ile)
rsync -avz --rsync-path="sudo rsync" \
pi@raspberrypi.local:/usr/include sysroot/usr/
rsync -avz --rsync-path="sudo rsync" \
pi@raspberrypi.local:/usr/lib sysroot/usr/
rsync -avz --rsync-path="sudo rsync" \
pi@raspberrypi.local:/lib sysroot/lib/
rsync -avz --rsync-path="sudo rsync" \
pi@raspberrypi.local:/opt/vc sysroot/opt/
# Sembolik linkleri düzelt (sysroot'a göreli yapılır)
python3 sysroot-relativelinks.py sysroot
CMake toolchain dosyası
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(SYSROOT_PATH "/opt/sysroot-rpi4")
set(CMAKE_SYSROOT ${SYSROOT_PATH})
set(CMAKE_FIND_ROOT_PATH ${SYSROOT_PATH})
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# Qt host araçları
set(QT_HOST_PATH /opt/qt6-host)
set(QT_HOST_PATH_CMAKE_DIR /opt/qt6-host/lib/cmake)
Qt 6 host araçları derleme
# Qt 6 kaynak indir
git clone --branch v6.7.2 --depth 1 \
https://code.qt.io/qt/qt5.git qt6
cd qt6 && perl init-repository
# Host araçları derle (hedef için gerekli)
mkdir build-host && cd build-host
cmake ../qt6 \
-DCMAKE_INSTALL_PREFIX=/opt/qt6-host \
-DQT_BUILD_EXAMPLES=OFF \
-DQT_BUILD_TESTS=OFF
cmake --build . --parallel $(nproc)
cmake --install .
# Cross-compile için Qt 6
mkdir build-rpi4 && cd build-rpi4
cmake ../qt6 \
-DCMAKE_TOOLCHAIN_FILE=../toolchain-rpi4.cmake \
-DCMAKE_INSTALL_PREFIX=/opt/qt6-rpi4 \
-DQT_HOST_PATH=/opt/qt6-host \
-DFEATURE_eglfs=ON \
-DFEATURE_linuxfb=ON \
-DFEATURE_wayland=OFF \
-DQT_BUILD_EXAMPLES=OFF
cmake --build . --parallel $(nproc)
cmake --install .
02 EGLFS yapılandırması — KMS JSON, cursor, vsync
EGLFS, DRM/KMS connector ve modunu JSON dosyası aracılığıyla yapılandırır. Çözünürlük, yönlendirme, cursor ve VSync bu dosyada belirlenir.
Ortam değişkenleri
eglfs olarak ayarla. Alternatif: eglfs:fb=1 ile belirli fb cihazı.1 ile fare imlecini gizle — dokunmatik kiosk için.1 ile VSync zorla — tear-free render için.KMS JSON konfigürasyon dosyası
{
"device": "/dev/dri/card0",
"hwcursor": false,
"pbuffers": true,
"outputs": [
{
"name": "HDMI-A-1",
"mode": "1920x1080",
"refresh": 60,
"rotation": 0,
"virtualIndex": 0
},
{
"name": "DSI-1",
"mode": "1024x600",
"refresh": 60,
"rotation": 90,
"virtualIndex": 1
}
]
}
Uygulama başlatma scripti
export QT_QPA_PLATFORM=eglfs
export QT_QPA_EGLFS_KMS_CONFIG=/etc/qt-kms.json
export QT_QPA_EGLFS_HIDECURSOR=1
export QT_QPA_EGLFS_FORCEVSYNC=1
export QT_QPA_FONTDIR=/usr/share/fonts/truetype
export QT_SCALE_FACTOR=1.5 # DPI ölçekleme
./my-qt6-hmi
03 QML ile HMI — Quick Controls 2, font, image cache
Qt Quick Controls 2 (QQC2), embedded için optimize edilmiş hafif kontrol seti sunar. Özel tema ve FreeType font rendering ile tutarlı görünüm sağlanır.
Minimal QML uygulaması
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
ApplicationWindow {
id: root
visible: true
width: 1024; height: 600
title: "HMI Panel"
color: "#1a1a2e"
ColumnLayout {
anchors.centerIn: parent
spacing: 20
Text {
text: "Motor Sıcaklığı"
font.pixelSize: 32
font.family: "Roboto"
color: "#e0e0e0"
Layout.alignment: Qt.AlignHCenter
}
Text {
id: tempDisplay
text: motorMonitor.temperature + " °C"
font.pixelSize: 72
font.bold: true
color: motorMonitor.temperature > 80 ? "#ff4444" : "#44ff88"
Layout.alignment: Qt.AlignHCenter
}
ProgressBar {
value: motorMonitor.temperature / 120
Layout.fillWidth: true
Layout.preferredHeight: 24
}
Button {
text: "Resetle"
Layout.alignment: Qt.AlignHCenter
onClicked: motorMonitor.reset()
}
}
}
Embedded için QQC2 tema
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickStyle>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
// Embedded için "Basic" stil — en hafif
// Alternatifler: "Material", "Universal", "Fusion"
QQuickStyle::setStyle("Basic");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
Font rendering ve image cache
# FreeType ile anti-aliased font
export QT_QPA_FONTDIR=/usr/share/fonts/truetype/roboto
# Texture atlas boyutu (VRAM yönetimi)
export QSG_ATLAS_WIDTH=2048
export QSG_ATLAS_HEIGHT=2048
# Image cache boyutu (MB) — düşük bellek için azalt
export QML_DISK_CACHE_PATH=/tmp/qml-cache
export QT_QUICK_CONTROLS_CONF=/etc/qtquickcontrols2.conf
04 Touch input — evdev, libinput, kalibrasyon
Qt 6 EGLFS, dokunmatik ekranı evdev veya libinput backend'i üzerinden alır. Ekran koordinatlarını uygulama koordinatlarına eşlemek için kalibrasyon gerekebilir.
Ortam değişkenleri
rotate=180:invertx:invertz.evdevtouch:/dev/input/event1 ile belirli cihazı zorla.Dokunmatik kalibrasyon
# Dokunma noktasını izle
evtest /dev/input/event1
# libinput ile cihaz listesi
libinput list-devices
# Qt evdev kalibrasyon parametreleri (weston.ini formatından farklı)
export QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS=\
/dev/input/event1:rotate=0
# 90 derece döndürülmüş ekran
export QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS=\
/dev/input/event1:rotate=90
# Yanlış eksen: X/Y eksenlerini değiştir
export QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS=\
/dev/input/event1:swapxy:invertx
QML'de çoklu dokunma
Rectangle {
width: 800; height: 480
MultiPointTouchArea {
anchors.fill: parent
minimumTouchPoints: 1
maximumTouchPoints: 5
onTouchUpdated: (touchPoints) => {
touchPoints.forEach(tp => {
console.log("Parmak", tp.pointId,
":", tp.x.toFixed(0), tp.y.toFixed(0))
})
}
}
// Pinch/zoom örneği
PinchArea {
anchors.fill: parent
onPinchUpdated: (pinch) => {
console.log("Scale:", pinch.scale.toFixed(2))
}
}
}
05 Qt + OpenGL ES — QOpenGLWidget, GLSL shader
Qt 6 ile doğrudan OpenGL ES çağrıları yapılabilir. QOpenGLWidget widget içine GLES2 render gömer; QQuickView ise QML sahnesini GLES2 ile hızlandırır.
QOpenGLWidget ile özel render
#include <QOpenGLWidget>
#include <QOpenGLFunctions_ES2>
#include <QOpenGLShaderProgram>
class GlWidget : public QOpenGLWidget,
protected QOpenGLFunctions_ES2
{
Q_OBJECT
QOpenGLShaderProgram *prog = nullptr;
protected:
void initializeGL() override {
initializeOpenGLFunctions();
prog = new QOpenGLShaderProgram(this);
prog->addShaderFromSourceCode(QOpenGLShader::Vertex,
"attribute vec2 pos;\n"
"void main() { gl_Position = vec4(pos, 0.0, 1.0); }\n");
prog->addShaderFromSourceCode(QOpenGLShader::Fragment,
"precision mediump float;\n"
"uniform vec4 color;\n"
"void main() { gl_FragColor = color; }\n");
prog->link();
}
void paintGL() override {
glClearColor(0.1f, 0.1f, 0.15f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
prog->bind();
prog->setUniformValue("color", QVector4D(0.2f, 0.8f, 0.4f, 1.0f));
GLfloat verts[] = { 0.0f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f };
prog->enableAttributeArray("pos");
prog->setAttributeArray("pos", GL_FLOAT, verts, 2);
glDrawArrays(GL_TRIANGLES, 0, 3);
prog->release();
}
void resizeGL(int w, int h) override {
glViewport(0, 0, w, h);
}
};
GPU hızlandırmalı QML animasyon
import QtQuick 2.15
Rectangle {
width: 400; height: 400
// GPU'da çalışan özel shader efekti
layer.enabled: true
layer.effect: ShaderEffect {
property real time: 0.0
NumberAnimation on time {
from: 0; to: 6.28; duration: 3000
loops: Animation.Infinite
}
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform highp float time;
void main() {
highp vec2 uv = qt_TexCoord0;
uv.x += 0.01 * sin(uv.y * 20.0 + time);
gl_FragColor = texture2D(source, uv);
}
"
}
Image { source: "panel_bg.png"; anchors.fill: parent }
}
06 Yocto meta-qt6 — qt6-base, qt6-declarative, IVI
Yocto Project'te Qt 6 için meta-qt6 katmanı gereklidir. Qt IVI (In-Vehicle Infotainment) ek katmanı otomotiv HMI için servis altyapısı sağlar.
meta-qt6 kurulumu
# meta-qt6 kaynak
git clone https://code.qt.io/yocto/meta-qt6.git \
-b 6.7 sources/meta-qt6
# bblayers.conf'a ekle
bitbake-layers add-layer sources/meta-qt6
# local.conf'ta Qt özellikleri etkinleştir
DISTRO_FEATURES:append = " opengl eglfs"
QT6_EGLFS_INTEGRATION = "kms"
IMAGE_INSTALL — Qt 6 paketleri
require recipes-core/images/core-image-minimal.bb
IMAGE_INSTALL:append = " \
qt6-base \
qt6-declarative \
qt6-quickcontrols2 \
qt6-multimedia \
qt6-svg \
qt6-virtualkeyboard \
fontconfig \
ttf-roboto \
my-hmi-application \
"
# QML önbelleği için tmp dizini
IMAGE_INSTALL:append = " qml-cache-pregeneration"
Qt IVI (In-Vehicle Infotainment)
import QtIvi 1.0
import QtIvi.Media 1.0
Item {
MediaPlayer {
id: player
onPlaybackStateChanged: {
if (playbackState === MediaPlayer.PlayingState)
console.log("Çalıyor:", currentTrack.title)
}
}
ClimateControl {
id: climate
Component.onCompleted: {
console.log("Kabin sıcaklığı:",
targetTemperature.value, "°C")
}
}
}
07 Performans optimizasyonu — QML profiler, render loop
Embedded sistemlerde Qt uygulaması yavaş çalışıyorsa QML profiler ile darboğazı bul, render loop seçimini optimize et ve delegate caching ile bellek kullanımını azalt.
QML profiler
# Uygulamayı profiler modunda başlat
QML_PROFILER=1 ./my-hmi-app &
# Qt Creator'da Analyze → QML Profiler → Attach
# veya komut satırından:
qmlprofiler -attach localhost my-hmi-app
# FPS sayacını HUD olarak göster
QSG_RENDER_TIMING=1 ./my-hmi-app
export QSG_VISUALIZE=overdraw # overdraw analizi
export QSG_VISUALIZE=batches # render batch analizi
Render loop seçimi
# Threaded render loop (önerilen)
export QSG_RENDER_LOOP=threaded
# GPU sürücüsü threaded desteklemiyorsa
export QSG_RENDER_LOOP=basic
# Mevcut loop'u öğren
QSG_INFO=1 ./my-hmi-app 2>&1 | grep "render loop"
Delegate caching ve texture atlas
ListView {
model: sensorModel
cacheBuffer: 200 // piksel cinsinden önbellek alanı
delegate: Rectangle {
// layer.enabled = true → GPU texture cache
layer.enabled: true
layer.smooth: true
width: ListView.view.width
height: 60
Text {
text: model.name + ": " + model.value
font.pixelSize: 20
anchors.centerIn: parent
}
}
}
08 Pratik
Üç gerçek senaryo: i.MX8 LVDS ekranda Qt 6 dashboard, NXP eIQ ile Qt + ML inference ve Yocto image build akışı.
i.MX8 LVDS ekranda Qt 6 dashboard
# i.MX8 için cross-compile
export QT_QPA_PLATFORM=eglfs
export QT_QPA_EGLFS_KMS_CONFIG=/etc/qt-kms-imx8.json
# qt-kms-imx8.json
cat > /etc/qt-kms-imx8.json <<'EOF'
{
"device": "/dev/dri/card1",
"outputs": [{
"name": "LVDS-1",
"mode": "1024x768",
"refresh": 60
}]
}
EOF
# GPU sürücüsü: Vivante GC7000 (NXP proprietary)
# Yocto'da: PREFERRED_PROVIDER_virtual/mesa = "imx-gpu-viv"
./dashboard-app
NXP eIQ ile Qt + ML inference
#include <QQuickImageProvider>
#include <tensorflow/lite/interpreter.h>
#include <tensorflow/lite/model.h>
class InferenceProvider : public QQuickImageProvider {
public:
InferenceProvider() : QQuickImageProvider(QImage) {
// TFLite modelini yükle
model_ = tflite::FlatBufferModel::BuildFromFile(
"/usr/share/models/mobilenet_v2.tflite");
tflite::InterpreterBuilder(*model_, resolver_)
(&interpreter_);
interpreter_->AllocateTensors();
}
QImage requestImage(const QString &id,
QSize *, const QSize &) override
{
// Kamera frame'ini al, inference yap, sonucu overlay et
QImage frame = captureFrame();
runInference(frame);
drawBoundingBoxes(frame);
return frame;
}
private:
std::unique_ptr<tflite::FlatBufferModel> model_;
tflite::ops::builtin::BuiltinOpResolver resolver_;
std::unique_ptr<tflite::Interpreter> interpreter_;
};
Yocto Qt 6 image build — tam akış
# Katmanları hazırla
source oe-init-build-env build-imx8
bitbake-layers add-layer ../sources/meta-qt6
bitbake-layers add-layer ../sources/meta-imx
bitbake-layers add-layer ../sources/meta-imx/meta-bsp
# local.conf
echo 'MACHINE = "imx8mq-evk"' >> conf/local.conf
echo 'DISTRO = "fsl-imx-wayland"' >> conf/local.conf
echo 'IMAGE_INSTALL:append = " qt6-base qt6-declarative my-hmi"' \
>> conf/local.conf
# Build
bitbake core-image-qt6-hmi
# SD kart imajını yaz
bzcat tmp/deploy/images/imx8mq-evk/*.wic.bz2 \
| dd of=/dev/sdX bs=4M status=progress