Portando Qt a STM32

Portando Qt a STM32Boas tardes Estamos no proxecto Embox lanzou Qt en STM32F7-Discovery e gustaríame falar diso. Antes xa contamos como conseguimos lanzar OpenCV.

Qt é un marco multiplataforma que inclúe non só compoñentes gráficos, senón tamén cousas como QtNetwork, un conxunto de clases para traballar con bases de datos, Qt for Automation (incluída a implementación de IoT) e moito máis. O equipo de Qt foi proactivo sobre o uso de Qt en sistemas integrados, polo que as bibliotecas son bastante configurables. Non obstante, ata hai pouco, poucas persoas pensaban en portar Qt a microcontroladores, probablemente porque tal tarefa parece difícil: Qt é grande, os MCU son pequenos.

Por outra banda, neste momento existen microcontroladores pensados ​​para funcionar con multimedia e superiores aos primeiros Pentium. Hai aproximadamente un ano apareceu o blog Qt publicación. Os desenvolvedores fixeron un porto de Qt para o sistema operativo RTEMS e lanzaron exemplos con widgets en varias placas que executaban stm32f7. Isto interesounos. Notable, e os propios desenvolvedores escriben sobre iso, que Qt é lento no STM32F7-Discovery. Preguntámonos se poderíamos executar Qt baixo Embox, e non só debuxar un widget, senón executar unha animación.

Qt 4.8 foi portado a Embox durante moito tempo, polo que decidimos probalo. Escollemos a aplicación moveblocks, un exemplo de animación elástica.

Qt moveblocks en QEMUPortando Qt a STM32

Para comezar, configuramos Qt, se é posible, co conxunto mínimo de compoñentes necesarios para soportar a animación. Para iso hai unha opción “-qconfig minimal,small,medium...”. Conecta un ficheiro de configuración de Qt con moitas macros: que activar / que desactivar. Despois desta opción, engadimos outras bandeiras á configuración se queremos desactivar outra cousa. Velaquí un exemplo do noso configuración.

Para que Qt funcione, cómpre engadir unha capa de compatibilidade do sistema operativo. Unha forma é implementar QPA (Qt Platform Abstracción). Tomamos como base o complemento fb_base preparado incluído en Qt, sobre a base do cal funciona QPA para Linux. O resultado é un pequeno complemento chamado emboxfb, que proporciona a Qt o framebuffer de Embox, e despois debuxa alí sen axuda externa.

Isto é o que parece crear un complemento

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 así será o rediseño

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

Como resultado, coa optimización do compilador para o tamaño da memoria -Os activada, a imaxe da biblioteca resultou ser de 3.5 MB, que por suposto non encaixa na memoria principal do STM32F746. Como xa escribimos no noso outro artigo sobre OpenCV, este taboleiro ten:

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

Dado que xa se engadiu o soporte para executar código desde QSPI a OpenCV, decidimos comezar cargando toda a imaxe de Embox c Qt en QSPI. E vaga, todo comezou case de inmediato desde QSPI! Pero como no caso de OpenCV, resultou que funciona moi lentamente.

Portando Qt a STM32

Polo tanto, decidimos facelo deste xeito: primeiro copiamos a imaxe en QSPI, despois cargamos na SDRAM e executamos desde alí. Desde SDRAM fíxose un pouco máis rápido, pero aínda lonxe de QEMU.

Portando Qt a STM32

A continuación, houbo unha idea de incluír un punto flotante; despois de todo, Qt fai algúns cálculos das coordenadas dos cadrados na animación. Intentamos, pero aquí non obtivemos ningunha aceleración visible, aínda que dentro Artigo Os desenvolvedores de Qt afirmaron que FPU dá un aumento significativo na velocidade para "arrastrar animación" na pantalla táctil. Pode haber moito menos cálculos de coma flotante nos moveblocks, e isto depende do exemplo específico.

A idea máis efectiva foi mover o framebuffer da SDRAM á memoria interna. Para iso, fixemos que as dimensións da pantalla non fosen 480x272, senón 272x272. Tamén reducimos a profundidade da cor de A8R8G8B8 a R5G6B5, reducindo así o tamaño dun píxel de 4 a 2 bytes. O tamaño do framebuffer resultante é 272 * 272 * 2 = 147968 bytes. Isto deu unha aceleración significativa, quizais o máis notable, a animación tornouse case suave.

A última optimización foi executar código Embox desde RAM e código Qt desde SDRAM. Para iso, primeiro, como é habitual, enlazamos embox de forma estática con Qt, pero colocamos os segmentos de texto, rodata, datos e bss da biblioteca en QSPI para despois copialo na 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))

Ao executar o código Embox desde a ROM, tamén recibimos unha aceleración notable. Como resultado, a animación resultou bastante suave:


Ao final, mentres preparaba o artigo e probaba diferentes configuracións de Embox, descubriuse que Qt moveblocks funciona moi ben desde QSPI cun framebuffer en SDRAM, e o pescozo de botella era precisamente do tamaño do framebuffer. Ao parecer, para superar a "presentación de diapositivas" inicial, unha aceleración de 2 veces foi suficiente debido a unha redución banal do tamaño do framebuffer. Pero non foi posible conseguir tal resultado transferindo só o código Embox a varias memorias rápidas (a aceleración non foi de 2, senón de 1.5 veces).

Como probalo vostede mesmo

Se tes un STM32F7-Discovery, podes executar Qt en Embbox ti mesmo. Podes ler como se fai isto na nosa web wiki.

Conclusión

Como resultado, conseguimos lanzar Qt! A complexidade da tarefa, na nosa opinión, é algo esaxerada. Por suposto, cómpre ter en conta as características específicas dos microcontroladores e comprender xeralmente a arquitectura dos sistemas informáticos. Os resultados da optimización apuntan ao feito ben coñecido de que o pescozo de botella nun sistema informático non é o procesador, senón a memoria.

Este ano imos participar no festival TechTrain. Alí contarémosche con máis detalle e mostraremos Qt, OpenCV en microcontroladores e outros nosos logros.

Fonte: www.habr.com

Engadir un comentario