Porterer Qt til STM32

Porterer Qt til STM32God ettermiddag Vi er med i prosjektet embox lanserte Qt på STM32F7-Discovery og vil gjerne snakke om det. Tidligere har vi allerede fortalt hvordan vi klarte å lansere OpenCV.

Qt er et rammeverk på tvers av plattformer som inkluderer ikke bare grafiske komponenter, men også ting som QtNetwork, et sett med klasser for å jobbe med databaser, Qt for Automation (inkludert for IoT-implementering) og mye mer. Qt-teamet har vært proaktive med å bruke Qt i innebygde systemer, så bibliotekene er ganske konfigurerbare. Men inntil nylig var det få som tenkte på å portere Qt til mikrokontrollere, sannsynligvis fordi en slik oppgave virker vanskelig - Qt er stor, MCU-er er små.

På den annen side, for øyeblikket er det mikrokontrollere designet for å fungere med multimedia og overlegne de første Pentiums. For omtrent et år siden dukket Qt-bloggen opp post. Utviklerne laget en port av Qt for RTEMS OS, og lanserte eksempler med widgets på flere brett som kjører stm32f7. Dette interesserte oss. Det var merkbart, og utviklerne skriver selv om det, at Qt er treg på STM32F7-Discovery. Vi lurte på om vi kunne kjøre Qt under Embox, og ikke bare tegne en widget, men kjøre en animasjon.

Qt 4.8 har blitt portert til Embox i lang tid, så vi bestemte oss for å prøve den på den. Vi valgte moveblocks-applikasjonen - et eksempel på spenstig animasjon.

Qt moveblocks på QEMUPorterer Qt til STM32

Til å begynne med konfigurerer vi Qt, hvis mulig, med minimumssettet med komponenter som kreves for å støtte animasjon. For dette er det et alternativ "-qconfig minimal,small, medium...". Den kobler sammen en konfigurasjonsfil fra Qt med mange makroer - hva du skal aktivere / hva du skal deaktivere. Etter dette alternativet legger vi til andre flagg i konfigurasjonen hvis vi ønsker å deaktivere noe annet. Her er et eksempel på vår konfigurasjon.

For at Qt skal fungere, må du legge til et OS-kompatibilitetslag. En måte er å implementere QPA (Qt Platform Abstraction). Vi tok utgangspunkt i den ferdige fb_base-pluginen som er inkludert i Qt, som QPA for Linux fungerer på grunnlag av. Resultatet er en liten plugin kalt emboxfb, som forsyner Qt med Embox sin framebuffer, og så trekker den dit uten hjelp utenfra.

Slik ser det ut å lage en plugin

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

Og slik vil omtegningen se ut

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

Som et resultat, med kompilatoroptimalisering for minnestørrelse -Os aktivert, viste bibliotekbildet seg å være 3.5 MB, noe som selvfølgelig ikke passer inn i hovedminnet til STM32F746. Som vi allerede skrev i vår andre artikkel om OpenCV, har dette brettet:

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

Siden støtte for utførelse av kode fra QSPI allerede er lagt til OpenCV, bestemte vi oss for å starte med å laste hele Embox c Qt-bildet inn i QSPI. Og hurra, alt startet nesten umiddelbart fra QSPI! Men som i tilfellet med OpenCV, viste det seg at det fungerer for sakte.

Porterer Qt til STM32

Derfor bestemte vi oss for å gjøre det på denne måten - først kopierer vi bildet til QSPI, laster det så inn i SDRAM og kjører derfra. Fra SDRAM ble det litt raskere, men fortsatt langt fra QEMU.

Porterer Qt til STM32

Deretter var det en ide om å inkludere et flytende komma - tross alt gjør Qt noen beregninger av koordinatene til kvadrater i animasjon. Vi prøvde, men her fikk vi ingen synlig akselerasjon, selv om inn artikkel Qt-utviklere hevdet at FPU gir en betydelig økning i hastighet for å "dra animasjon" på berøringsskjermen. Det kan være betydelig færre flyttallsberegninger i moveblocks, og dette avhenger av det spesifikke eksemplet.

Den mest effektive ideen var å flytte rammebufferen fra SDRAM til internminnet. For å gjøre dette laget vi ikke skjermdimensjonene 480x272, men 272x272. Vi senket også fargedybden fra A8R8G8B8 til R5G6B5, og reduserte dermed størrelsen på én piksel fra 4 til 2 byte. Den resulterende rammebufferstørrelsen er 272 * 272 * 2 = 147968 byte. Dette ga en betydelig akselerasjon, kanskje mest merkbart, animasjonen ble nesten jevn.

Den siste optimaliseringen var å kjøre Embox-kode fra RAM og Qt-kode fra SDRAM. For å gjøre dette kobler vi først, som vanlig, Embox statisk sammen med Qt, men vi plasserer tekst-, rodata-, data- og bss-segmentene til biblioteket i QSPI for deretter å kopiere det til 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))

Ved å kjøre Embox-koden fra ROM fikk vi også en merkbar akselerasjon. Som et resultat viste animasjonen seg ganske jevn:


Helt på slutten, mens du forberedte artikkelen og prøvde forskjellige Embox-konfigurasjoner, viste det seg at Qt moveblocks fungerer utmerket fra QSPI med en framebuffer i SDRAM, og flaskehalsen var akkurat på størrelse med framebufferen! Tilsynelatende, for å overvinne den første "lysbildefremvisningen", var en 2-dobbel akselerasjon nok på grunn av en banal reduksjon i størrelsen på rammebufferen. Men det var ikke mulig å oppnå et slikt resultat ved å overføre bare Embox-koden til forskjellige raske minner (akselerasjonen var ikke 2, men omtrent 1.5 ganger).

Hvordan prøve det selv

Hvis du har en STM32F7-Discovery, kan du kjøre Qt under Embox selv. Du kan lese hvordan dette gjøres på vår wiki.

Konklusjon

Som et resultat klarte vi å lansere Qt! Kompleksiteten i oppgaven er etter vår mening noe overdrevet. Naturligvis må du ta hensyn til detaljene til mikrokontrollere og generelt forstå arkitekturen til datasystemer. Optimaliseringsresultatene peker på det velkjente faktum at flaskehalsen i et datasystem ikke er prosessoren, men minnet.

I år skal vi delta på festivalen TechTrain. Der vil vi fortelle deg mer detaljert og vise Qt, OpenCV på mikrokontrollere og våre andre prestasjoner.

Kilde: www.habr.com

Legg til en kommentar