
下午好 我们在
Embox项目中在STM32F7-Discovery上启动了Qt,并希望介绍一下。 之前,我们已经讲述了如何启动
OpenCV 。
Qt是一个跨平台框架,不仅包括图形组件,还包括QtNetwork,用于处理数据库的一组类,用于自动化的Qt(包括IoT的实现)等。 Qt团队的开发人员已经预见到Qt在嵌入式系统中的使用,因此这些库的配置都很好。 但是,直到最近,很少有人考虑将Qt移植到微控制器,这可能是因为此任务看起来很复杂-Qt很大,MCU很小。
另一方面,目前有设计用于多媒体的微控制器,其性能优于第一批奔腾处理器。 大约一年前,一个帖子出现在Qt博客上。 开发人员在RTEMS操作系统下创建了一个Qt端口,并在运行stm32f7的几块板上启动了带有小部件的示例。 它使我们感兴趣。 值得注意的是,开发人员自己撰写了有关Qt降低STM32F7-Discovery的文章。 我们想知道是否可以在Embox下运行Qt,而不仅仅是绘制一个小部件,而是启动动画。
Embt已经很长时间移植了Qt 4.8,因此我们决定尝试一下。 我们选择了moveblocks应用程序-弹性动画的示例。
首先,如果可能,我们将Qt配置为支持动画所需的最少组件集。 为此,有“ -qconfig最小,较小,中等...”选项。 它包括来自Qt的配置文件,其中包含许多宏-启用/禁用的内容。 在此选项之后,如果我们要禁用其他功能,请向配置中添加其他标志。 这是我们的
配置示例。
为了使Qt正常工作,您需要添加OS兼容性层。 一种方法是实现QPA(Qt平台抽象)。 他们将现成的fb_base插件作为Qt的一部分,基于Linux的QPA。 结果是一个小的emboxfb插件,提供了Embox Qt帧缓冲区,然后它已经在没有任何帮助的情况下绘制了。
这就是插件创建的样子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(); }
因此,这里的重绘看起来 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; }
结果,通过对内存大小-Os进行编译器的优化,该库的映像原来为3.5 MB,这当然不适合STM32F746的主存储器。 正如我们在有关OpenCV的另一篇文章中所写的那样,该委员会拥有:
- 1 MB ROM
- 320 Kb内存
- 8 MB SDRAM
- 16 MB QSPI
由于已经为OpenCV添加了从QSPI执行代码的支持,因此我们决定首先将整个Embox c Qt映像加载到QSPI中。 欢呼,一切几乎都从QSPI开始! 但是与OpenCV一样,结果太慢了。

因此,我们决定这样做-首先将映像复制到QSPI,然后将其加载到SDRAM中并从那里执行。 从SDRAM可以得到更快的速度,但是距离QEMU仍然很远。

接下来的想法是包含一个浮点-因为Qt对动画中正方形的坐标进行了一些计算。 他们尝试了一下,但是并没有看到明显的加速,尽管Qt开发人员在
文章中声称FPU大大提高了触摸屏上“拖动动画”的速度。 也许移动块的浮点计算量大大减少,这取决于特定的示例。
最有效的想法是将帧缓冲区从SDRAM传输到内部存储器。 为此,我们没有480x272的屏幕尺寸,而是272x272。 我们还将颜色深度从A8R8G8B8减少到R5G6B5,从而将一个像素的大小从4个字节减少到2个字节。 我们得到的帧缓冲区大小为272 * 272 * 2 = 147968字节。 这使动画加速了,也许是最引人注目的,几乎变得流畅了。
最后的优化是从RAM执行Embox代码,从SDRAM执行Qt。 为此,我们首先像往常一样,将Embox与Qt静态链接,但是我们将文本,rodata,data和bss库段放置在QSPI中,以便以后将它们复制到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))
由于代码的执行,ROM中的Embox也获得了明显的加速。 结果,动画变得非常流畅:
到最后,在准备文章并尝试Embox的各种配置时,事实证明Qt moveblocks在QSPI和SDRAM中的帧缓冲区上可以很好地工作,并且帧缓冲区的大小是瓶颈! 显然,要克服最初的“幻灯片显示”,由于帧缓冲区大小的过时减小,加速2倍就足够了。 但是,仅通过将Embox代码传输到各种快速存储器中就不可能获得这样的结果(加速度不是2倍,而是1.5倍)。
如何自己尝试
如果您拥有STM32F7-Discovery,则可以自己在Embox下运行Qt。 您可以在我们的
Wiki上阅读如何执行此操作。
结论
结果,我们设法启动了Qt! 我们认为,这项任务的复杂性有些夸大。 自然,您需要考虑微控制器的细节,并通常了解计算系统的体系结构。 优化结果表明了一个众所周知的事实,即计算系统中的瓶颈不是处理器,而是内存。
今年,我们将参加
TechTrain音乐节。 在那里,我们将更详细地讲解并展示微控制器上的Qt,OpenCV以及我们的其他成就。