Portando Qt a STM32

Portando Qt a STM32Buenas tardes estamos en el proyecto Empaque Lancé Qt en STM32F7-Discovery y me gustaría hablar de ello. Anteriormente ya contamos cómo logramos lanzar OpenCV.

Qt es un marco multiplataforma que incluye no solo componentes gráficos, sino también elementos como QtNetwork, un conjunto de clases para trabajar con bases de datos, Qt para automatización (incluso para la implementación de IoT) y mucho más. El equipo de Qt ha sido proactivo en el uso de Qt en sistemas integrados, por lo que las bibliotecas son bastante configurables. Sin embargo, hasta hace poco, pocas personas pensaban en portar Qt a microcontroladores, probablemente porque esa tarea parece difícil: Qt es grande, las MCU son pequeñas.

Por otro lado, actualmente existen microcontroladores diseñados para trabajar con multimedia y superiores a los primeros Pentium. Hace aproximadamente un año apareció el blog Qt. enviar. Los desarrolladores hicieron una adaptación de Qt para el sistema operativo RTEMS y lanzaron ejemplos con widgets en varias placas que ejecutan stm32f7. Esto nos interesó. Se notó, y los propios desarrolladores escriben sobre ello, que Qt es lento en el STM32F7-Discovery. Nos preguntábamos si podríamos ejecutar Qt en Embox y no solo dibujar un widget, sino ejecutar una animación.

Qt 4.8 ha sido portado a Embox durante mucho tiempo, así que decidimos probarlo. Elegimos la aplicación Moveblocks, un ejemplo de animación elástica.

Bloques de movimiento Qt en QEMUPortando Qt a STM32

Para empezar, configuramos Qt, si es posible, con el conjunto mínimo de componentes necesarios para soportar la animación. Para esto existe una opción “-qconfig mínimo, pequeño, mediano…”. Conecta un archivo de configuración de Qt con muchas macros: qué habilitar/qué deshabilitar. Después de esta opción, agregamos otras banderas a la configuración si queremos deshabilitar algo más. Aquí tienes un ejemplo de nuestro configuraciones.

Para que Qt funcione, es necesario agregar una capa de compatibilidad del sistema operativo. Una forma es implementar QPA (Qt Platform Abstraction). Tomamos como base el complemento fb_base listo para usar incluido en Qt, sobre la base del cual funciona QPA para Linux. El resultado es un pequeño complemento llamado emboxfb, que proporciona a Qt el framebuffer de Embox y luego dibuja allí sin ninguna ayuda externa.

Así es como se ve la creación de 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();
}

Y así será el 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, con la optimización del compilador para el tamaño de la memoria -Os habilitada, la imagen de la biblioteca resultó ser de 3.5 MB, que por supuesto no cabe en la memoria principal del STM32F746. Como ya escribimos en nuestro otro artículo sobre OpenCV, esta placa tiene:

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

Dado que ya se agregó soporte para ejecutar código desde QSPI a OpenCV, decidimos comenzar cargando toda la imagen Embox c Qt en QSPI. ¡Y hurra, todo empezó casi de inmediato desde QSPI! Pero como en el caso de OpenCV, resultó que funciona demasiado lento.

Portando Qt a STM32

Por lo tanto, decidimos hacerlo de esta manera: primero copiamos la imagen a QSPI, luego la cargamos en SDRAM y la ejecutamos desde allí. Desde SDRAM se volvió un poco más rápido, pero aún está lejos de QEMU.

Portando Qt a STM32

Luego surgió la idea de incluir un punto flotante; después de todo, Qt hace algunos cálculos de las coordenadas de los cuadrados en la animación. Lo intentamos, pero aquí no conseguimos ninguna aceleración visible, aunque en статье Los desarrolladores de Qt afirmaron que FPU ofrece un aumento significativo en la velocidad de "arrastrar animación" en la pantalla táctil. Es posible que haya muchos menos cálculos de punto flotante en los bloques de movimiento, y esto depende del ejemplo específico.

La idea más efectiva fue mover el framebuffer de la SDRAM a la memoria interna. Para hacer esto, hicimos que las dimensiones de la pantalla no fueran 480x272, sino 272x272. También redujimos la profundidad del color de A8R8G8B8 a R5G6B5, reduciendo así el tamaño de un píxel de 4 a 2 bytes. El tamaño del framebuffer resultante es 272 * 272 * 2 = 147968 bytes. Esto dio una aceleración significativa, quizás lo más notable es que la animación se volvió casi fluida.

La última optimización fue ejecutar código Embox desde RAM y código Qt desde SDRAM. Para hacer esto, primero, como de costumbre, vinculamos estáticamente Embox junto con Qt, pero colocamos los segmentos de texto, rodata, datos y bss de la biblioteca en QSPI para luego copiarlos a 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))

Al ejecutar el código Embox desde la ROM, también recibimos una aceleración notable. Como resultado, la animación resultó bastante fluida:


Al final, mientras preparaba el artículo y probaba diferentes configuraciones de Embox, resultó que Qt moveblocks funciona muy bien desde QSPI con un framebuffer en SDRAM, ¡y el cuello de botella era precisamente el tamaño del framebuffer! Aparentemente, para superar la "presentación de diapositivas" inicial, fue suficiente una aceleración doble debido a una reducción banal en el tamaño del framebuffer. Pero no fue posible lograr tal resultado transfiriendo solo el código Embox a varias memorias rápidas (la aceleración no fue 2, sino aproximadamente 2 veces).

Cómo probarlo tú mismo

Si tiene un STM32F7-Discovery, puede ejecutar Qt en Embox usted mismo. Puedes leer cómo se hace esto en nuestro wiki.

Conclusión

Como resultado, logramos lanzar Qt! La complejidad de la tarea, en nuestra opinión, es algo exagerada. Naturalmente, es necesario tener en cuenta las características específicas de los microcontroladores y, en general, comprender la arquitectura de los sistemas informáticos. Los resultados de la optimización apuntan al hecho bien conocido de que el cuello de botella en un sistema informático no es el procesador, sino la memoria.

Este año participaremos en el festival. TechTrain. Allí le contaremos con más detalle y le mostraremos Qt, OpenCV en microcontroladores y otros logros nuestros.

Fuente: habr.com

Añadir un comentario