Portierung von Qt auf STM32

Portierung von Qt auf STM32Guten Tag! Wir sind im Projekt Embox habe Qt auf STM32F7-Discovery gestartet und würde gerne darüber sprechen. Zuvor haben wir bereits erzählt, wie wir den Start geschafft haben OpenCV.

Qt ist ein plattformübergreifendes Framework, das nicht nur grafische Komponenten, sondern auch Dinge wie QtNetwork, eine Reihe von Klassen für die Arbeit mit Datenbanken, Qt für Automatisierung (einschließlich für die IoT-Implementierung) und vieles mehr umfasst. Das Qt-Team hat Qt proaktiv in eingebetteten Systemen eingesetzt, sodass die Bibliotheken gut konfigurierbar sind. Allerdings dachten bis vor Kurzem nur wenige Menschen darüber nach, Qt auf Mikrocontroller zu portieren, wahrscheinlich weil eine solche Aufgabe schwierig erscheint – Qt ist groß, MCUs sind klein.

Andererseits gibt es derzeit Mikrocontroller, die für den Multimedia-Betrieb konzipiert und den ersten Pentiums überlegen sind. Vor etwa einem Jahr erschien der Qt-Blog Post. Die Entwickler haben eine Qt-Portierung für das RTEMS-Betriebssystem erstellt und Beispiele mit Widgets auf mehreren Boards mit stm32f7 veröffentlicht. Das hat uns interessiert. Auffällig ist, und die Entwickler selbst schreiben darüber, dass Qt auf dem STM32F7-Discovery langsam ist. Wir haben uns gefragt, ob wir Qt unter Embox ausführen und nicht nur ein Widget zeichnen, sondern auch eine Animation ausführen könnten.

Qt 4.8 wurde schon seit langem auf Embox portiert, daher haben wir beschlossen, es darauf auszuprobieren. Wir haben uns für die Moveblocks-Anwendung entschieden – ein Beispiel für federnde Animation.

Qt-Moveblocks auf QEMUPortierung von Qt auf STM32

Zunächst konfigurieren wir Qt, wenn möglich, mit dem minimalen Satz an Komponenten, die zur Unterstützung von Animationen erforderlich sind. Hierfür gibt es die Option „-qconfig minimal,small,medium…“. Es verbindet eine Konfigurationsdatei von Qt mit vielen Makros – was aktiviert/was deaktiviert werden soll. Nach dieser Option fügen wir der Konfiguration weitere Flags hinzu, wenn wir etwas anderes deaktivieren möchten. Hier ist ein Beispiel von uns Konfiguration.

Damit Qt funktioniert, müssen Sie eine Betriebssystemkompatibilitätsschicht hinzufügen. Eine Möglichkeit ist die Implementierung von QPA (Qt Platform Abstraction). Als Grundlage haben wir das in Qt enthaltene vorgefertigte Plugin fb_base genommen, auf dessen Basis QPA für Linux funktioniert. Das Ergebnis ist ein kleines Plugin namens emboxfb, das Qt den Framebuffer von Embox bereitstellt und dann ohne fremde Hilfe dorthin zieht.

So sieht die Erstellung eines Plugins aus

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

Und so wird die Neuzeichnung aussehen

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

Als Ergebnis stellte sich heraus, dass das Bibliotheksbild bei aktivierter Compiler-Optimierung für die Speichergröße -Os 3.5 MB groß war, was natürlich nicht in den Hauptspeicher des STM32F746 passt. Wie wir bereits in unserem anderen Artikel über OpenCV geschrieben haben, verfügt dieses Board über:

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

Da OpenCV bereits Unterstützung für die Ausführung von Code aus QSPI bietet, haben wir uns entschieden, zunächst das gesamte Embox c Qt-Image in QSPI zu laden. Und Hurra, alles begann fast sofort mit QSPI! Doch wie im Fall von OpenCV stellte sich heraus, dass es zu langsam arbeitet.

