Dober večer Smo v projektu
Qt je večplatformsko ogrodje, ki ne vključuje samo grafičnih komponent, temveč tudi stvari, kot je QtNetwork, niz razredov za delo z bazami podatkov, Qt za avtomatizacijo (vključno z implementacijo IoT) in še veliko več. Ekipa Qt je bila proaktivna glede uporabe Qt v vgrajenih sistemih, zato so knjižnice precej nastavljive. Vendar je do nedavnega le malo ljudi razmišljalo o prenosu Qt na mikrokontrolerje, verjetno zato, ker se zdi takšna naloga težka - Qt je velik, MCU-ji so majhni.
Po drugi strani pa trenutno obstajajo mikrokrmilniki, ki so zasnovani za delo z multimedijo in so boljši od prvih Pentiumov. Pred približno letom dni se je pojavil Qt blog
Qt 4.8 je že dolgo časa prenesen na Embox, zato smo se odločili, da ga preizkusimo na njem. Izbrali smo aplikacijo moveblocks - primer pomladne animacije.
Qt moveblocks na QEMU
Za začetek konfiguriramo Qt, če je mogoče, z minimalnim naborom komponent, potrebnih za podporo animacije. Za to obstaja možnost "-qconfig minimalno, majhno, srednje ...". Povezuje konfiguracijsko datoteko iz Qt s številnimi makri - kaj omogočiti / kaj onemogočiti. Po tej možnosti konfiguraciji dodamo še druge zastavice, če želimo onemogočiti še kaj. Tukaj je naš primer
Da bo Qt deloval, morate dodati plast združljivosti OS. Eden od načinov je implementacija QPA (Qt Platform Abstraction). Za osnovo smo vzeli že pripravljen vtičnik fb_base, vključen v Qt, na podlagi katerega deluje QPA za Linux. Rezultat je majhen vtičnik, imenovan emboxfb, ki Qt-u zagotovi okvirni medpomnilnik Embox, nato pa tja riše brez zunanje pomoči.
Takole izgleda ustvarjanje vtičnika
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();
}
In takole bo videti prerisovanje
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;
}
Kot rezultat, z omogočeno optimizacijo prevajalnika za velikost pomnilnika -Os, se je slika knjižnice izkazala za 3.5 MB, kar seveda ne ustreza glavnemu pomnilniku STM32F746. Kot smo že pisali v našem drugem članku o OpenCV, ima ta plošča:
- 1 MB ROM
- 320 KB RAM
- 8 MB SDRAM
- 16 MB QSPI
Ker je bila OpenCV že dodana podpora za izvajanje kode iz QSPI, smo se odločili, da začnemo z nalaganjem celotne slike Embox c Qt v QSPI. In hura, vse se je začelo skoraj takoj iz QSPI! A kot v primeru OpenCV se je izkazalo, da deluje prepočasi.
Zato smo se odločili, da to storimo na ta način - najprej kopiramo sliko v QSPI, nato jo naložimo v SDRAM in izvedemo od tam. Iz SDRAM-a je postal nekoliko hitrejši, a še vedno daleč od QEMU.
Nato se je pojavila ideja, da bi vključili plavajočo vejico - navsezadnje Qt naredi nekaj izračunov koordinat kvadratov v animaciji. Poskušali, a tukaj nismo dobili vidnejših pospeškov, čeprav v
Najučinkovitejša ideja je bila premakniti okvirni medpomnilnik iz SDRAM v notranji pomnilnik. Da bi to naredili, smo naredili dimenzije zaslona ne 480x272, ampak 272x272. Zmanjšali smo tudi barvno globino z A8R8G8B8 na R5G6B5 in tako zmanjšali velikost ene slikovne pike s 4 na 2 bajta. Nastala velikost medpomnilnika okvirjev je 272 * 272 * 2 = 147968 bajtov. To je znatno pospešilo, morda najbolj opazno je, da je animacija postala skoraj gladka.
Najnovejša optimizacija je bila zagon kode Embox iz RAM-a in kode Qt iz SDRAM-a. Da bi to naredili, najprej, kot običajno, statično povežemo Embox skupaj s Qt, vendar segmente besedila, rodata, podatkov in bss knjižnice postavimo v QSPI, da jih nato kopiramo v 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))
Z izvajanjem kode Embox iz ROM-a smo bili deležni tudi opaznega pospeška. Posledično se je animacija izkazala za precej gladko:
Čisto na koncu, med pripravo članka in preizkušanjem različnih Embox konfiguracij, se je izkazalo, da Qt moveblocks odlično deluje iz QSPI z framebufferjem v SDRAM, ozko grlo pa je bila ravno velikost framebufferja! Očitno je bilo za premagovanje začetne "diaprojekcije" dovolj 2-kratni pospešek zaradi banalnega zmanjšanja velikosti medpomnilnika okvirjev. Toda takšnega rezultata ni bilo mogoče doseči s prenosom samo kode Embox v različne hitre pomnilnike (pospešek ni bil 2, ampak približno 1.5-krat).
Kako poskusiti sami
Če imate STM32F7-Discovery, lahko sami zaženete Qt pod Emboxom. Kako to poteka, si lahko preberete na našem
Zaključek
Kot rezultat nam je uspelo zagnati Qt! Kompleksnost naloge je po našem mnenju nekoliko pretirana. Seveda morate upoštevati posebnosti mikrokontrolerjev in na splošno razumeti arhitekturo računalniških sistemov. Rezultati optimizacije kažejo na dobro znano dejstvo, da ozko grlo v računalniškem sistemu ni procesor, temveč pomnilnik.
Letos se bomo festivala udeležili
Vir: www.habr.com