Portování Qt na STM32

Portování Qt na STM32Dobré odpoledne Jsme v projektu Embox spustil Qt na STM32F7-Discovery a rád by o tom promluvil. Již dříve jsme řekli, jak se nám podařilo spustit OpenCV.

Qt je multiplatformní framework, který zahrnuje nejen grafické komponenty, ale také takové věci, jako je QtNetwork, sada tříd pro práci s databázemi, Qt for Automation (včetně implementace IoT) a mnoho dalšího. Tým Qt byl proaktivní při používání Qt ve vestavěných systémech, takže knihovny jsou docela konfigurovatelné. Až donedávna však o portování Qt na mikrokontroléry uvažovalo jen málo lidí, pravděpodobně proto, že se takový úkol zdá obtížný – Qt je velké, MCU malé.

Na druhou stranu v současnosti existují mikrokontroléry určené pro práci s multimédii a lepší než první Pentia. Asi před rokem se objevil blog Qt zveřejnit. Vývojáři vytvořili port Qt pro RTEMS OS a spustili příklady s widgety na několika deskách běžících na stm32f7. Tohle nás zaujalo. Bylo patrné a sami vývojáři o tom píší, že Qt je na STM32F7-Discovery pomalé. Přemýšleli jsme, jestli bychom mohli spustit Qt pod Emboxem a nejen nakreslit widget, ale spustit animaci.

Qt 4.8 je již delší dobu portován na Embox, a tak jsme se rozhodli jej na něm vyzkoušet. Vybrali jsme aplikaci moveblocks - příklad pružné animace.

Pohyblivé bloky Qt na QEMUPortování Qt na STM32

Pro začátek nakonfigurujeme Qt, pokud je to možné, s minimální sadou komponent nutných pro podporu animace. K tomu existuje možnost „-qconfig minimální, malé, střední...“. Propojuje konfigurační soubor z Qt s mnoha makry - co povolit / co zakázat. Po této volbě přidáme do konfigurace další příznaky, pokud chceme zakázat něco jiného. Zde je příklad našeho konfigurace.

Aby Qt fungovalo, musíte přidat vrstvu kompatibility OS. Jedním ze způsobů je implementace QPA (Qt Platform Abstraction). Jako základ jsme vzali hotový plugin fb_base obsažený v Qt, na jehož základě QPA pro Linux funguje. Výsledkem je malý plugin nazvaný emboxfb, který poskytuje Qt framebuffer Emboxu, a pak tam kreslí bez cizí pomoci.

Takto vypadá vytvoření pluginu

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();
}

A takto bude vypadat překreslení

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;
}

Výsledkem bylo, že se zapnutou optimalizací kompilátoru pro velikost paměti -Os se obraz knihovny ukázal jako 3.5 MB, což se samozřejmě nevejde do hlavní paměti STM32F746. Jak jsme již psali v našem jiném článku o OpenCV, tato deska má:

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

Protože podpora pro spouštění kódu z QSPI již byla do OpenCV přidána, rozhodli jsme se začít načtením celého obrazu Embox c Qt do QSPI. A hurá, vše začalo téměř okamžitě od QSPI! Ale stejně jako v případě OpenCV se ukázalo, že funguje příliš pomalu.

Portování Qt na STM32

Proto jsme se rozhodli to udělat takto – nejprve obrázek zkopírujeme do QSPI, poté jej nahrajeme do SDRAM a odtud spustíme. Z SDRAM se to stalo trochu rychlejší, ale stále daleko od QEMU.

Portování Qt na STM32

Dále byl nápad zahrnout plovoucí desetinnou čárku - koneckonců Qt provádí nějaké výpočty souřadnic čtverců v animaci. Snažili jsme se, ale tady jsme se žádného viditelného zrychlení nedočkali, i když v článek Vývojáři Qt tvrdili, že FPU poskytuje výrazné zvýšení rychlosti pro „tažení animace“ na dotykové obrazovce. V pohyblivých blocích může být výrazně méně výpočtů s pohyblivou řádovou čárkou, a to závisí na konkrétním příkladu.

Nejúčinnějším nápadem bylo přesunout framebuffer z SDRAM do vnitřní paměti. K tomu jsme udělali rozměry obrazovky ne 480x272, ale 272x272. Snížili jsme také barevnou hloubku z A8R8G8B8 na R5G6B5, čímž jsme snížili velikost jednoho pixelu ze 4 na 2 bajty. Výsledná velikost framebufferu je 272 * 272 * 2 = 147968 bajtů. Tím došlo k výraznému zrychlení, možná nejvýrazněji se animace stala téměř plynulou.

Poslední optimalizací bylo spuštění kódu Embox z RAM a Qt kódu z SDRAM. Abychom to udělali, nejprve, jako obvykle, staticky propojíme Embox dohromady s Qt, ale textové, rodata, data a bss segmenty knihovny umístíme do QSPI, abychom je pak zkopírovali do SDRAM.

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))

Spuštěním kódu Embox z ROM jsme se dočkali i znatelného zrychlení. V důsledku toho se animace ukázala docela hladká:


Na úplném konci, při přípravě článku a zkoušení různých konfigurací Emboxu, se ukázalo, že Qt moveblocks fungují skvěle od QSPI s framebufferem v SDRAM a úzkým hrdlem byla právě velikost framebufferu! K překonání počáteční „slideshow“ zřejmě stačilo 2-násobné zrychlení kvůli banálnímu snížení velikosti framebufferu. Ale takového výsledku nebylo možné dosáhnout přenesením pouze Embox kódu do různých rychlých pamětí (zrychlení nebylo 2, ale cca 1.5x).

Jak to zkusit sami

Pokud máte STM32F7-Discovery, můžete sami spustit Qt pod Emboxem. Jak se to dělá, si můžete přečíst u nás wiki.

Závěr

V důsledku toho se nám podařilo spustit Qt! Složitost úkolu je podle nás poněkud přehnaná. Přirozeně je potřeba vzít v úvahu specifika mikrokontrolérů a obecně rozumět architektuře počítačových systémů. Výsledky optimalizace poukazují na známý fakt, že úzkým hrdlem výpočetního systému není procesor, ale paměť.

Letos se festivalu zúčastníme TechTrain. Tam vám to řekneme podrobněji a ukážeme Qt, OpenCV na mikrokontrolérech a naše další úspěchy.

Zdroj: www.habr.com

Přidat komentář