Pag-port ng Qt sa STM32

Pag-port ng Qt sa STM32Magandang hapon Nasa project kami Embox inilunsad ang Qt sa STM32F7-Discovery at gustong pag-usapan ito. Kanina, sinabi na namin kung paano kami nakapag-launch OpenCV.

Ang Qt ay isang cross-platform framework na kinabibilangan hindi lamang ng mga graphical na bahagi, kundi pati na rin ang mga bagay tulad ng QtNetwork, isang set ng mga klase para sa pagtatrabaho sa mga database, Qt para sa Automation (kabilang ang para sa pagpapatupad ng IoT) at marami pa. Naging maagap ang koponan ng Qt tungkol sa paggamit ng Qt sa mga naka-embed na system, kaya medyo na-configure ang mga library. Gayunpaman, hanggang kamakailan lamang, kakaunti ang nag-isip tungkol sa pag-port ng Qt sa mga microcontroller, marahil dahil mukhang mahirap ang ganoong gawain - malaki ang Qt, maliit ang mga MCU.

Sa kabilang banda, sa ngayon ay may mga microcontroller na idinisenyo upang gumana sa multimedia at higit na mataas sa mga unang Pentium. Mga isang taon na ang nakalipas, lumitaw ang Qt blog magpaskil. Ang mga developer ay gumawa ng port ng Qt para sa RTEMS OS, at naglunsad ng mga halimbawa na may mga widget sa ilang mga board na tumatakbo sa stm32f7. Ito ay interesado sa amin. Kapansin-pansin, at ang mga developer mismo ang sumulat tungkol dito, na ang Qt ay mabagal sa STM32F7-Discovery. Kami ay nagtataka kung maaari naming patakbuhin ang Qt sa ilalim ng Embox, at hindi lamang gumuhit ng isang widget, ngunit magpatakbo ng isang animation.

Matagal nang nai-port ang Qt 4.8 sa Embox, kaya nagpasya kaming subukan ito. Pinili namin ang application na moveblocks - isang halimbawa ng springy animation.

Qt moveblocks sa QEMUPag-port ng Qt sa STM32

Upang magsimula sa, i-configure namin ang Qt, kung maaari, na may pinakamababang hanay ng mga bahagi na kinakailangan upang suportahan ang animation. Para dito mayroong isang pagpipilian "-qconfig minimal, maliit, katamtaman...". Ikinokonekta nito ang isang configuration file mula sa Qt na may maraming macro - kung ano ang paganahin / kung ano ang hindi paganahin. Pagkatapos ng opsyong ito, nagdaragdag kami ng iba pang mga flag sa configuration kung gusto naming huwag paganahin ang ibang bagay. Narito ang isang halimbawa ng aming pagsasaayos.

Upang gumana ang Qt, kailangan mong magdagdag ng layer ng compatibility ng OS. Ang isang paraan ay ang pagpapatupad ng QPA (Qt Platform Abstraction). Kinuha namin bilang batayan ang handa na fb_base plugin na kasama sa Qt, batay sa kung saan gumagana ang QPA para sa Linux. Ang resulta ay isang maliit na plugin na tinatawag na emboxfb, na nagbibigay sa Qt ng framebuffer ng Embox, at pagkatapos ay gumuhit ito doon nang walang anumang tulong sa labas.

Ito ang hitsura ng paglikha ng isang plugin

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();
}

At ito ang magiging hitsura ng muling pagguhit

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;
}

Bilang isang resulta, kapag pinagana ang compiler optimization para sa laki ng memorya -Os, ang imahe ng library ay naging 3.5 MB, na siyempre ay hindi magkasya sa pangunahing memorya ng STM32F746. Tulad ng naisulat na namin sa aming iba pang artikulo tungkol sa OpenCV, ang board na ito ay may:

  • 1 MB ROM
  • 320 KB na RAM
  • 8 MB SDRAM
  • 16 MB QSPI

Dahil ang suporta para sa pagpapatupad ng code mula sa QSPI ay naidagdag na sa OpenCV, nagpasya kaming magsimula sa pamamagitan ng paglo-load ng buong Embox c Qt na imahe sa QSPI. At hurray, halos kaagad nagsimula ang lahat mula sa QSPI! Ngunit tulad ng sa kaso ng OpenCV, ito ay naging masyadong mabagal.

