Portage de Qt vers STM32

Bon après-midi Dans le projet Embox, nous avons lancé Qt sur STM32F7-Discovery et nous aimerions en parler. Plus tôt, nous avions déjà expliqué comment nous avions réussi à lancer OpenCV .

Qt est un framework multiplateforme qui comprend non seulement des composants graphiques, mais aussi des éléments comme QtNetwork, un ensemble de classes pour travailler avec des bases de données, Qt for Automation (y compris l'implémentation de l'IoT), et bien plus encore. Les développeurs de l'équipe Qt ont prévu l'utilisation de Qt dans les systèmes embarqués, donc les bibliothèques sont assez bien configurées. Cependant, jusqu'à récemment, peu de gens pensaient au portage de Qt sur des microcontrôleurs, probablement parce que cette tâche semble compliquée - Qt est grand, les MCU sont petits.

D'un autre côté, il existe actuellement des microcontrôleurs conçus pour fonctionner avec le multimédia et supérieurs aux premiers Pentium. Il y a environ un an, un article est apparu sur le blog Qt. Les développeurs ont créé le port Qt sous RTEMS et ont lancé des exemples avec des widgets sur plusieurs cartes exécutant stm32f7. Cela nous a intéressé. C'était perceptible, et les développeurs eux-mêmes écrivent à ce sujet que Qt ralentit sur le STM32F7-Discovery. Nous nous sommes demandé si nous pouvions exécuter Qt sous Embox, et pas seulement dessiner un widget, mais démarrer l'animation.

Embt portait Qt 4.8 depuis longtemps, nous avons donc décidé de l'essayer. Nous avons choisi l'application moveblocks - un exemple d'animation dynamique.

Déplacement des blocs Qt sur QEMU


Pour commencer, nous configurons Qt, si possible, avec l'ensemble minimum de composants requis pour prendre en charge l'animation. Pour cela il y a l'option «-qconfig minimal, small, medium ...». Il comprend un fichier de configuration de Qt avec de nombreuses macros - quoi activer / désactiver. Après cette option, ajoutez d'autres indicateurs à la configuration si nous voulons désactiver autre chose. Voici un exemple de notre configuration .

Pour que Qt fonctionne, vous devez ajouter une couche de compatibilité OS. Une façon consiste à implémenter QPA (Qt Platform Abstraction). La base était le plugin prêt à l'emploi fb_base dans le cadre de Qt, sur la base duquel QPA pour Linux fonctionne. Le résultat est un petit plugin emboxfb qui fournit le framebuffer Embox Qt, puis il y dessine déjà sans aucune aide.

Voici à quoi ressemble la création d'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(); } 


Et donc ici, le redessin ressemblera
 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; } 


En conséquence, avec l'optimisation incluse du compilateur pour la taille de la mémoire -Os, l'image de la bibliothèque s'est avérée être de 3,5 Mo, ce qui bien sûr ne rentre pas dans la mémoire principale du STM32F746. Comme nous l'avons déjà écrit dans notre autre article sur OpenCV, cette carte a:

  • 1 Mo de ROM
  • 320 Ko de RAM
  • 8 Mo de SDRAM
  • 16 Mo QSPI

Étant donné que la prise en charge de l'exécution de code à partir de QSPI a déjà été ajoutée pour OpenCV, nous avons décidé de commencer par charger l'intégralité de l'image Embox c Qt dans QSPI. Et hourra, tout est parti presque immédiatement de QSPI! Mais comme avec OpenCV, cela s'est avéré trop lent.



Par conséquent, nous avons décidé de le faire - copiez d'abord l'image sur QSPI, puis chargez-la dans SDRAM et exécutez à partir de là. De SDRAM, il est devenu un peu plus rapide, mais encore loin de QEMU.



Ensuite, l'idée a été d'inclure une virgule flottante - parce que Qt fait certains calculs des coordonnées des carrés dans l'animation. Ils l'ont essayé, mais ils n'ont pas obtenu d'accélération visible, bien que les développeurs de Qt aient affirmé dans l' article que FPU donne une augmentation significative de la vitesse pour «faire glisser l'animation» sur l'écran tactile. Peut-être que moveblocks a beaucoup moins de calculs en virgule flottante, et cela dépend d'un exemple spécifique.

L'idée la plus efficace était de transférer le framebuffer de la SDRAM vers la mémoire interne. Pour ce faire, nous n'avions pas une taille d'écran de 480x272, mais 272x272. Nous avons également réduit la profondeur de couleur de A8R8G8B8 à R5G6B5, réduisant ainsi la taille d'un pixel de 4 à 2 octets. Nous avons obtenu la taille de framebuffer 272 * 272 * 2 = 147968 octets. Cela a donné une accélération significative, peut-être la plus notable, l'animation est devenue presque fluide.

La dernière optimisation a été l'exécution du code Embox à partir de la RAM et de Qt à partir de la SDRAM. Pour ce faire, nous lions d'abord, comme d'habitude, un lien statique Embox avec Qt, mais nous plaçons les segments de bibliothèques de texte, rodata, données et bss dans QSPI, afin de pouvoir les copier ultérieurement sur 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 raison de l'exécution du code, Embox de ROM a également reçu une accélération tangible. En conséquence, l'animation s'est avérée assez fluide:


Déjà à la toute fin, tout en préparant un article et en essayant diverses configurations d'Embox, il s'est avéré que Qt moveblocks fonctionne très bien avec QSPI avec un framebuffer en SDRAM, et la taille du framebuffer était le goulot d'étranglement! Apparemment, pour surmonter le «diaporama» initial, l'accélération de 2 fois était suffisante en raison de la réduction banale de la taille du framebuffer. Mais il n'a pas été possible d'obtenir un tel résultat en transférant uniquement du code Embox vers diverses mémoires rapides (l'accélération a été obtenue non pas par 2, mais par environ 1,5 fois).

Comment l'essayer vous-même


Si vous avez STM32F7-Discovery, vous pouvez exécuter Qt sous Embox vous-même. Vous pouvez lire comment faire cela sur notre wiki .

Conclusion


En conséquence, nous avons réussi à lancer Qt! À notre avis, la complexité de la tâche est quelque peu exagérée. Naturellement, vous devez prendre en compte les spécificités des microcontrôleurs et comprendre généralement l'architecture des systèmes informatiques. Les résultats de l'optimisation indiquent un fait bien connu que le goulot d'étranglement dans le système informatique n'est pas le processeur, mais la mémoire.

Cette année, nous participerons au festival TechTrain . Nous y expliquerons plus en détail et montrerons Qt, OpenCV sur les microcontrôleurs et nos autres réalisations.

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


All Articles