Qt portolása STM32-re

Qt portolása STM32-reJó napot Benne vagyunk a projektben Embox elindította a Qt-t az STM32F7-Discovery-en, és szeretne beszélni róla. Korábban már elmondtuk, hogyan sikerült elindítani OpenCV.

A Qt egy többplatformos keretrendszer, amely nemcsak grafikus összetevőket tartalmaz, hanem olyan dolgokat is, mint a QtNetwork, az adatbázisokkal való munkavégzéshez szükséges osztályok, a Qt for Automation (beleértve az IoT megvalósítását is) és még sok más. A Qt csapata proaktívan foglalkozott a Qt beágyazott rendszerekben történő használatával, így a könyvtárak meglehetősen konfigurálhatók. Azonban egészen a közelmúltig kevesen gondoltak arra, hogy a Qt-t mikrokontrollerekre portolják, valószínűleg azért, mert egy ilyen feladat nehéznek tűnik - a Qt nagy, az MCU-k kicsik.

Másrészt jelenleg léteznek olyan mikrokontrollerek, amelyeket multimédiával való együttműködésre terveztek, és jobbak az első Pentiumoknál. Körülbelül egy éve jelent meg a Qt blog posta. A fejlesztők létrehoztak egy Qt portot az RTEMS operációs rendszerhez, és példákat indítottak widgetekkel több stm32f7-et futtató kártyán. Ez érdekelt minket. Észrevehető volt, és maguk a fejlesztők is írnak róla, hogy a Qt lassú az STM32F7-Discovery-n. Arra voltunk kíváncsiak, hogy futtathatjuk-e a Qt-t az Embox alatt, és nem csak egy widgetet rajzolhatunk, hanem animációt is.

A Qt 4.8-at már régóta portolták az Emboxra, ezért úgy döntöttünk, hogy kipróbáljuk rajta. A Moveblocks alkalmazást választottuk – ez egy példa a ruganyos animációra.

Qt mozgásblokkok a QEMU-nQt portolása STM32-re

Először is beállítjuk a Qt-t, ha lehetséges, az animáció támogatásához szükséges minimális összetevőkészlettel. Ehhez van egy "-qconfig minimal,small,medium..." opció. Összekapcsolja a Qt konfigurációs fájlját sok makróval - mit kell engedélyezni / mit tiltani. Ezen opció után további jelzőket adunk a konfigurációhoz, ha valami mást szeretnénk letiltani. Íme egy példa a miénkről konfiguráció.

A Qt működéséhez hozzá kell adni egy operációs rendszer kompatibilitási réteget. Az egyik módja a QPA (Qt Platform Abstraction) megvalósítása. A Qt-ben található kész fb_base plugint vettük alapul, amely alapján működik a QPA for Linux. Az eredmény egy emboxfb nevű kis plugin, ami ellátja a Qt-t az Embox framebufferével, majd külső segítség nélkül oda is rajzol.

Így néz ki egy plugin létrehozása

