Prenos Qt na STM32

Prenos Qt na STM32Dober večer Smo v projektu Embox lansiral Qt na STM32F7-Discovery in bi o tem rad spregovoril. Prej smo že povedali, kako nam je uspelo zagnati OpenCV.

Qt je večplatformsko ogrodje, ki ne vključuje samo grafičnih komponent, temveč tudi stvari, kot je QtNetwork, niz razredov za delo z bazami podatkov, Qt za avtomatizacijo (vključno z implementacijo IoT) in še veliko več. Ekipa Qt je bila proaktivna glede uporabe Qt v vgrajenih sistemih, zato so knjižnice precej nastavljive. Vendar je do nedavnega le malo ljudi razmišljalo o prenosu Qt na mikrokontrolerje, verjetno zato, ker se zdi takšna naloga težka - Qt je velik, MCU-ji so majhni.

Po drugi strani pa trenutno obstajajo mikrokrmilniki, ki so zasnovani za delo z multimedijo in so boljši od prvih Pentiumov. Pred približno letom dni se je pojavil Qt blog post. Razvijalci so naredili vrata Qt za RTEMS OS in lansirali primere s pripomočki na več ploščah, ki izvajajo stm32f7. To nas je zanimalo. Opaziti je bilo in razvijalci sami pišejo o tem, da je Qt počasen na STM32F7-Discovery. Spraševali smo se, ali bi lahko zagnali Qt pod Emboxom in ne samo narisali pripomočka, ampak zagnali animacijo.

Qt 4.8 je že dolgo časa prenesen na Embox, zato smo se odločili, da ga preizkusimo na njem. Izbrali smo aplikacijo moveblocks - primer pomladne animacije.

Qt moveblocks na QEMUPrenos Qt na STM32

Za začetek konfiguriramo Qt, če je mogoče, z minimalnim naborom komponent, potrebnih za podporo animacije. Za to obstaja možnost "-qconfig minimalno, majhno, srednje ...". Povezuje konfiguracijsko datoteko iz Qt s številnimi makri - kaj omogočiti / kaj onemogočiti. Po tej možnosti konfiguraciji dodamo še druge zastavice, če želimo onemogočiti še kaj. Tukaj je naš primer konfiguracijo.

Da bo Qt deloval, morate dodati plast združljivosti OS. Eden od načinov je implementacija QPA (Qt Platform Abstraction). Za osnovo smo vzeli že pripravljen vtičnik fb_base, vključen v Qt, na podlagi katerega deluje QPA za Linux. Rezultat je majhen vtičnik, imenovan emboxfb, ki Qt-u zagotovi okvirni medpomnilnik Embox, nato pa tja riše brez zunanje pomoči.

Takole izgleda ustvarjanje vtičnika

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

In takole bo videti prerisovanje

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

Kot rezultat, z omogočeno optimizacijo prevajalnika za velikost pomnilnika -Os, se je slika knjižnice izkazala za 3.5 MB, kar seveda ne ustreza glavnemu pomnilniku STM32F746. Kot smo že pisali v našem drugem članku o OpenCV, ima ta plošča:

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

Ker je bila OpenCV že dodana podpora za izvajanje kode iz QSPI, smo se odločili, da začnemo z nalaganjem celotne slike Embox c Qt v QSPI. In hura, vse se je začelo skoraj takoj iz QSPI! A kot v primeru OpenCV se je izkazalo, da deluje prepočasi.

Prenos Qt na STM32

Zato smo se odločili, da to storimo na ta način - najprej kopiramo sliko v QSPI, nato jo naložimo v SDRAM in izvedemo od tam. Iz SDRAM-a je postal nekoliko hitrejši, a še vedno daleč od QEMU.

Prenos Qt na STM32

Nato se je pojavila ideja, da bi vključili plavajočo vejico - navsezadnje Qt naredi nekaj izračunov koordinat kvadratov v animaciji. Poskušali, a tukaj nismo dobili vidnejših pospeškov, čeprav v članek Razvijalci Qt so trdili, da FPU znatno poveča hitrost "vlečenja animacije" na zaslonu na dotik. V premikajočih blokih je lahko bistveno manj izračunov s plavajočo vejico, kar je odvisno od konkretnega primera.

Najučinkovitejša ideja je bila premakniti okvirni medpomnilnik iz SDRAM v notranji pomnilnik. Da bi to naredili, smo naredili dimenzije zaslona ne 480x272, ampak 272x272. Zmanjšali smo tudi barvno globino z A8R8G8B8 na R5G6B5 in tako zmanjšali velikost ene slikovne pike s 4 na 2 bajta. Nastala velikost medpomnilnika okvirjev je 272 * 272 * 2 = 147968 bajtov. To je znatno pospešilo, morda najbolj opazno je, da je animacija postala skoraj gladka.

Najnovejša optimizacija je bila zagon kode Embox iz RAM-a in kode Qt iz SDRAM-a. Da bi to naredili, najprej, kot običajno, statično povežemo Embox skupaj s Qt, vendar segmente besedila, rodata, podatkov in bss knjižnice postavimo v QSPI, da jih nato kopiramo v 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))

Z izvajanjem kode Embox iz ROM-a smo bili deležni tudi opaznega pospeška. Posledično se je animacija izkazala za precej gladko:


Čisto na koncu, med pripravo članka in preizkušanjem različnih Embox konfiguracij, se je izkazalo, da Qt moveblocks odlično deluje iz QSPI z framebufferjem v SDRAM, ozko grlo pa je bila ravno velikost framebufferja! Očitno je bilo za premagovanje začetne "diaprojekcije" dovolj 2-kratni pospešek zaradi banalnega zmanjšanja velikosti medpomnilnika okvirjev. Toda takšnega rezultata ni bilo mogoče doseči s prenosom samo kode Embox v različne hitre pomnilnike (pospešek ni bil 2, ampak približno 1.5-krat).

Kako poskusiti sami

Če imate STM32F7-Discovery, lahko sami zaženete Qt pod Emboxom. Kako to poteka, si lahko preberete na našem wiki.

Zaključek

Kot rezultat nam je uspelo zagnati Qt! Kompleksnost naloge je po našem mnenju nekoliko pretirana. Seveda morate upoštevati posebnosti mikrokontrolerjev in na splošno razumeti arhitekturo računalniških sistemov. Rezultati optimizacije kažejo na dobro znano dejstvo, da ozko grlo v računalniškem sistemu ni procesor, temveč pomnilnik.

Letos se bomo festivala udeležili TechTrain. Tam vam bomo podrobneje povedali in pokazali Qt, OpenCV na mikrokontrolerjih in druge naše dosežke.

Vir: www.habr.com

Dodaj komentar