Portierung von Qt auf STM32

Deshalb haben wir uns für diese Vorgehensweise entschieden: Zuerst kopieren wir das Image nach QSPI, laden es dann in das SDRAM und führen es von dort aus aus. Von SDRAM wurde es etwas schneller, aber immer noch weit von QEMU entfernt.

Portierung von Qt auf STM32

Als nächstes gab es die Idee, einen Gleitkommawert einzubeziehen – schließlich führt Qt einige Berechnungen der Koordinaten von Quadraten in Animationen durch. Wir haben es versucht, aber hier bekamen wir keine sichtbare Beschleunigung, obwohl in Artikel Qt-Entwickler behaupteten, dass die FPU die Geschwindigkeit beim „Ziehen von Animationen“ auf dem Touchscreen erheblich steigert. In Moveblocks gibt es möglicherweise deutlich weniger Gleitkommaberechnungen, und dies hängt vom jeweiligen Beispiel ab.

Die effektivste Idee bestand darin, den Framebuffer vom SDRAM in den internen Speicher zu verschieben. Zu diesem Zweck haben wir die Bildschirmabmessungen nicht auf 480 x 272, sondern auf 272 x 272 festgelegt. Außerdem haben wir die Farbtiefe von A8R8G8B8 auf R5G6B5 gesenkt und damit die Größe eines Pixels von 4 auf 2 Byte reduziert. Die resultierende Framebuffergröße beträgt 272 * 272 * 2 = 147968 Bytes. Dies führte zu einer erheblichen Beschleunigung, was vielleicht am deutlichsten auffiel: Die Animation wurde nahezu flüssig.

Die neueste Optimierung bestand darin, Embox-Code aus dem RAM und Qt-Code aus dem SDRAM auszuführen. Dazu verknüpfen wir zunächst wie üblich Embox statisch mit Qt, platzieren aber die Text-, Rodata-, Daten- und BSS-Segmente der Bibliothek in QSPI, um sie dann ins SDRAM zu kopieren.

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))

Durch die Ausführung des Embox-Codes aus dem ROM erhielten wir ebenfalls eine spürbare Beschleunigung. Dadurch ist die Animation recht flüssig geworden:


Ganz am Ende, als ich den Artikel vorbereitete und verschiedene Embox-Konfigurationen ausprobierte, stellte sich heraus, dass Qt Moveblocks von QSPI mit einem Framebuffer im SDRAM hervorragend funktioniert, und der Engpass war genau die Größe des Framebuffers! Um die anfängliche „Diashow“ zu überwinden, reichte offenbar eine zweifache Beschleunigung aufgrund einer banalen Reduzierung der Framebuffergröße. Es war jedoch nicht möglich, ein solches Ergebnis zu erzielen, indem nur der Embox-Code in verschiedene schnelle Speicher übertragen wurde (die Beschleunigung betrug nicht das 2-fache, sondern etwa das 2-fache).

So probieren Sie es selbst aus

Wenn Sie über eine STM32F7-Discovery verfügen, können Sie Qt unter Embox selbst ausführen. Wie das geht, können Sie auf unserer Seite nachlesen Wiki.

Abschluss

Als Ergebnis ist es uns gelungen, Qt! auf den Markt zu bringen. Die Komplexität der Aufgabe ist unserer Meinung nach etwas übertrieben. Natürlich müssen Sie die Besonderheiten von Mikrocontrollern berücksichtigen und die Architektur von Computersystemen allgemein verstehen. Die Optimierungsergebnisse weisen auf die bekannte Tatsache hin, dass der Flaschenhals in einem Computersystem nicht der Prozessor, sondern der Speicher ist.

Dieses Jahr werden wir am Festival teilnehmen TechTrain. Dort verraten wir es euch genauer und zeigen Qt, OpenCV auf Mikrocontrollern und unsere weiteren Errungenschaften.

Source: habr.com

Kommentar hinzufügen