Prenos Qt na STM32

Prenos Qt na STM32Dobrý deň Sme v projekte Embox spustil Qt na STM32F7-Discovery a chcel by som o tom hovoriť. Už predtým sme povedali, ako sa nám podarilo spustiť OpenCV.

Qt je multiplatformový rámec, ktorý zahŕňa nielen grafické komponenty, ale aj také veci ako QtNetwork, sadu tried pre prácu s databázami, Qt for Automation (vrátane implementácie internetu vecí) a oveľa viac. Tím Qt bol aktívny pri používaní Qt vo vstavaných systémoch, takže knižnice sú celkom konfigurovateľné. Až donedávna však len málo ľudí premýšľalo o portovaní Qt na mikrokontroléry, pravdepodobne preto, že takáto úloha sa zdá náročná - Qt je veľké, MCU sú malé.

Na druhej strane, v súčasnosti existujú mikrokontroléry určené na prácu s multimédiami a lepšie ako prvé Pentiá. Asi pred rokom sa objavil blog Qt pošta. Vývojári vytvorili port Qt pre OS RTEMS a spustili príklady s widgetmi na niekoľkých doskách so systémom stm32f7. Toto nás zaujalo. Bolo badať a píšu o tom aj samotní vývojári, že Qt je na STM32F7-Discovery pomalé. Zaujímalo nás, či by sme mohli spustiť Qt pod Emboxom a nielen nakresliť widget, ale spustiť animáciu.

Qt 4.8 je už dlhší čas portovaný na Embox, tak sme sa rozhodli ho na ňom vyskúšať. Vybrali sme aplikáciu moveblocks - príklad pružnej animácie.

Pohyblivé bloky Qt na QEMUPrenos Qt na STM32

Na začiatok nakonfigurujeme Qt, ak je to možné, s minimálnou sadou komponentov potrebných na podporu animácie. Na tento účel existuje možnosť „-qconfig minimal,small,medium...“. Spája konfiguračný súbor z Qt s mnohými makrami - čo povoliť / čo zakázať. Po tejto voľbe pridáme do konfigurácie ďalšie príznaky, ak chceme zakázať niečo iné. Tu je príklad nášho konfigurácia.

Aby Qt fungovalo, musíte pridať vrstvu kompatibility OS. Jedným zo spôsobov je implementácia QPA (Qt Platform Abstraction). Ako základ sme vzali hotový doplnok fb_base zahrnutý v Qt, na základe ktorého funguje QPA pre Linux. Výsledkom je malý plugin s názvom emboxfb, ktorý poskytuje Qt framebuffer Emboxu a potom sa tam kreslí bez akejkoľvek vonkajšej pomoci.

Takto vyzerá vytvorenie 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 vyzerať prekreslenie

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ýsledkom bolo, že so zapnutou optimalizáciou kompilátora pre veľkosť pamäte -Os sa obraz knižnice ukázal ako 3.5 MB, čo sa samozrejme nezmestí do hlavnej pamäte STM32F746. Ako sme už písali v našom inom článku o OpenCV, táto doska má:

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

Keďže podpora pre spustenie kódu z QSPI už bola pridaná do OpenCV, rozhodli sme sa začať načítaním celého obrazu Embox c Qt do QSPI. A hurá, všetko začalo takmer okamžite od QSPI! Ale rovnako ako v prípade OpenCV sa ukázalo, že funguje príliš pomaly.

Prenos Qt na STM32

Preto sme sa rozhodli urobiť to takto - najprv skopírujeme obrázok do QSPI, potom ho načítame do SDRAM a odtiaľ spustíme. Z SDRAM sa to stalo trochu rýchlejšie, ale stále ďaleko od QEMU.

Prenos Qt na STM32

Ďalej bol nápad zahrnúť plávajúcu desatinnú čiarku - koniec koncov, Qt robí nejaké výpočty súradníc štvorcov v animácii. Snažili sme sa, ale tu sme sa nedočkali žiadneho viditeľného zrýchlenia, hoci v článok Vývojári Qt tvrdili, že FPU výrazne zvyšuje rýchlosť „ťahania animácie“ na dotykovej obrazovke. V pohyblivých blokoch môže byť výrazne menej výpočtov s pohyblivou rádovou čiarkou, a to závisí od konkrétneho príkladu.

Najúčinnejším nápadom bolo presunúť framebuffer z SDRAM do internej pamäte. Aby sme to dosiahli, vytvorili sme rozmery obrazovky nie 480x272, ale 272x272. Znížili sme aj farebnú hĺbku z A8R8G8B8 na R5G6B5, čím sme zmenšili veľkosť jedného pixelu zo 4 na 2 bajty. Výsledná veľkosť framebufferu je 272 * 272 * 2 = 147968 bajtov. To poskytlo výrazné zrýchlenie, možno najvýraznejšie, animácia sa stala takmer plynulou.

Poslednou optimalizáciou bolo spustenie kódu Embox z RAM a Qt kódu z SDRAM. Aby sme to urobili, najprv, ako obvykle, staticky prepojíme Embox spolu s Qt, ale textové, rodata, data a bss segmenty knižnice umiestnime do QSPI, aby sme ich potom skopí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))

Spustením Embox kódu z ROM sme sa dočkali aj citeľného zrýchlenia. V dôsledku toho bola animácia celkom hladká:


Na samom konci, pri príprave článku a skúšaní rôznych konfigurácií Emboxu, sa ukázalo, že Qt moveblocks funguje skvele od QSPI s framebufferom v SDRAM a prekážkou bola práve veľkosť framebufferu! Na prekonanie počiatočnej „slideshow“ zrejme stačilo 2-násobné zrýchlenie v dôsledku banálneho zníženia veľkosti framebufferu. Ale prenosom iba Embox kódu do rôznych rýchlych pamätí sa takýto výsledok dosiahnuť nedal (zrýchlenie nebolo 2, ale asi 1.5-násobné).

Ako to vyskúšať sami

Ak máte STM32F7-Discovery, môžete spustiť Qt pod Emboxom sami. Ako sa to robí, si môžete prečítať u nás wiki.

Záver

V dôsledku toho sa nám podarilo spustiť Qt! Zložitosť úlohy je podľa nášho názoru trochu prehnaná. Prirodzene, musíte brať do úvahy špecifiká mikrokontrolérov a vo všeobecnosti pochopiť architektúru počítačových systémov. Výsledky optimalizácie poukazujú na známy fakt, že prekážkou vo výpočtovom systéme nie je procesor, ale pamäť.

Tento rok sa festivalu zúčastníme TechTrain. Tam vám povieme podrobnejšie a ukážeme Qt, OpenCV na mikrokontroléroch a ďalšie naše úspechy.

Zdroj: hab.com

Pridať komentár