Portierung von Qt auf STM32

Guten Tag! Wir im Embox- Projekt haben Qt auf STM32F7-Discovery gestartet und möchten darüber berichten. Wir haben bereits zuvor erzählt, wie wir es geschafft haben, OpenCV zu starten.

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 for Automation (einschließlich der Implementierung von IoT) und vieles mehr enthält. Die Entwickler des Qt-Teams haben die Verwendung von Qt in eingebetteten Systemen vorgesehen, sodass die Bibliotheken ziemlich gut konfiguriert sind. Bis vor kurzem haben jedoch nur wenige Leute darüber nachgedacht, Qt auf Mikrocontroller zu portieren, wahrscheinlich weil diese Aufgabe kompliziert aussieht - Qt ist groß, MCUs sind klein.

Auf der anderen Seite gibt es derzeit Mikrocontroller, die für die Arbeit mit Multimedia ausgelegt sind und den ersten Pentiums überlegen sind. Vor ungefähr einem Jahr erschien ein Beitrag im Qt-Blog. Die Entwickler erstellten einen Qt-Port unter dem RTEMS-Betriebssystem und starteten Beispiele mit Widgets auf mehreren Boards, auf denen stm32f7 ausgeführt wird. Es hat uns interessiert. Es fiel auf, und die Entwickler selbst schreiben darüber, dass Qt beim STM32F7-Discovery langsamer wird. Wir haben uns gefragt, ob wir Qt unter Embox ausführen und nicht nur ein Widget zeichnen, sondern auch die Animation starten können.

Embt hat Qt 4.8 schon lange portiert, daher haben wir uns entschlossen, es zu versuchen. Wir haben uns für die Moveblocks-Anwendung entschieden - ein Beispiel für eine federnde Animation.

Qt moveblocks auf QEMU


Zunächst konfigurieren wir Qt nach Möglichkeit mit dem Mindestsatz an Komponenten, die zur Unterstützung der Animation erforderlich sind. Hierfür gibt es die Option "-qconfig minimal, klein, mittel ...". Es enthält eine Konfigurationsdatei von Qt mit vielen Makros - was aktiviert / deaktiviert werden soll. Fügen Sie nach dieser Option der Konfiguration weitere Flags hinzu, wenn Sie etwas anderes deaktivieren möchten. Hier ist ein Beispiel für unsere Konfiguration .

Damit Qt funktioniert, müssen Sie eine Betriebssystemkompatibilitätsebene hinzufügen. Eine Möglichkeit besteht darin, QPA (Qt Platform Abstraction) zu implementieren. Basis war das fertige Plugin fb_base als Teil von Qt, auf dessen Basis QPA für Linux funktioniert. Das Ergebnis ist ein kleines emboxfb-Plugin, das den Embox Qt-Framebuffer bereitstellt und dort bereits ohne Hilfe zeichnet.

So sieht die Erstellung von 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 hier das Neuzeichnen aussehen
 QRegion QEmboxFbScreen::doRedraw() { QVector<QRect> rects; QRegion touched = QFbScreen::doRedraw(); DPRINTF("QEmboxFbScreen::doRedraw\n"); 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; } 


Mit der enthaltenen Optimierung des Compilers für die Speichergröße -Os ergab sich ein Image der Bibliothek von 3,5 MB, was natürlich nicht in den Hauptspeicher des STM32F746 passt. Wie wir bereits in unserem anderen Artikel über OpenCV geschrieben haben, hat dieses Board:

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

Da die Unterstützung für die Ausführung von Code aus QSPI für OpenCV bereits hinzugefügt wurde, haben wir uns entschlossen, zunächst das gesamte Embox c Qt-Image in QSPI zu laden. Und Hurra, alles begann fast sofort mit QSPI! Aber wie bei OpenCV stellte sich heraus, dass es zu langsam war.



Aus diesem Grund haben wir uns dazu entschlossen: Kopieren Sie das Image zuerst in QSPI, laden Sie es dann in das SDRAM und führen Sie es von dort aus aus. Von SDRAM wurde es etwas schneller, aber immer noch weit von QEMU entfernt.



Als nächstes kam die Idee, einen Gleitkomma aufzunehmen - da Qt einige Berechnungen der Koordinaten der Quadrate in der Animation durchführt. Sie haben es versucht, aber sie haben keine sichtbare Beschleunigung erhalten, obwohl Qt-Entwickler in dem Artikel behaupteten, dass die FPU die Geschwindigkeit für das „Ziehen von Animationen“ auf dem Touchscreen erheblich erhöht. Möglicherweise hat Moveblocks deutlich weniger Gleitkommaberechnungen, und dies hängt von einem bestimmten Beispiel ab.

Die effektivste Idee war, den Framebuffer vom SDRAM in den internen Speicher zu übertragen. Dafür haben wir die Bildschirmgröße nicht 480x272, sondern 272x272 gemacht. Wir haben auch die Farbtiefe von A8R8G8B8 auf R5G6B5 reduziert und damit die Größe eines Pixels von 4 auf 2 Byte reduziert. Wir haben die Framebuffer-Größe 272 * 272 * 2 = 147968 Bytes. Dies gab eine signifikante Beschleunigung, vielleicht die auffälligste, die Animation wurde fast glatt.

Die letzte Optimierung war die Ausführung von Embox-Code aus dem RAM und Qt aus dem SDRAM. Dazu verknüpfen wir zunächst wie gewohnt statisch Embox mit Qt, platzieren jedoch die Segmente der Text-, Rodata-, Daten- und BSS-Bibliotheken in QSPI, damit wir sie später in SDRAM kopieren können.

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

Aufgrund der Ausführung des Codes erhielt Embox aus dem ROM auch eine spürbare Beschleunigung. Infolgedessen erwies sich die Animation als ziemlich flüssig:


Bereits ganz am Ende, als ein Artikel vorbereitet und verschiedene Konfigurationen von Embox ausprobiert wurden, stellte sich heraus, dass Qt-Moveblocks von QSPI mit einem Framebuffer in SDRAM einwandfrei funktionieren und die Größe des Framebuffers der Engpass war! Um die anfängliche „Diashow“ zu überwinden, war die Beschleunigung aufgrund der banalen Reduzierung der Framebuffer-Größe offenbar ausreichend. Es war jedoch nicht möglich, ein solches Ergebnis zu erzielen, indem nur Embox-Code in verschiedene schnelle Speicher übertragen wurde (die Beschleunigung wurde nicht um das 2-fache, sondern um das 1,5-fache erreicht).

Wie Sie es selbst versuchen


Wenn Sie über STM32F7-Discovery verfügen, können Sie Qt selbst unter Embox ausführen. Wie das geht, lesen Sie in unserem Wiki .

Fazit


Infolgedessen ist es uns gelungen, Qt! 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 eine bekannte Tatsache hin, dass der Engpass im Computersystem nicht der Prozessor, sondern der Speicher ist.

Dieses Jahr werden wir am TechTrain Festival teilnehmen. Dort werden wir Qt, OpenCV auf Mikrocontrollern und unsere anderen Erfolge genauer beschreiben und zeigen.

Source: https://habr.com/ru/post/de459730/


All Articles