Портирование 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 по возможности с минимальным набором компонент, требуемым для поддержки анимации. Для этого существует опция “-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::doRedraw\n");
    
        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 оказалось, что работает слишком медленно.



    Поэтому решили делать так — сначала копируем образ в QSPI, затем загружаем его в SDRAM и выполняемся оттуда. Из SDRAM стало немного быстрей, но все равно далеко от QEMU.



    Далее была идея включить плавающую точку — ведь Qt делает некоторые вычисления координат квадратов в анимации. Попробовали, но здесь не получили видимого ускорения, хотя в статье разработчики Qt утверждали, что FPU дает значительный прирост в скорости для “dragging animation” на touchscreen’e. Возможно, в moveblocks существенно меньше вычислений с плавающей точкой, и это зависит от конкретного примера.

    Самым же эффективным оказалась идея перенести фреймбуфер из SDRAM во внутреннюю память. Для этого мы сделали размеры экрана не 480x272, а 272x272. Еще понизили глубину цвета с 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 на микроконтроллерах и прочие наши достижения.
    Embox
    160,73
    Открытая и свободная ОС для встроенных систем
    Поделиться публикацией

    Похожие публикации

    Комментарии 26

      –4
      Господи, и тут уже этот кьют)))
        +9

        Молодцы ребята, респект Вам

          +1

          В посте про Qt, на который Вы сослались использовался Qt5, а у Вас Qt4. Все-таки разница существенная, может сделаете 2й заход?

            +4
            Возможно, они как раз пишут что у них в 5.8 какая-то прям новая система конфигурирования, и вероятно Qt получится еще лучше сконфигурировать. Так что да, надеюсь сделаем 2й заход :)
            0

            А другие, более жизненные примеры есть, с виджетами, например? Ведь чтобы квадраты рисовать таких тяжёлых фреймворков не нужно

              0
              Moveblocks стандартный пример из Qt, так что думаю раз QT запустили, то и виджеты осилим. Виджеты так-то проще чем анимация, на самом деле. Надеюсь, что уже скоро и с виджетами попробуем :)
              +1

              По-моему, дешевле и эффективней подключить МК к малине.
              Мк берет на себя реакцию на события и обработку датчиков. Малина — помогает с велосипедами.
              И железо, и софт, в итоге дешевле. А запас возможностей — сильно выше.
              Все равно, в военпром нельзя даже стм32

                +1
                И железо, и софт, в итоге дешевле.

                А как так получается, что МК+Малина дешевле чем просто MK.

                Если говорить о одиночном изделии или мелкосерийном, вполне возможно Вы правы, ведь разработка займет больше времени. Но если тиражировать тысячами, то экономия в 10 доларов вполне может окупить разработку. В приведенном посте из блога Qt об этом в частности говориться.
                Если учесть, что в статье показано, как существенно сократить затраты на разработку, то есть использовать Qt прямо на микроконтроллере без существенных затрат, то выгода еще очевиднее. И например, существует код Qt, которые заказчик уже написал и отладил, ему хочется использовать более дешевое решение в виде МК, оно меньше потребляет имеет лучшие температурные режимы и так далее. Ну и конечно, устройство должно тиражироваться не в количестве 5 штук.

                Все равно, в военпром нельзя даже стм32

                Речь не о военпроме, но если посмотреть шире, то разрешенные в этом военпроме решения имеют достаточно маленькие ресурсы, например нет даже динамической памяти. Следовательно реально можно использовать системы с несколькими МБ ОЗУ. Ну и аналоги стм-ок в лице миландровких МК все такие есть.
                  +1
                  Raspberry pi zero стоит $5. Для реакции на датчики и прочее, можно подключить что попроще, например cm0, ему не надо городить 8Мб ОЗУ и так далее.

                  Видя скорость прорисовки «анимации» становится не по себе, только и всего.
                    +1
                    Не устраивает скорость прорисовки на результирующем видео? то есть тут

                    Странно, на мой взгляд вполне плавная прорисовка
                      0
                      Извиняюсь, мой косяк. Вчера все было раз в 6 медленее.
                        +1
                        Но при этом, кроме отрисовки пружинящих кубиков, система мало что может сделать.
                        Пример классный, но мне всё же кажется, что есть библиотеки несколько компактнее Qt для задач реализации UI на МК.
                          +1
                          но мне всё же кажется, что есть библиотеки несколько компактнее Qt для задач реализации UI на МК.

                          Абсолютно согласен! про одну nuklear мы даже писали на хабре. Qt же на мой взгляд, сильно больше чем библиотека для рисования GUI-шек. Даже мне кажется что если дело только в GUI то лучше взять другую библиотеку!
                          Но если речь идет об удобной, привычной и мощьной платформе для разработки, данная задача имеет смысл. Или если уже разработано какое то ПО переписывать которое не хочется, точнее дешевле затащить на МК, чем переписывать ПО.

                          Но при этом, кроме отрисовки пружинящих кубиков, система мало что может сделать.

                          Так она и должна делать только то что ей сказано. В данном классе устройств вся функциональность определяется на этапе проектирования. То есть статическая конфигурация, чтобы лишний софт не лез на плату.

                          И да, конечно анимация только демонстрация возможностей. так же можно включить любое ПО на Qt. Embox соберет по файлам проекта (.pro файлам) и будет уже другой функционал.
                            0
                            В том и дело. Я вот не знаю, насколько будут работать модули Qt для работы с сетью (даже если Ethernet есть аппаратно в контроллере) или хотя бы с Serial-портом.

                            Допустим, у меня есть хорошая годная утилитка, работающая в качестве панели управления станком: github.com/Denvi/Candle — исходный код в несколько сотен КБ, собранное приложение занимает несколько мегабайт, хотя внутри оно не очень сложное, как раз подходит для какого-нибудь M4 — небольшой буфер команд, внутреннее состояние станка, окей, есть визуализация траекторий в 3D.

                            Мне почему-то кажется, что на сборку форка этой панели уйдёт намного больше времени, чем написание кода с той же функциональностью с Nuklear + Tiny3DEngine.
                              0
                              Мне почему-то кажется, что на сборку форка этой панели уйдёт намного больше времени, чем написание кода с той же функциональностью с Nuklear + Tiny3DEngine.

                              Ну если цель запуск то нужно просто собрать данный проект в качестве приложения Embox. правда скорость отрисовки 3д сцен может сильно не обрадовать, но боюсь тоже самое будет и с Tiny3DEngine.

                              Я вот не знаю, насколько будут работать модули Qt для работы с сетью (даже если Ethernet есть аппаратно в контроллере) или хотя бы с Serial-портом.

                              По нашему опыту все работает очень прилично, так же как и на хосте. Запускали более серьезное приложение animated tiles (там почти 3д) по vnc (qt плагин из коробки) на большом ARM-е. Все прекрасно, правда памяти требует побольше.
                                +1
                                3D там нужно для визуализации сотни линий и положения сверла, вид по умолчанию можно сделать вообще сверху. Пусть тормозит на здоровье. На Raspberry у меня вообще пока не получилось libmesa поднять, вместо сцены чёрный прямоугольник.

                                Хм, спасибо, когда окажется в руках плата с экраном и внешней RAM — попробую, убедили :)
                                  0
                                  Обращайтесь :) Постараемся помочь если возникнут проблемы.

                                  Мы тоже попробуем портировать https://github.com/Denvi/Candle, но не прямо сейчас.
                  0
                  «Однако до недавних пор, мало кто задумывался о портировании Qt на микроконтроллеры» — Qt на микроконтроллерах уже достаточно давно. В 2018 показывали много демок на Embedded World прямо на девайсах (в основном qml, но виджеты тоже работают). Есть еще Boot2Qt специально для микроконтроллеров.
                    +1
                    А я об этом в статье упоминал — blog.qt.io/blog/2018/05/03/qt-microncontrollers-mcu
                      +1
                      Но все-таки я бы не сказал, что они там давно, я не видел версию работающую из коробки, они тоже только демки показывают
                      +1
                      Совсем не давно я закончил пушить патчи в Qt dev для поддержки RTEMS из коробки. QPA плагин к сожалению останеться commercial only, но в целом можно использовать linuxfb.
                        +1
                        Очень круто! :) То есть уже сейчас можно попробовать самому под RTEMS запустить Qt с linuxfb?

                        И еще такой вопрос, возможно ли добавление также порта Qt под Embox (я имею ввиду QPA части и мб конфигов), если, например, получим показатели не хуже чем на RTEMS? Не вместо RTEMS конечно, а как альтернативу.
                          +1
                          Да, в целом можно.

                          Я думаю что это возможно, но я бы для начало рекомендовал написать в developer mail list.
                          А почему бы вам не использовать linuxfb плагин? Судя по коду который вы показли, он очень похож на него.
                            0

                            Пока решили упростить qpa плагин, дабы не тащить ничего лишнего. Но вероятно попробуем linuxfb как только начнём добавлять поддержку прерываний от touchscreen'а для примеров с виджетами. Пока не смотрели как там прерывания пробрасываются в qt по-хорошему (какой-то плагин с мышью и клавиатурой у нас есть, но весьма костыльный).

                        +4
                        Хотел внести несколько комментариев/предложений.
                        1. Касательно FPU. Вы используете плату STM32F746. У данной платы FPU c single precision. Этого не достатчно для JIT компиляции в QML engine.
                        QML engine требует FPU c double precision. Поэтому вы и не увидили никакого прироста (наверно в блоге я не достатчно точно это написал).
                        2. У STM32F746 есть небольшой графический ускоритель DMA2D, рекомендовал бы добавить его поддержку. Сможете получить небольшой прирост.
                        3. Ешё STM32F746 есть L1 cache, так же рекомендовал бы поиграться с настройками. Сможете получить еще значительное ускорение. (Может вы уже его используете, извиняюсь, но я не увидел в статье упоминания)
                          0
                          Спасибо! Пока до этих моментов не добрались, оптимизировали исключительно за счет вынесения разных частей в быстрые памяти, но думаю посмотрим.

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое