午安我們在專案中
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! 我們認為,這項任務的複雜性有些被誇大了。 當然,您需要考慮微控制器的具體情況並大致了解電腦系統的體系結構。 最佳化結果指出了一個眾所周知的事實:計算系統的瓶頸不是處理器,而是記憶體。
今年我們將參加這個節日
來源: www.habr.com