Prenos Qt-a na STM32

Prenos Qt-a na STM32Dobar dan Mi smo u projektu Embox pokrenuo Qt na STM32F7-Discovery i želio bih razgovarati o tome. Ranije smo već rekli kako smo uspeli da lansiramo OpenCV.

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

S druge strane, trenutno postoje mikrokontroleri dizajnirani za rad sa multimedijom i superiorniji od prvih Pentiuma. Prije otprilike godinu dana pojavio se Qt blog post. Programeri su napravili port za Qt za RTEMS OS, i lansirali primjere sa widgetima na nekoliko ploča koje koriste stm32f7. Ovo nas je zanimalo. Bilo je primjetno, a i sami programeri pišu o tome, da je Qt spor na STM32F7-Discovery. Pitali smo se možemo li pokrenuti Qt pod Embox-om, a ne samo crtati widget, već pokrenuti animaciju.

Qt 4.8 je već dugo portiran u Embox, pa smo odlučili da ga isprobamo na njemu. Odabrali smo aplikaciju moveblocks - primjer opružne animacije.

Qt pokretni blokovi na QEMUPrenos Qt-a na STM32

Za početak, konfigurišemo Qt, ako je moguće, sa minimalnim skupom komponenti potrebnih za podršku animacije. Za ovo postoji opcija “-qconfig minimalno,malo,srednje...”. Povezuje konfiguracionu datoteku iz Qt-a sa mnogim makroima - šta omogućiti/šta onemogućiti. Nakon ove opcije dodajemo druge zastavice u konfiguraciju ako želimo da onemogućimo nešto drugo. Evo primjera našeg konfiguracija.

Da bi Qt radio, potrebno je da dodate sloj kompatibilnosti sa operativnim sistemom. Jedan od načina je implementacija QPA (Qt Platform Abstraction). Za osnovu smo uzeli gotovi dodatak fb_base uključen u Qt, na osnovu kojeg radi QPA za Linux. Rezultat je mali dodatak koji se zove emboxfb, koji Qt-u obezbjeđuje Emboxov framebuffer, a zatim ga crta bez ikakve pomoći izvana.

Ovako izgleda kreiranje 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, sa omogućenom optimizacijom kompajlera za veličinu memorije -Os, slika biblioteke je bila 3.5 MB, što se naravno ne uklapa u glavnu memoriju STM32F746. Kao što smo već pisali u našem drugom članku o OpenCV-u, ova ploča ima:

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

Pošto je podrška za izvršavanje koda iz QSPI već dodata OpenCV-u, odlučili smo da počnemo učitavanjem cijele Embox c Qt slike u QSPI. I ura, sve je počelo skoro odmah od QSPI! Ali kao iu slučaju OpenCV-a, pokazalo se da radi presporo.

Prenos Qt-a na STM32

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

Prenos Qt-a na STM32

Zatim je postojala ideja da se uključi plutajući zarez - na kraju krajeva, Qt radi neke kalkulacije 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 ekranu osetljivom na dodir. Može biti znatno manje izračunavanja s pokretnim zarezom u blokovima pokreta, a to ovisi o konkretnom primjeru.

Najefikasnija ideja je bila premjestiti framebuffer iz SDRAM-a u internu memoriju. Da bismo to učinili, napravili smo dimenzije ekrana ne 480x272, već 272x272. Takođe smo smanjili dubinu boje sa A8R8G8B8 na R5G6B5, čime smo smanjili veličinu jednog piksela sa 4 na 2 bajta. Rezultirajuća veličina bafera okvira je 272 * 272 * 2 = 147968 bajtova. Ovo je dalo značajno ubrzanje, možda najuočljivije, animacija je postala gotovo glatka.

Najnovija optimizacija je bila pokretanje Embox koda iz RAM-a i Qt koda iz SDRAM-a. Da bismo to uradili, prvo, kao i obično, statički povezujemo Embox zajedno sa Qt-om, ali tekst, rodata, podatke i bss segmente biblioteke stavljamo u QSPI da bismo ih 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 primjetno ubrzanje. Kao rezultat toga, animacija je ispala prilično glatka:


Na samom kraju, pripremajući članak i isprobavajući različite Embox konfiguracije, pokazalo se da Qt moveblocks odlično radi od QSPI sa framebufferom u SDRAM-u, a usko grlo je bila upravo veličina framebuffera! Očigledno, da bi se prevladao početni "slideshow", dvostruko ubrzanje bilo je dovoljno zbog banalnog smanjenja veličine framebuffera. 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 da probate sami

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

zaključak

Kao rezultat toga, uspjeli smo pokrenuti Qt! Složenost zadatka je, po našem mišljenju, pomalo preuveličana. Naravno, morate uzeti u obzir specifičnosti mikrokontrolera i općenito razumjeti arhitekturu računarskih sistema. Rezultati optimizacije ukazuju na dobro poznatu činjenicu da usko grlo u računarskom sistemu nije procesor, već memorija.

Ove godine ćemo učestvovati na festivalu TechTrain. Tamo ćemo vam reći detaljnije i pokazati Qt, OpenCV na mikrokontrolerima i naša druga dostignuća.

izvor: www.habr.com

Dodajte komentar