Portar Qt a STM32

Buenas tardes En el proyecto Embox lanzamos Qt en STM32F7-Discovery y nos gustaría contarlo. Anteriormente, ya contamos cómo logramos lanzar OpenCV .

Qt es un marco multiplataforma que incluye no solo componentes gráficos, sino también cosas como QtNetwork, un conjunto de clases para trabajar con bases de datos, Qt para automatización (incluida la implementación de IoT) y mucho más. Los desarrolladores del equipo de Qt han previsto el uso de Qt en sistemas integrados, por lo que las bibliotecas están bastante bien configuradas. Sin embargo, hasta hace poco, pocas personas pensaban en portar Qt a microcontroladores, probablemente porque esta tarea parece complicada: Qt es grande, las MCU son pequeñas.

Por otro lado, por el momento hay microcontroladores diseñados para trabajar con multimedia y son superiores a los primeros Pentium. Hace aproximadamente un año, apareció una publicación en el blog de Qt. Los desarrolladores hicieron el puerto Qt bajo RTEMS y lanzaron ejemplos con widgets en varias placas que ejecutan stm32f7. Nos interesó Fue notable, y los propios desarrolladores escriben sobre esto que Qt ralentiza en el STM32F7-Discovery. Nos preguntamos si podríamos ejecutar Qt en Embox, y no solo dibujar un widget, sino iniciar la animación.

Embt ha estado portando Qt 4.8 durante mucho tiempo, así que decidimos probarlo. Elegimos la aplicación moveblocks, un ejemplo de animación elástica.

Qt moveblocks en QEMU


Para comenzar, configuramos Qt, si es posible, con el conjunto mínimo de componentes necesarios para admitir animación. Para esto existe la opción "-qconfig minimal, small, medium ...". Incluye un archivo de configuración de Qt con muchas macros: qué habilitar / deshabilitar. Después de esta opción, agregue otras banderas a la configuración si queremos deshabilitar algo más. Aquí hay un ejemplo de nuestra configuración .

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

Así es como se ve la creación de complementos
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í, aquí se verá el rediseño
 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; } 


Como resultado, con la optimización incluida del compilador para el tamaño de memoria -Os, 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, este foro tiene:

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

Dado que el soporte para ejecutar código de QSPI ya se ha agregado para OpenCV, decidimos comenzar cargando toda la imagen Embox c Qt en QSPI. Y hurra, ¡todo comenzó casi de inmediato desde QSPI! Pero como con OpenCV, resultó ser demasiado lento.



Por lo tanto, decidimos hacer esto: primero copie la imagen a QSPI, luego cárguela en SDRAM y ejecútela desde allí. Desde SDRAM se hizo un poco más rápido, pero aún lejos de QEMU.



La siguiente fue la idea de incluir un punto flotante, porque Qt hace algunos cálculos de las coordenadas de los cuadrados en la animación. Lo intentaron, pero no obtuvieron una aceleración visible, aunque los desarrolladores de Qt afirmaron en el artículo que FPU proporciona un aumento significativo en la velocidad para "arrastrar animación" en la pantalla táctil. Quizás moveblocks tiene significativamente menos cálculos de coma flotante, y esto depende de un ejemplo específico.

La idea más efectiva fue transferir el framebuffer de SDRAM a la memoria interna. Para hacer esto, no teníamos un tamaño de pantalla de 480x272, sino de 272x272. También redujimos la profundidad de color de A8R8G8B8 a R5G6B5, reduciendo así el tamaño de un píxel de 4 a 2 bytes. Tenemos el tamaño del framebuffer 272 * 272 * 2 = 147968 bytes. Esto dio una aceleración significativa, quizás la más notable, la animación se volvió casi suave.

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

Debido a la ejecución del código, Embox de ROM también recibió una aceleración tangible. Como resultado, la animación resultó ser bastante fluida:


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

Cómo probarlo tú mismo


Si tiene STM32F7-Discovery, puede ejecutar Qt en Embox usted mismo. Puedes leer cómo hacer esto en nuestra wiki .

Conclusión


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

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

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


All Articles