Prijenos Qt-a na STM32

Prijenos Qt-a na STM32Dobar dan U projektu smo Embox lansirao Qt na STM32F7-Discovery i želio bi razgovarati o tome. Ranije smo već rekli kako smo uspjeli pokrenuti OpenCV.

Qt je međuplatformski okvir koji uključuje ne samo grafičke komponente, već i stvari kao što su QtNetwork, skup klasa za rad s bazama podataka, Qt za automatizaciju (uključujući implementaciju IoT-a) i još mnogo toga. Qt tim bio je proaktivan u korištenju Qt-a u ugrađenim sustavima, tako da su biblioteke prilično konfigurabilne. Međutim, donedavno je malo ljudi razmišljalo o prijenosu Qt-a na mikrokontrolere, vjerojatno zato što se takav zadatak čini teškim - Qt je velik, MCU-ovi su mali.

S druge strane, u ovom trenutku postoje mikrokontroleri dizajnirani za rad s multimedijom i superiorniji od prvih Pentiuma. Prije otprilike godinu dana pojavio se Qt blog pošta. Programeri su napravili priključak Qt-a za RTEMS OS i pokrenuli primjere s widgetima na nekoliko ploča koje pokreću stm32f7. Ovo nas je zanimalo. Bilo je vidljivo, a o tome pišu i sami programeri, da je Qt spor na STM32F7-Discovery. Pitali smo se možemo li pokrenuti Qt pod Emboxom, a ne samo nacrtati widget, već pokrenuti animaciju.

Qt 4.8 je već duže vrijeme portiran na Embox pa smo ga odlučili isprobati na njemu. Odabrali smo aplikaciju moveblocks - primjer elastične animacije.

Qt moveblocks na QEMUPrijenos Qt-a na STM32

Za početak, konfiguriramo Qt, ako je moguće, s minimalnim skupom komponenti potrebnih za podršku animaciji. Za to postoji opcija “-qconfig minimalno, malo, srednje...”. Povezuje konfiguracijsku datoteku iz Qt-a s mnogo makronaredbi - što omogućiti / što onemogućiti. Nakon ove opcije, dodajemo druge zastavice u konfiguraciju ako želimo onemogućiti nešto drugo. Evo primjera našeg konfiguracija.

Kako bi Qt radio, trebate dodati sloj kompatibilnosti OS-a. Jedan od načina je implementacija QPA (Qt Platform Abstraction). Kao osnovu uzeli smo gotov fb_base dodatak uključen u Qt, na temelju kojeg radi QPA za Linux. Rezultat je mali dodatak nazvan emboxfb, koji daje Qt-u Emboxov okvirni međuspremnik, a zatim crta tamo bez ikakve vanjske pomoći.

Ovako izgleda stvaranje dodatka

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 ovako će izgledati precrtavanje

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

Kao rezultat toga, s omogućenom optimizacijom kompajlera za veličinu memorije -Os, slika knjižnice se pokazala kao 3.5 MB, što naravno ne stane u glavnu memoriju STM32F746. Kao što smo već napisali u našem drugom članku o OpenCV-u, ova ploča ima:

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

Budući da je podrška za izvršavanje koda iz QSPI-ja već dodana u OpenCV, odlučili smo započeti učitavanjem cijele Embox c Qt slike u QSPI. I hura, sve je krenulo gotovo odmah od QSPI-ja! No, kao iu slučaju OpenCV-a pokazalo se da radi presporo.

Prijenos Qt-a na STM32

Stoga smo odlučili to učiniti na ovaj način - prvo kopiramo sliku u QSPI, zatim je učitamo u SDRAM i izvršimo od tamo. Od SDRAM-a postao je malo brži, ali još uvijek daleko od QEMU-a.

Prijenos Qt-a na STM32

Zatim, postojala je ideja da se uključi pokretni zarez - uostalom, Qt radi neke izračune koordinata kvadrata u animaciji. Pokušali smo, ali ovdje nismo dobili nikakvo vidljivo ubrzanje, iako u članak Qt programeri su tvrdili da FPU daje značajno povećanje brzine za "povlačenje animacije" na zaslonu osjetljivom na dodir. U poteznim blokovima može biti znatno manje izračuna s pomičnim zarezom, a to ovisi o konkretnom primjeru.

Najučinkovitija ideja bila je premjestiti okvirni međuspremnik iz SDRAM-a u internu memoriju. Da bismo to učinili, napravili smo dimenzije zaslona ne 480x272, već 272x272. Također smo smanjili dubinu boje s A8R8G8B8 na R5G6B5, čime smo smanjili veličinu jednog piksela s 4 na 2 bajta. Rezultirajuća veličina međuspremnika okvira je 272 * 272 * 2 = 147968 bajtova. To je dalo značajno ubrzanje, možda najuočljivije, animacija je postala gotovo glatka.

Najnovija optimizacija bila je pokretanje Embox koda iz RAM-a i Qt koda iz SDRAM-a. Da bismo to učinili, prvo, kao i obično, statički povezujemo Embox zajedno s Qt-om, ali stavljamo tekst, rodata, podatke i bss segmente biblioteke u QSPI kako bismo ga zatim kopirali u 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))

Izvršavanjem Embox koda iz ROM-a također smo dobili osjetno ubrzanje. Kao rezultat toga, animacija je ispala prilično glatka:


Na samom kraju, tijekom pripreme članka i isprobavanja različitih Embox konfiguracija, pokazalo se da Qt moveblocks odlično radi iz QSPI-ja s framebufferom u SDRAM-u, a usko grlo je bila upravo veličina framebuffera! Očigledno, za prevladavanje početnog "slideshowa" bilo je dovoljno dvostruko ubrzanje zbog banalnog smanjenja veličine okvira međuspremnika. Ali nije bilo moguće postići takav rezultat prijenosom samo Embox koda u razne brze memorije (ubrzanje nije bilo 2, već oko 2 puta).

Kako to sami isprobati

Ako imate STM32F7-Discovery, možete sami pokrenuti Qt pod Emboxom. Kako se to radi možete pročitati na našem wiki.

Zaključak

Kao rezultat, uspjeli smo pokrenuti Qt! Složenost zadatka, po našem mišljenju, donekle je pretjerana. Naravno, morate uzeti u obzir specifičnosti mikrokontrolera i općenito razumjeti arhitekturu računalnih sustava. Rezultati optimizacije ukazuju na dobro poznatu činjenicu da usko grlo računalnog sustava nije procesor, već memorija.

Ove godine ćemo sudjelovati na festivalu TechTrain. Tamo ćemo vam detaljnije reći i pokazati Qt, OpenCV na mikrokontrolerima i druga naša postignuća.

Izvor: www.habr.com

Dodajte komentar