Portar Qt a STM32

Portar Qt a STM32Bona tarda Estem al projecte Embox va llançar Qt a STM32F7-Discovery i m'agradaria parlar-ne. Abans, ja vam explicar com vam aconseguir el llançament OpenCV.

Qt és un marc multiplataforma que inclou no només components gràfics, sinó també coses com QtNetwork, un conjunt de classes per treballar amb bases de dades, Qt for Automation (inclosa la implementació d'IoT) i molt més. Els desenvolupadors de l'equip de Qt han previst l'ús de Qt en sistemes encastats, de manera que les biblioteques són bastant ben configurables. No obstant això, fins fa poc, poca gent pensava en portar Qt a microcontroladors, probablement perquè aquesta tasca sembla difícil: Qt és gran, les MCU són petites.

D'altra banda, actualment hi ha microcontroladors pensats per funcionar amb multimèdia i superar els primers Pentium. Va aparèixer al bloc Qt fa aproximadament un any publicar. Els desenvolupadors van fer un port de Qt amb el sistema operatiu RTEMS i van executar exemples amb ginys en diversos taulers amb stm32f7. Ens interessa això. Es va notar, i els mateixos desenvolupadors escriuen sobre això, que Qt es desaccelera a STM32F7-Discovery. Ens vam preguntar si podríem executar Qt a Embox, i no només dibuixar un giny, sinó executar una animació.

Qt 4.8 s'ha portat a Embox durant molt de temps, així que vam decidir provar-lo. Hem escollit l'aplicació moveblocks, un exemple d'animació elàstica.

Qt moveblocks a QEMUPortar Qt a STM32

Per començar, configurem Qt amb el conjunt mínim de components necessaris per suportar l'animació, si és possible. Hi ha una opció "-qconfig minimal,small,medium..." per a això. Inclou un fitxer de configuració de Qt amb moltes macros: què activar / què desactivar. Després d'aquesta opció, afegim altres senyaladors a la configuració si volem desactivar una altra cosa addicionalment. Aquí teniu un exemple del nostre configuració.

Perquè Qt funcioni, heu d'afegir una capa de compatibilitat amb el sistema operatiu. Una manera és implementar QPA (Qt Platform Abstraction). Es va prendre com a base el connector fb_base ja fet com a part de Qt, sobre la base del qual funciona QPA per a Linux. El resultat és un petit connector emboxfb que proporciona a Qt el framebuffer d'Embox i, a continuació, dibuixa allà sense ajuda externa.

Aquest és el aspecte del connector

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

I així es veurà el redibuix

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

Com a resultat, amb l'optimització del compilador per a la mida de la memòria activada -Os, la imatge de la biblioteca va resultar ser de 3.5 MB, que per descomptat no encaixa a la memòria principal de l'STM32F746. Com vam escriure al nostre altre article sobre OpenCV, aquest tauler té:

  • ROM d'1 MB
  • 320 KB de RAM
  • 8 MB de SDRAM
  • 16 MB QSPI

Com que ja s'ha afegit suport per executar codi des de QSPI per a OpenCV, vam decidir començar carregant tota la imatge d'Embox amb Qt a QSPI. I hurra, tot va començar gairebé immediatament des de QSPI! Però com en el cas d'OpenCV, va resultar massa lent.

Portar Qt a STM32

Per tant, vam decidir fer-ho: primer copiem la imatge a QSPI, després la carreguem a SDRAM i l'executem des d'allà. Des de SDRAM es va fer una mica més ràpid, però encara lluny de QEMU.

Portar Qt a STM32

Després va sorgir la idea d'incloure un punt flotant; després de tot, Qt fa alguns càlculs de les coordenades dels quadrats de l'animació. Ho vam intentar, però aquí no vam aconseguir una acceleració visible, encara que dins article Els desenvolupadors de Qt han afirmat que la FPU proporciona un augment de velocitat significatiu per "arrossegar animacions" a la pantalla tàctil. Pot ser que hi hagi menys càlculs de coma flotant als blocs de moviment, i depèn de l'exemple específic.

La idea de transferir el framebuffer de SDRAM a la memòria interna va resultar ser la més eficaç. Per fer-ho, hem fet que la mida de la pantalla no sigui 480x272, sinó 272x272. També vam reduir la profunditat del color d'A8R8G8B8 a R5G6B5, reduint així la mida d'un píxel de 4 a 2 bytes. Tenim la mida del framebuffer 272 * 272 * 2 = 147968 bytes. Això va donar una acceleració significativa, potser la més notable, l'animació es va tornar gairebé suau.

L'última optimització va ser l'execució del codi Embox des de la RAM i Qt des de la SDRAM. Per fer-ho, primer, com és habitual, enllacem estàticament l'Embox juntament amb el Qt, però col·loquem els segments de text, rodata, dades i bss de la biblioteca a QSPI per copiar-lo posteriorment a 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))

En executar el codi Embbox des de la ROM, també hem obtingut una acceleració notable. Com a resultat, l'animació va resultar bastant suau:


Ja al final, preparant l'article i provant diferents configuracions d'Embox, va resultar que Qt moveblocks funciona molt bé des de QSPI amb un framebuffer en SDRAM, i el coll d'ampolla era exactament de la mida del framebuffer! Pel que sembla, per superar la "presentació de diapositives" inicial, va ser suficient una acceleració de dues vegades a causa d'una reducció banal de la mida del framebuffer. Però no va ser possible aconseguir aquest resultat transferint només el codi Embox a diverses memòries ràpides (l'acceleració no va ser de 2, sinó aproximadament 2 vegades).

Com provar-ho tu mateix

Si teniu STM32F7-Discovery, podeu executar Qt a Embox vosaltres mateixos. Podeu llegir com es fa al nostre wiki.

Conclusió

Com a resultat, vam aconseguir iniciar Qt! La complexitat de la tasca, al nostre parer, és una mica exagerada. Naturalment, cal tenir en compte les especificitats dels microcontroladors i en general comprendre l'arquitectura dels sistemes informàtics. Els resultats de l'optimització apunten al fet ben conegut que el coll d'ampolla en un sistema informàtic no és el processador, sinó la memòria.

Aquest any participarem al festival Tren tecnològic. Allà explicarem amb més detall i mostrarem Qt, OpenCV en microcontroladors i els nostres altres assoliments.

Font: www.habr.com

Afegeix comentari