Portering af Qt til STM32

Portering af Qt til STM32God eftermiddag Vi er med i projektet Embox lancerede Qt på STM32F7-Discovery og vil gerne tale om det. Tidligere fortalte vi allerede, hvordan det lykkedes at lancere OpenCV.

Qt er et rammeværk på tværs af platforme, der ikke kun omfatter grafiske komponenter, men også ting som QtNetwork, et sæt klasser til at arbejde med databaser, Qt for Automation (inklusive til IoT-implementering) og meget mere. Qt-teamet har været proaktivt med at bruge Qt i indlejrede systemer, så bibliotekerne er ret konfigurerbare. Indtil for nylig har de færreste dog tænkt på at overføre Qt til mikrocontrollere, sandsynligvis fordi en sådan opgave virker vanskelig - Qt er stor, MCU'er er små.

På den anden side er der i øjeblikket mikrocontrollere designet til at arbejde med multimedier og er bedre end de første Pentiums. For omkring et år siden dukkede Qt-bloggen op indlæg. Udviklerne lavede en port af Qt til RTEMS OS og lancerede eksempler med widgets på flere boards, der kører stm32f7. Dette interesserede os. Det var bemærkelsesværdigt, og udviklerne skriver selv om det, at Qt er langsom på STM32F7-Discovery. Vi spekulerede på, om vi kunne køre Qt under Embox, og ikke bare tegne en widget, men køre en animation.

Qt 4.8 er blevet overført til Embox i lang tid, så vi besluttede at prøve det på det. Vi valgte moveblocks-applikationen - et eksempel på fjedrende animation.

Qt moveblocks på QEMUPortering af Qt til STM32

Til at begynde med konfigurerer vi Qt, hvis det er muligt, med det minimumssæt af komponenter, der kræves for at understøtte animation. Til dette er der muligheden "-qconfig minimal,small, medium...". Den forbinder en konfigurationsfil fra Qt med mange makroer - hvad skal aktiveres / hvad skal deaktiveres. Efter denne mulighed tilføjer vi andre flag til konfigurationen, hvis vi vil deaktivere noget andet. Her er et eksempel på vores konfiguration.

For at Qt kan fungere, skal du tilføje et OS-kompatibilitetslag. En måde er at implementere QPA (Qt Platform Abstraction). Vi tog udgangspunkt i det færdiglavede fb_base-plugin, der er inkluderet i Qt, og som QPA til Linux fungerer på grundlag af. Resultatet er et lille plugin kaldet emboxfb, som forsyner Qt med Embox's framebuffer, og så trækker det derhen uden hjælp udefra.

Sådan ser det ud at oprette et 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 sådan kommer gentegningen til at se ud

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 compiler-optimeringen for hukommelsesstørrelse -Os aktiveret, viste biblioteksbilledet sig at være 3.5 MB, hvilket naturligvis ikke passer ind i hovedhukommelsen på STM32F746. Som vi allerede skrev i vores anden artikel om OpenCV, har dette board:

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

Da understøttelse af eksekvering af kode fra QSPI allerede er tilføjet til OpenCV, besluttede vi at starte med at indlæse hele Embox c Qt-billedet i QSPI. Og hurra, alt startede næsten med det samme fra QSPI! Men som i tilfældet med OpenCV viste det sig, at det virker for langsomt.

Portering af Qt til STM32

Derfor besluttede vi at gøre det på denne måde - først kopierer vi billedet til QSPI, indlæser det derefter i SDRAM og udfører derfra. Fra SDRAM blev det lidt hurtigere, men stadig langt fra QEMU.

Portering af Qt til STM32

Dernæst var der en idé om at inkludere et flydende komma - Qt laver trods alt nogle beregninger af kvadraters koordinater i animation. Vi forsøgte, men her fik vi ikke nogen synlig acceleration, selvom vi var i artiklen Qt-udviklere hævdede, at FPU giver en betydelig stigning i hastigheden for at "trække animation" på touchscreen. Der kan være væsentligt færre floating point-beregninger i moveblocks, og det afhænger af det konkrete eksempel.

Den mest effektive idé var at flytte framebufferen fra SDRAM til intern hukommelse. For at gøre dette lavede vi skærmdimensionerne ikke 480x272, men 272x272. Vi sænkede også farvedybden fra A8R8G8B8 til R5G6B5 og reducerede dermed størrelsen på en pixel fra 4 til 2 bytes. Den resulterende rammebufferstørrelse er 272 * 272 * 2 = 147968 bytes. Dette gav en betydelig acceleration, måske mest bemærkelsesværdigt, animationen blev næsten jævn.

Den seneste optimering var at køre Embox-kode fra RAM og Qt-kode fra SDRAM. For at gøre dette kobler vi først som sædvanligt Embox statisk sammen med Qt, men vi placerer tekst-, rodata-, data- og bss-segmenterne af biblioteket i QSPI for derefter at 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 at udføre Embox-koden fra ROM fik vi også en mærkbar acceleration. Som et resultat viste animationen sig ret glat:


Til allersidst, mens man forberedte artiklen og prøvede forskellige Embox-konfigurationer, viste det sig, at Qt moveblocks fungerer fantastisk fra QSPI med en framebuffer i SDRAM, og flaskehalsen var præcis på størrelse med framebufferen! Tilsyneladende var en 2-dobbelt acceleration nok for at overvinde det indledende "diasshow" på grund af en banal reduktion i størrelsen af ​​rammebufferen. Men det var ikke muligt at opnå et sådant resultat ved kun at overføre Embox-koden til forskellige hurtige hukommelser (hastigheden var ikke 2, men omkring 1.5 gange).

Sådan prøver du det selv

Hvis du har en STM32F7-Discovery, kan du selv køre Qt under Embox. Du kan læse hvordan det foregår på vores wiki.

Konklusion

Som et resultat lykkedes det at lancere Qt! Opgavens kompleksitet er efter vores mening noget overdrevet. Naturligvis skal du tage højde for detaljerne ved mikrocontrollere og generelt forstå arkitekturen i computersystemer. Optimeringsresultaterne peger på det velkendte faktum, at flaskehalsen i et computersystem ikke er processoren, men hukommelsen.

I år deltager vi i festivalen TechTrain. Der vil vi fortælle dig mere detaljeret og vise Qt, OpenCV på mikrocontrollere og vores andre præstationer.

Kilde: www.habr.com

Tilføj en kommentar