Pag-port ng Qt sa STM32

Samakatuwid, nagpasya kaming gawin ito sa ganitong paraan - una naming kopyahin ang imahe sa QSPI, pagkatapos ay i-load ito sa SDRAM at isagawa mula doon. Mula sa SDRAM naging mas mabilis ito, ngunit malayo pa rin sa QEMU.

Pag-port ng Qt sa STM32

Susunod, nagkaroon ng ideya na isama ang isang lumulutang na punto - pagkatapos ng lahat, ang Qt ay gumagawa ng ilang mga kalkulasyon ng mga coordinate ng mga parisukat sa animation. Sinubukan namin, ngunit dito hindi kami nakakuha ng anumang nakikitang acceleration, bagaman nasa Artikulo Sinabi ng mga developer ng Qt na ang FPU ay nagbibigay ng makabuluhang pagtaas sa bilis para sa "pag-drag ng animation" sa touchscreen. Maaaring may mas kaunting mga kalkulasyon ng floating point sa mga moveblock, at depende ito sa partikular na halimbawa.

Ang pinaka-epektibong ideya ay upang ilipat ang framebuffer mula sa SDRAM sa panloob na memorya. Upang gawin ito, ginawa namin ang mga sukat ng screen na hindi 480x272, ngunit 272x272. Ibinaba din namin ang lalim ng kulay mula A8R8G8B8 hanggang R5G6B5, kaya binabawasan ang laki ng isang pixel mula 4 hanggang 2 byte. Ang resultang laki ng framebuffer ay 272 * 272 * 2 = 147968 bytes. Nagbigay ito ng isang makabuluhang acceleration, marahil pinaka-kapansin-pansin, ang animation ay naging halos makinis.

Ang pinakabagong pag-optimize ay ang patakbuhin ang Embox code mula sa RAM at Qt code mula sa SDRAM. Upang gawin ito, una namin, gaya ng dati, statically link na Embox kasama ng Qt, ngunit inilalagay namin ang teksto, rodata, data at bss segment ng library sa QSPI upang pagkatapos ay kopyahin ito sa 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))

Sa pamamagitan ng pag-execute ng Embox code mula sa ROM, nakatanggap din kami ng kapansin-pansing acceleration. Bilang isang resulta, ang animation ay naging medyo makinis:


Sa pinakadulo, habang inihahanda ang artikulo at sinusubukan ang iba't ibang mga pagsasaayos ng Embox, lumabas na ang mga Qt moveblock ay mahusay na gumagana mula sa QSPI na may isang framebuffer sa SDRAM, at ang bottleneck ay eksaktong sukat ng framebuffer! Tila, upang mapagtagumpayan ang paunang "slideshow", isang 2-tiklop na acceleration ay sapat na dahil sa isang banal na pagbawas sa laki ng framebuffer. Ngunit hindi posible na makamit ang gayong resulta sa pamamagitan ng paglilipat lamang ng Embox code sa iba't ibang mabilis na alaala (ang bilis ay hindi 2, ngunit mga 1.5 beses).

Paano subukan ito sa iyong sarili

Kung mayroon kang STM32F7-Discovery, maaari mong patakbuhin ang Qt sa ilalim ng Embox. Mababasa mo kung paano ito ginagawa sa aming wiki.

Konklusyon

Bilang resulta, nagawa naming ilunsad ang Qt! Ang pagiging kumplikado ng gawain, sa aming opinyon, ay medyo pinalaki. Naturally, kailangan mong isaalang-alang ang mga detalye ng microcontrollers at sa pangkalahatan ay maunawaan ang arkitektura ng mga computer system. Ang mga resulta ng pag-optimize ay tumutukoy sa kilalang katotohanan na ang bottleneck sa isang computing system ay hindi ang processor, ngunit ang memorya.

Sa taong ito tayo ay lalahok sa pagdiriwang TechTrain. Doon ay sasabihin namin sa iyo nang mas detalyado at ipapakita ang Qt, OpenCV sa mga microcontroller at iba pa naming mga nagawa.

Pinagmulan: www.habr.com

Magdagdag ng komento