下午好我们在项目中
Qt 是一个跨平台框架,不仅包括图形组件,还包括 QtNetwork、一组用于处理数据库的类、Qt for Automation(包括用于 IoT 实现)等。 Qt 团队一直积极主动地在嵌入式系统中使用 Qt,因此这些库是非常可配置的。 然而,直到最近,很少有人考虑将 Qt 移植到微控制器,可能是因为这样的任务看起来很困难——Qt 很大,MCU 很小。
另一方面,目前有一些微控制器设计用于多媒体工作,并且优于第一代奔腾。 大约一年前,Qt博客出现了
Qt 4.8 已经移植到 Embox 很长时间了,所以我们决定在上面尝试一下。 我们选择了 moveblocks 应用程序 - 弹性动画的一个示例。
QEMU 上的 Qt moveblocks
首先,如果可能的话,我们使用支持动画所需的最少组件集来配置 Qt。 为此,有一个选项“-qconfig minus,small,medium...”。 它将 Qt 的配置文件与许多宏连接起来——启用什么/禁用什么。 在此选项之后,如果我们想禁用其他功能,我们可以在配置中添加其他标志。 这是我们的一个例子
为了让 Qt 工作,您需要添加操作系统兼容层。 一种方法是实现 QPA(Qt 平台抽象)。 我们以 Qt 中包含的现成 fb_base 插件为基础,QPA for Linux 在此基础上工作。 结果是一个名为 emboxfb 的小插件,它为 Qt 提供 Embox 的帧缓冲区,然后在没有任何外部帮助的情况下在那里进行绘制。
这就是创建插件的样子
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::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;
}
结果,在启用内存大小 -Os 的编译器优化后,库映像大小为 3.5 MB,这当然不适合 STM32F746 的主内存。 正如我们在另一篇有关 OpenCV 的文章中所写的,该板具有:
- 1MB 只读存储器
- 320KB 内存
- 8MB内存
- 16MB QSPI
由于 OpenCV 中已经添加了对从 QSPI 执行代码的支持,因此我们决定首先将整个 Embox c Qt 图像加载到 QSPI 中。 万岁,一切几乎都是从 QSPI 立即开始的! 但正如 OpenCV 的情况一样,事实证明它的运行速度太慢了。
因此,我们决定这样做 - 首先我们将图像复制到 QSPI,然后将其加载到 SDRAM 并从那里执行。 与 SDRAM 相比,它变得更快了一点,但与 QEMU 仍然相差甚远。
接下来,有一个想法是包含一个浮点 - 毕竟,Qt 对动画中的正方形坐标进行了一些计算。 我们尝试过,但在这里我们没有得到任何明显的加速,尽管在
最有效的想法是将帧缓冲区从 SDRAM 移至内部存储器。 为此,我们将屏幕尺寸设置为 480x272,而不是 272x272。 我们还将颜色深度从 A8R8G8B8 降低到 R5G6B5,从而将一个像素的大小从 4 字节减少到 2 字节。 生成的帧缓冲区大小为 272 * 272 * 2 = 147968 字节。 这提供了显着的加速,也许最引人注目的是,动画变得几乎平滑。
最新的优化是从 RAM 运行 Embox 代码,从 SDRAM 运行 Qt 代码。 为此,我们首先像往常一样将 Embox 与 Qt 静态链接在一起,但我们将库的文本、rodata、数据和 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 在 SDRAM 中具有帧缓冲区的 QSPI 中工作得很好,而瓶颈恰恰是帧缓冲区的大小! 显然,为了克服最初的“幻灯片放映”,由于帧缓冲区大小的平庸减少,2 倍加速就足够了。 但仅仅将 Embox 代码转移到各种快速存储器中是不可能达到这样的结果的(加速不是 2 倍,而是大约 1.5 倍)。
如何自己尝试一下
如果您有 STM32F7-Discovery,您可以自己在 Embox 下运行 Qt。 您可以在我们的网站上阅读这是如何完成的
结论
结果,我们成功启动了 Qt! 我们认为,这项任务的复杂性有些被夸大了。 当然,您需要考虑微控制器的具体情况并大致了解计算机系统的体系结构。 优化结果指出了一个众所周知的事实:计算系统的瓶颈不是处理器,而是内存。
今年我们将参加这个节日
来源: habr.com