Партаванне Qt на STM32

Партаванне Qt на STM32Добры дзень! Мы ў праекце Embox запусцілі Qt на STM32F7-Discovery і хацелі б пра гэта расказаць. Раней, мы ўжо расказвалі як нам удалося запусціць OpenCV.

Qt - гэта кросплатформавы фрэймворк, які складаецца з не толькі графічныя кампаненты, але і такія рэчы як QtNetwork, набор класаў для працы з базамі дадзеных, Qt for Automation (у тым ліку, для рэалізацыі IoT) і шматлікае іншае. Распрацоўнікі каманды Qt загадзя прадугледзелі выкарыстанне Qt ва ўбудаваных сістэмах, таму бібліятэкі даволі добра канфігуруюцца. Аднак да нядаўніх часоў, мала хто задумляўся аб партаванні Qt на мікракантролеры, верагодна таму, што такая задача выглядае складанай – Qt вялікае, MCU маленькія.

З іншага боку, на дадзены момант існуюць мікракантролеры, прызначаныя для працы з мультымедыя і праўзыходныя першыя Pentium-ы. Каля года таму ў блогу Qt з'явіўся пост. Распрацоўнікі зрабілі порт Qt пад АС RTEMS, і запусцілі прыклады з віджэтамі на некалькіх поплатках пад кіраваннем stm32f7. Нас гэта зацікавіла. Было прыкметна, і самі распрацоўшчыкі пра гэта пішуць, што Qt тармозіць на STM32F7-Discovery. Нам стала цікава, ці зможам мы запусціць Qt пад Embox, пры гэтым не проста намаляваць віджэт, а запусціць анімацыю.

У Embox ужо даўно партавана Qt 4.8, таму вырашылі паспрабаваць на ім. Выбралі прыкладанне moveblocks - прыклад спружыністай анімацыі.

Qt moveblocks на QEMUПартаванне Qt на STM32

Для пачатку які канфігуруецца Qt па магчымасці з мінімальным наборам кампанент, патрабаваным для падтрымкі анімацыі. Для гэтага існуе опцыя "-qconfig minimal,small,medium …". Яна падлучае канфігурацыйны файл са складу Qt c мноствам макрасаў - што ўключыць / што адключыць. Пасля гэтай опцыі дадаем у канфігурацыю іншыя сцягі, калі жадаем яшчэ нешта адключыць дадаткова. Вось прыклад нашай канфігурацыі.

Для таго, каб Qt зарабіла, трэба дадаць пласт сумяшчальнасці з АС. Адзін са спосабаў - рэалізаваць QPA (Qt Platform Abstraction). За аснову ўзялі ўжо гатовы ўбудова fb_base у складзе Qt, на базе якога працуе QPA для Лінукс. У выніку атрымаўся невялікі плягін emboxfb, які дае Qt фрэймбуфер Embox'a, а далей яно малюе туды ўжо без старонняй дапамогі.

Вось так выглядае стварэнне плагіна

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 Мб, што вядома не залазіць у асноўную памяць STM32F746. Як мы ўжо пісалі ў нашым іншым артыкуле пра OpenCV, на гэтай плаце маецца:

  • 1 Мб ROM
  • 320 Кб RAM
  • 8 Мб SDRAM
  • 16 Мб QSPI

Бо для OpenCV ужо была дададзеная падтрымка выканання кода з QSPI, мы вырашылі пачаць з таго, што загрузілі выяву Embox c Qt у QSPI цалкам. І ўра, усё амаль адразу ж запусцілася з QSPI! Але як і ў выпадку з OpenCV аказалася, што працуе занадта павольна.

Партаванне Qt на STM32

Таму вырашылі рабіць так – спачатку які капіюецца выява ў QSPI, затым загружаны яго ў SDRAM і выконваемся адтуль. З SDRAM стала крыху хутчэй, але ўсё роўна далёка ад QEMU.

Партаванне Qt на STM32

Далей была ідэя ўключыць плаваючую кропку - бо Qt робіць некаторыя вылічэнні каардынат квадратаў у анімацыі. Паспрабавалі, але тут не атрымалі бачнага паскарэння, хаця ў артыкуле распрацоўшчыкі Qt сцвярджалі, што FPU дае значны прырост у хуткасці для "dragging animation" на touchscreen'e. Магчыма, у moveblocks істотна менш вылічэнняў з якая плавае кропкай, і гэта залежыць ад канкрэтнага прыкладу.

Самым жа эфектыўным аказалася ідэя перанесці фрэймбуфер з SDRAM ва ўнутраную памяць. Для гэтага мы зрабілі памеры экрана не 480×272, а 272×272. Яшчэ панізілі глыбіню колеру з A8R8G8B8 да R5G6B5, такім чынам скараціўшы памер аднаго пікселя з 4 да 2 байт. Атрымалі памер фрэймбуфера 272*272*2 = 147968 байт. Гэта дало значнае паскарэнне, бадай, самае прыкметнае, анімацыя стала амаль плаўнай.

Апошняй аптымізацыяй стала выкананне кода Embox з RAM, а Qt з SDRAM. Для гэтага мы спачатку як звычайна лінкуем статычна Embox разам з Qt, але сегменты text, 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))

За рахунак выканання кода Embox з ROM таксама атрымалі адчувальнае паскарэнне. У выніку анімацыя атрымалася дастаткова плаўнай:


Ужо ў самым канцы, падрыхтоўваючы артыкул і спрабуючы розныя канфігурацыі Embox'a, высветлілася, што Qt moveblocks выдатна працуе і з QSPI з фрэймбуферам у SDRAM, а вузкім месцам быў менавіта памер фрэймбуфера! Відаць, каб пераадолець пачатковае "слайдшоў" хапала паскарэння ў 2 разы за кошт банальнага памяншэння памеру фрэймбуфера. А дамагчыся такога выніку пераносам толькі кода Embox у розныя хуткія памяці не атрымалася (паскарэнне атрымлівалася не ў 2, а прыкладна ў 1.5 разу).

Як паспрабаваць самому

Калі ў Вас ёсць STM32F7-Discovery, Вы можаце запусціць Qt пад Embox самі. Прачытаць як гэта робіцца можна на нашым вікі.

Заключэнне

У выніку нам удалося запусціць Qt! Складанасць задачы, на наш погляд, крыху перабольшана. Натуральна трэба ўлічваць спецыфіку мікракантролераў і ўвогуле разумець архітэктуру вылічальных сістэм. Вынікі аптымізацыі паказваюць на вядомы факт, што самае вузкае месца ў вылічальнай сістэме, гэта не працэсар, а памяць.

У гэтым годзе мы будзем удзельнічаць на фестывалі TechTrain. Там мы падрабязней раскажам і пакажам Qt, OpenCV на мікракантролерах і іншыя нашы дасягненні.

Крыніца: habr.com

Дадаць каментар