Porting di Qt su STM32

Porting di Qt su STM32Buon pomeriggio Siamo nel progetto Embox ho lanciato Qt su STM32F7-Discovery e vorrei parlarne. In precedenza, abbiamo già detto come siamo riusciti a lanciare OpenCV.

Qt è un framework multipiattaforma che include non solo componenti grafici, ma anche cose come QtNetwork, un insieme di classi per lavorare con i database, Qt for Automation (inclusa l'implementazione IoT) e molto altro. Il team Qt è stato proattivo nell'usare Qt nei sistemi embedded, quindi le librerie sono abbastanza configurabili. Tuttavia, fino a poco tempo fa, poche persone pensavano di portare Qt sui microcontrollori, probabilmente perché un compito del genere sembrava difficile: Qt è grande, gli MCU sono piccoli.

D'altra parte, al momento esistono microcontrollori progettati per funzionare con la multimedialità e superiori ai primi Pentium. Circa un anno fa è apparso il blog Qt inviare. Gli sviluppatori hanno realizzato un port di Qt per il sistema operativo RTEMS e hanno lanciato esempi con widget su diverse schede che eseguono stm32f7. Questo ci interessava. Era evidente, e gli stessi sviluppatori lo scrivono, che Qt è lento su STM32F7-Discovery. Ci chiedevamo se potessimo eseguire Qt sotto Embox e non solo disegnare un widget, ma eseguire un'animazione.

Qt 4.8 è stato portato su Embox per molto tempo, quindi abbiamo deciso di provarlo. Abbiamo scelto l'applicazione moveblocks, un esempio di animazione elastica.

Sposta i blocchi Qt su QEMUPorting di Qt su STM32

Per cominciare, configuriamo Qt, se possibile, con il set minimo di componenti richiesti per supportare l'animazione. Per questo esiste l'opzione “-qconfig minimal,small,medium...”. Collega un file di configurazione da Qt con molte macro: cosa abilitare/cosa disabilitare. Dopo questa opzione, aggiungiamo altri flag alla configurazione se vogliamo disabilitare qualcos'altro. Ecco un esempio del nostro configurazione.

Affinché Qt funzioni, è necessario aggiungere un livello di compatibilità del sistema operativo. Un modo è implementare QPA (Qt Platform Abstraction). Abbiamo preso come base il plugin fb_base già pronto incluso in Qt, sulla base del quale funziona QPA per Linux. Il risultato è un piccolo plugin chiamato emboxfb, che fornisce a Qt il framebuffer di Embox, e poi si disegna lì senza alcun aiuto esterno.

Ecco come appare la creazione di un 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();
}

E questo è come apparirà il ridisegno

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

Di conseguenza, con l'ottimizzazione del compilatore per la dimensione della memoria -Os abilitata, l'immagine della libreria risulta essere di 3.5 MB, che ovviamente non rientra nella memoria principale dell'STM32F746. Come abbiamo già scritto nel nostro altro articolo su OpenCV, questa scheda ha:

  • 1 MB di ROM
  • 320KB di RAM
  • SD-RAM da 8 MB
  • QSPI da 16 MB

Poiché il supporto per l'esecuzione del codice da QSPI è già stato aggiunto a OpenCV, abbiamo deciso di iniziare caricando l'intera immagine Embox c Qt in QSPI. E evviva, tutto è partito quasi subito da QSPI! Ma come nel caso di OpenCV, si è scoperto che funziona troppo lentamente.

Porting di Qt su STM32

Pertanto, abbiamo deciso di farlo in questo modo: prima copiamo l'immagine su QSPI, quindi la carichiamo nella SDRAM ed eseguiamo da lì. Da SDRAM è diventato un po' più veloce, ma ancora lontano da QEMU.

Porting di Qt su STM32

Successivamente, è nata l'idea di includere un punto mobile: dopo tutto, Qt esegue alcuni calcoli sulle coordinate dei quadrati nell'animazione. Ci abbiamo provato, ma qui non abbiamo ottenuto alcuna accelerazione visibile, anche se dentro Articolo Gli sviluppatori Qt hanno affermato che la FPU offre un aumento significativo della velocità per il "trascinamento dell'animazione" sul touchscreen. Potrebbero esserci molti meno calcoli in virgola mobile nei moveblock e questo dipende dall'esempio specifico.

L'idea più efficace è stata spostare il framebuffer dalla SDRAM alla memoria interna. Per fare ciò, abbiamo reso le dimensioni dello schermo non 480x272, ma 272x272. Abbiamo anche abbassato la profondità del colore da A8R8G8B8 a R5G6B5, riducendo così la dimensione di un pixel da 4 a 2 byte. La dimensione del framebuffer risultante è 272 * 272 * 2 = 147968 byte. Ciò ha dato un'accelerazione significativa, forse la più evidente, l'animazione è diventata quasi fluida.

L'ultima ottimizzazione è stata l'esecuzione del codice Embox dalla RAM e del codice Qt dalla SDRAM. Per fare questo, prima colleghiamo staticamente Embox insieme a Qt, come al solito, ma posizioniamo i segmenti text, rodata, data e bss della libreria in QSPI per poi copiarli su 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))

Eseguendo il codice Embox dalla ROM, abbiamo anche ricevuto una notevole accelerazione. Di conseguenza, l'animazione è risultata abbastanza fluida:


Alla fine, mentre preparavo l'articolo e provavo diverse configurazioni di Embox, si è scoperto che i moveblock Qt funzionano benissimo da QSPI con un framebuffer in SDRAM, e il collo di bottiglia era proprio la dimensione del framebuffer! A quanto pare, per superare lo “slideshow” iniziale, è stata sufficiente un'accelerazione di 2 volte dovuta ad una banale riduzione delle dimensioni del framebuffer. Ma non è stato possibile ottenere un risultato del genere trasferendo solo il codice Embox su varie memorie veloci (l'accelerazione non era 2, ma circa 1.5 volte).

Come provarlo tu stesso

Se disponi di un STM32F7-Discovery, puoi eseguire tu stesso Qt sotto Embox. Puoi leggere come viene fatto sul nostro вики.

conclusione

Di conseguenza, siamo riusciti a lanciare Qt! La complessità del compito, a nostro avviso, è alquanto esagerata. Naturalmente, è necessario tenere conto delle specificità dei microcontrollori e in generale comprendere l'architettura dei sistemi informatici. I risultati dell'ottimizzazione evidenziano il fatto ben noto che il collo di bottiglia in un sistema informatico non è il processore, ma la memoria.

Quest'anno parteciperemo al festival TechTrain. Lì te lo diremo in modo più dettagliato e mostreremo Qt, OpenCV sui microcontrollori e gli altri nostri risultati.

Fonte: habr.com

Aggiungi un commento