Portage de Qt vers STM32

Portage de Qt vers STM32Bon après-midi Nous sommes dans le projet Embox a lancé Qt sur STM32F7-Discovery et j'aimerais en parler. Plus tôt, nous avons déjà raconté comment nous avons réussi à lancer OpenCV.

Qt est un framework multiplateforme qui comprend non seulement des composants graphiques, mais également des éléments tels que QtNetwork, un ensemble de classes permettant de travailler avec des bases de données, Qt for Automation (y compris pour l'implémentation de l'IoT) et bien plus encore. L'équipe Qt a été proactive quant à l'utilisation de Qt dans les systèmes embarqués, les bibliothèques sont donc assez configurables. Cependant, jusqu'à récemment, peu de gens envisageaient de porter Qt sur des microcontrôleurs, probablement parce qu'une telle tâche semble difficile : Qt est grand, les MCU sont petits.

D'autre part, 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, le blog Qt est apparu poster. Les développeurs ont réalisé un portage de Qt pour le système d'exploitation RTEMS et ont lancé des exemples avec des widgets sur plusieurs cartes exécutant stm32f7. Cela nous a intéressé. Il était visible, et les développeurs eux-mêmes en parlent, que Qt est lent sur le STM32F7-Discovery. Nous nous demandions si nous pouvions exécuter Qt sous Embox, et non seulement dessiner un widget, mais exécuter une animation.

Qt 4.8 est porté sur Embox depuis longtemps, nous avons donc décidé de l'essayer. Nous avons choisi l'application moveblocks - un exemple d'animation élastique.

Blocs de déplacement Qt sur QEMUPortage de Qt vers STM32

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

Pour que Qt fonctionne, vous devez ajouter une couche de compatibilité du système d'exploitation. Une solution consiste à implémenter QPA (Qt Platform Abstraction). Nous avons pris comme base le plugin fb_base prêt à l'emploi inclus dans Qt, sur la base duquel fonctionne QPA pour Linux. Le résultat est un petit plugin appelé emboxfb, qui fournit à Qt le framebuffer d'Embox, puis il y dessine sans aucune aide extérieure.

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 voici à quoi ressemblera le redessin

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

En conséquence, avec l'optimisation du compilateur pour la taille de la mémoire -Os activée, 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 possède :

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

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

Portage de Qt vers STM32

Par conséquent, nous avons décidé de procéder de cette façon : nous copions d'abord l'image sur QSPI, puis la chargeons dans la SDRAM et l'exécutons à partir de là. De la SDRAM, c'est devenu un peu plus rapide, mais toujours loin du QEMU.

Portage de Qt vers STM32

Ensuite, il y a eu l'idée d'inclure une virgule flottante - après tout, Qt fait quelques calculs des coordonnées des carrés dans l'animation. Nous avons essayé, mais ici nous n'avons obtenu aucune accélération visible, même si dans article Les développeurs de Qt ont affirmé que FPU permettait d'augmenter considérablement la vitesse de « déplacement de l'animation » sur l'écran tactile. Il peut y avoir beaucoup moins de calculs à virgule flottante dans les moveblocks, et cela dépend de l'exemple spécifique.

L'idée la plus efficace était de déplacer le framebuffer de la SDRAM vers la mémoire interne. Pour ce faire, nous avons fixé les dimensions de l'écran non pas à 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. La taille du framebuffer résultant est de 272 * 272 * 2 = 147968 octets. Cela a donné une accélération significative, et peut-être le plus remarquable est que l'animation est devenue presque fluide.

La dernière optimisation consistait à exécuter le code Embox à partir de la RAM et le code Qt à partir de la SDRAM. Pour ce faire, nous lions d'abord, comme d'habitude, statiquement Embox avec Qt, mais nous plaçons les segments texte, rodata, data et bss de la bibliothèque dans QSPI afin de les copier ensuite dans la 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 exécutant le code Embox depuis la ROM, nous avons également reçu une accélération notable. En conséquence, l’animation s’est avérée assez fluide :


À la toute fin, en préparant l'article et en essayant différentes configurations d'Embox, il s'est avéré que les moveblocks Qt fonctionnent très bien depuis QSPI avec un framebuffer en SDRAM, et le goulot d'étranglement était précisément la taille du framebuffer ! Apparemment, pour surmonter le "diaporama" initial, une accélération de 2 fois suffisait en raison d'une banale réduction de la taille du framebuffer. Mais il n'a pas été possible d'obtenir un tel résultat en transférant uniquement le code Embox vers diverses mémoires rapides (l'accélération n'était pas de 2, mais d'environ 1.5 fois).

Comment l'essayer vous-même

Si vous disposez d'un STM32F7-Discovery, vous pouvez exécuter vous-même Qt sous Embox. Vous pouvez lire comment cela se fait sur notre wiki.

Conclusion

En conséquence, nous avons réussi à lancer Qt! La complexité de la tâche est, à notre avis, 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 soulignent le fait bien connu que le goulot d'étranglement dans un système informatique n'est pas le processeur, mais la mémoire.

Cette année nous participerons au festival Train technique. Là, nous vous en dirons plus en détail et vous montrerons Qt, OpenCV sur les microcontrôleurs et nos autres réalisations.

Source: habr.com

Ajouter un commentaire