QEmboxFbIntegration::QEmboxFbIntegration()
    : fontDb(new QGenericUnixFontDatabase())
{
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    const char *fbPath = "/dev/fb0";

    fbFd = open(fbPath, O_RDWR);
    if (fbPath < 0) {
        qFatal("QEmboxFbIntegration: Error open framebuffer %s", fbPath);
    }
    if (ioctl(fbFd, FBIOGET_FSCREENINFO, &finfo) == -1) {
        qFatal("QEmboxFbIntegration: Error ioctl framebuffer %s", fbPath);
    }
    if (ioctl(fbFd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
        qFatal("QEmboxFbIntegration: Error ioctl framebuffer %s", fbPath);
    }
    fbWidth        = vinfo.xres;
    fbHeight       = vinfo.yres;
    fbBytesPerLine = finfo.line_length;
    fbSize         = fbBytesPerLine * fbHeight;
    fbFormat       = vinfo.fmt;
    fbData = (uint8_t *)mmap(0, fbSize, PROT_READ | PROT_WRITE,
                             MAP_SHARED, fbFd, 0);
    if (fbData == MAP_FAILED) {
        qFatal("QEmboxFbIntegration: Error mmap framebuffer %s", fbPath);
    }
    if (!fbData || !fbSize) {
        qFatal("QEmboxFbIntegration: Wrong framebuffer: base = %p,"
               "size=%d", fbData, fbSize);
    }

    mPrimaryScreen = new QEmboxFbScreen(fbData, fbWidth,
                                        fbHeight, fbBytesPerLine,
                                        emboxFbFormatToQImageFormat(fbFormat));

    mPrimaryScreen->setPhysicalSize(QSize(fbWidth, fbHeight));
    mScreens.append(mPrimaryScreen);

    this->printFbInfo();
}

És így fog kinézni az újrarajzolás

QRegion QEmboxFbScreen::doRedraw()
{
    QVector<QRect> rects;
    QRegion touched = QFbScreen::doRedraw();

    DPRINTF("QEmboxFbScreen::doRedrawn");

    if (!compositePainter) {
        compositePainter = new QPainter(mFbScreenImage);
    }

    rects = touched.rects();
    for (int i = 0; i < rects.size(); i++) {
        compositePainter->drawImage(rects[i], *mScreenImage, rects[i]);
    }
    return touched;
}

Ennek eredményeként a fordító -Os memóriaméretre való optimalizálása mellett 3.5 MB-osnak bizonyult a könyvtár kép, ami természetesen nem fér bele az STM32F746 fő memóriájába. Ahogy azt az OpenCV-ről szóló másik cikkünkben már írtuk, ez a tábla a következőket tartalmazza:

  • 1 MB ROM
  • 320 KB RAM
  • 8 MB SDRAM
  • 16 MB QSPI

Mivel a QSPI kód ​​végrehajtásának támogatása már hozzáadásra került az OpenCV-hez, úgy döntöttünk, hogy kezdjük azzal, hogy betöltjük a teljes Embox c Qt képet a QSPI-be. És hurrá, szinte azonnal elkezdődött minden a QSPI-től! De mint az OpenCV esetében, kiderült, hogy túl lassan működik.

Qt portolása STM32-re

Ezért úgy döntöttünk, hogy így csináljuk - először a képet másoljuk a QSPI-be, majd betöltjük az SDRAM-ba, és onnan hajtjuk végre. SDRAM-ból kicsit gyorsabb lett, de még mindig messze van a QEMU-tól.

Qt portolása STM32-re

Ezután felmerült egy ötlet egy lebegőpontos beépítésre – elvégre a Qt elvégzi a négyzetek koordinátáinak számításait az animációban. Megpróbáltuk, de itt nem kaptunk látható gyorsulást, bár bent cikk A Qt fejlesztői azt állították, hogy az FPU jelentősen megnöveli a sebességet az „animáció húzásához” az érintőképernyőn. A mozgásblokkban lényegesen kevesebb lebegőpontos számítás lehet, és ez a konkrét példától függ.

A leghatékonyabb ötlet az volt, hogy a framebuffert az SDRAM-ból a belső memóriába helyezték át. Ennek érdekében a képernyő méreteit nem 480x272-re, hanem 272x272-re tettük. A színmélységet is csökkentettük A8R8G8B8-ról R5G6B5-re, így egy pixel méretét 4-ről 2 bájtra csökkentettük. A kapott framebuffer mérete 272 * 272 * 2 = 147968 bájt. Ez jelentős gyorsulást adott, talán a legszembetűnőbb, hogy szinte sima lett az animáció.

A legutóbbi optimalizálás az Embox kód futtatása volt a RAM-ból és a Qt kód az SDRAM-ból. Ehhez először a szokásos módon statikusan összekapcsoljuk az Emboxot a Qt-vel, de a könyvtár szövegét, rodata-ját, adatait és bss-szegmenseit elhelyezzük a QSPI-ben, hogy aztán az SDRAM-ba másoljuk.

section (qt_text, SDRAM, QSPI)
phdr	(qt_text, PT_LOAD, FLAGS(5))

section (qt_rodata, SDRAM, QSPI)
phdr	(qt_rodata, PT_LOAD, FLAGS(5))

section (qt_data, SDRAM, QSPI)
phdr	(qt_data, PT_LOAD, FLAGS(6))

section (qt_bss, SDRAM, QSPI)
phdr	(qt_bss, PT_LOAD, FLAGS(6))

Az Embox kód ROM-ból történő végrehajtásával is érezhető gyorsulást kaptunk. Ennek eredményeként az animáció meglehetősen sima lett:


A legvégén a cikk előkészítése és a különböző Embox konfigurációk kipróbálása közben derült ki, hogy a Qt moveblocks remekül működik QSPI-ből SDRAM-ban lévő framebufferrel, a szűk keresztmetszet pedig pont a framebuffer mérete volt! Nyilvánvalóan a kezdeti „diavetítés” leküzdéséhez elegendő volt a 2-szeres gyorsítás a keretpuffer méretének banális csökkentése miatt. De nem lehetett ilyen eredményt elérni, ha csak az Embox kódot vittük át különböző gyors memóriákra (a gyorsulás nem 2, hanem körülbelül 1.5-szeres volt).

Hogyan próbáld ki magad

Ha rendelkezik STM32F7-Discovery-vel, saját maga is futtathatja a Qt-t az Embox alatt. Ennek mikéntjét olvashatja nálunk вики.

Következtetés

Ennek eredményeként sikerült elindítani a Qt! A feladat összetettsége véleményünk szerint némileg eltúlzott. Természetesen figyelembe kell vennie a mikrokontrollerek sajátosságait, és általában meg kell értenie a számítógépes rendszerek architektúráját. Az optimalizálási eredmények rámutatnak arra a jól ismert tényre, hogy egy számítástechnikai rendszerben nem a processzor, hanem a memória jelenti a szűk keresztmetszetet.

Idén is részt veszünk a fesztiválon TechTrain. Ott részletesebben elmeséljük, és bemutatjuk a Qt-t, az OpenCV-t a mikrokontrollereken és egyéb eredményeinket.

Forrás: will.com

Hozzászólás