Мы в проекте Embox некоторое время назад запустили Qt на платформе STM32. Примером было приложение moveblocks — анимация с четырьмя синими квадратами, которые перемещаются по экрану. Нам захотелось большего, например, добавить интерактивность, ведь на плате доступен тачскрин. Мы выбрали приложение animatedtiles просто потому, что оно и на компьютере круто смотрится. По нажатию виртуальных кнопок множество иконок плавно перемещаются по экрану, собираясь в различные фигуры. Причем выглядит это вполне как 3d анимация и у нас даже были сомнения, справится ли микроконтроллер с подобной задачей.
Первым делом начинаем со сборки. Это проще всего делать на эмуляторе QEMU, т.к. там все точно влезает по памяти, и поэтому легко проверить всех ли компонентов хватает для сборки приложения. Проверив, что все animatedtiles успешно собралось, я легко перенес его в конфигурацию для нужной мне платы.
Размер экрана у STM32F746G-Discovery 480x272, при запуске приложение нарисовалось только в верхнюю часть экрана. Нам естественно захотелось выяснить в чем дело. Конечно можно уйти в отладку прямо на плате, но есть более простое решение. запустить приложение на Линукс с теми же самыми размерами 480x272 с виртуальным фреймбуфером QVFB.
Для запуска на Linux нам потребуется три части QVFB, библиотека Qt, и само приложение.
QVFB это обычное приложение, которое предоставит нам виртуальный экран для работы Qt. Собираем его как написано в официальной документации.
Запускаем с нужным размером экрана:
Далее, собираем библиотеку Qt как embedded, т.е. С указанием опции -embedded. Я еще отключил разные модули для ускорения сборки, в итоге конфигурация выглядела вот так:
Далее собираем приложение animatedtiles (qmake + make). И запускаем скомпилированное приложение, указав ему наш QVFB:
После запуска я увидел, что на Линуксе также рисуется только в часть экрана. Я немного доработал animatedtiles, добавив опцию “-fullscreen”, при указании которой приложение стартует в полноэкранном режиме.
Модифицированный исходный код приложения будем использовать в Embox. Пересобираем и запускаем. Приложение не запустилось, при этом появились сообщения о нехватке памяти в Qt. Смотрим в конфигурацию Embox и находим что размер кучи установлен 2Мб и его явно не хватает. Опять же, можно попробовать выяснить этот момент с прямо на плате, но давайте сделаем это с удобствами в Линукс.
Для этого запускаем приложение следующим образом:
В файле animatedtiles.out видим максимальное значение заполненности кучи порядка 2.7 Мб. Отлично, теперь можно не гадать, а вернуться в Embox и поставить размер кучи 3Мб.
Animatedtiles запустилось.
Давайте попробуем еще усложнить задачу, и запустим тот же пример на подобном микроконтроллере, но только с большим разрешением экрана — STM32F769I-Discovery (800x480). То есть теперь под фреймбуфер потребуется в 1.7 раз больше памяти (напомню, что у STM32F746G экран 480x272), но это компенсируется в два раза большим размером SDRAM (16 Мб против 8Мб доступной памяти SDRAM у STM32F746G).
Для оценки размера кучи, как и выше, сначала запускаем Qvfb и наше приложение на Линуксе:
Смотрим расход памяти в куче — около 6 МБ (почти в два раза больше, чем на STM32F746G).
Осталось выставить нужный размер кучи в mods.conf и пересобрать. Приложение запустилось сразу и без проблем, что и продемонстрировано этом коротком видео
Традиционно можно воспроизвести результаты самостоятельно. Как это сделать описано у нас на wiki.
Данная статья впервые была нами опубликована на английском языке на embedded.com.
Сборка
Первым делом начинаем со сборки. Это проще всего делать на эмуляторе QEMU, т.к. там все точно влезает по памяти, и поэтому легко проверить всех ли компонентов хватает для сборки приложения. Проверив, что все animatedtiles успешно собралось, я легко перенес его в конфигурацию для нужной мне платы.
Первый запуск на плате
Размер экрана у STM32F746G-Discovery 480x272, при запуске приложение нарисовалось только в верхнюю часть экрана. Нам естественно захотелось выяснить в чем дело. Конечно можно уйти в отладку прямо на плате, но есть более простое решение. запустить приложение на Линукс с теми же самыми размерами 480x272 с виртуальным фреймбуфером QVFB.
Запускаем на Линукс
Для запуска на Linux нам потребуется три части QVFB, библиотека Qt, и само приложение.
QVFB это обычное приложение, которое предоставит нам виртуальный экран для работы Qt. Собираем его как написано в официальной документации.
Запускаем с нужным размером экрана:
./qvfb -width 480 -height 272 -nocursor
Далее, собираем библиотеку Qt как embedded, т.е. С указанием опции -embedded. Я еще отключил разные модули для ускорения сборки, в итоге конфигурация выглядела вот так:
./configure -opensource -confirm-license -debug -embedded -qt-gfx-qvfb -qvfb -no-javascript-jit -no-script -no-scripttools -no-qt3support -no-webkit -nomake demos -nomake examples
Далее собираем приложение animatedtiles (qmake + make). И запускаем скомпилированное приложение, указав ему наш QVFB:
./examples/animation/animatedtiles/animatedtiles -qws -display QVFb:0
После запуска я увидел, что на Линуксе также рисуется только в часть экрана. Я немного доработал animatedtiles, добавив опцию “-fullscreen”, при указании которой приложение стартует в полноэкранном режиме.
Запуск на Embox
Модифицированный исходный код приложения будем использовать в Embox. Пересобираем и запускаем. Приложение не запустилось, при этом появились сообщения о нехватке памяти в Qt. Смотрим в конфигурацию Embox и находим что размер кучи установлен 2Мб и его явно не хватает. Опять же, можно попробовать выяснить этот момент с прямо на плате, но давайте сделаем это с удобствами в Линукс.
Для этого запускаем приложение следующим образом:
$ valgrind --tool=massif --massif-out-file=animatedtiles.massif ./examples/animation/animatedtiles/animatedtiles -qws -fullscreen
$ ms_print animatedtiles.massif > animatedtiles.out
В файле animatedtiles.out видим максимальное значение заполненности кучи порядка 2.7 Мб. Отлично, теперь можно не гадать, а вернуться в Embox и поставить размер кучи 3Мб.
Animatedtiles запустилось.
Запуск на STM32F769I-Discovery.
Давайте попробуем еще усложнить задачу, и запустим тот же пример на подобном микроконтроллере, но только с большим разрешением экрана — STM32F769I-Discovery (800x480). То есть теперь под фреймбуфер потребуется в 1.7 раз больше памяти (напомню, что у STM32F746G экран 480x272), но это компенсируется в два раза большим размером SDRAM (16 Мб против 8Мб доступной памяти SDRAM у STM32F746G).
Для оценки размера кучи, как и выше, сначала запускаем Qvfb и наше приложение на Линуксе:
$ ./qvfb -width 800 -height 480 -nocursor &
$ valgrind --tool=massif --massif-out-file=animatedtiles.massif ./examples/animation/animatedtiles/animatedtiles -qws -fullscreen
$ ms_print animatedtiles.massif > animatedtiles.out
Смотрим расход памяти в куче — около 6 МБ (почти в два раза больше, чем на STM32F746G).
Осталось выставить нужный размер кучи в mods.conf и пересобрать. Приложение запустилось сразу и без проблем, что и продемонстрировано этом коротком видео
Традиционно можно воспроизвести результаты самостоятельно. Как это сделать описано у нас на wiki.
Данная статья впервые была нами опубликована на английском языке на embedded.com.
EddyEm
Qt — невменяемая жиробасина с кучей оверхеда. Она даже на десктопе не нужна, т.к. есть более легковесные и вменяемые библиотеки для виджетов. А уж на микроконтроллере этой дряни точно не место!
Про C++ вместо C вообще молчу…
abondarev Автор
Ну некоторые с вами не согласятся. Особенно на счет C++.
На счет виджетов, в принципе согласен, если нужны только виджеты, есть более легкие альтернативы, например LVGL, тоже поддержанный в Embox. Но Qt это очень крутой фреймворк, и используют его не только ради виджетов. И например, если уже есть код на Qt то его переписывание может оказаться сложной задачей. В статье же показана возможность, разработать и отладить ПО на линуксе, а затем запустить на целевой платформе.
devprodest
Голубчик, про плюсы это вы зря. Если грамотно использовать, то на много интереснее и удобнее выходит.
abondarev Автор
Да, согласен. Но нужно грамотно использовать :)
И не нужно забывать что некоторые вещи недоступны bare-metal
Gromin
Немного интересуюсь этой темой. Подскажите, пожалуйста, где именно в QT куча оверхеда? И какие можно взять альтернативы (с соизмеримыми возможностями, конечно)?
Тут, я так понимаю, за основу взят очень старый Qt 4.8. С 5 версии они серьезно переработали потроха и, к тому же, у них есть специальная версия Qt для MCU. Увы, платная, но успешно применяемая.
А можете чуть подробнее помолчать? Чем именно C++ не подходит для контроллеров? Только, пожалуйста, не надо про «больший размер бинарников» и «использование динамической памяти».
Первое — чушь, доказательства даже на Хабре были. Например: habr.com/ru/post/347980
Второе — это про STL, а не про C++. Есть реализации STL для контроллеров, а вообще STL использовать и не обязательно. И без нее у «плюсов» остается еще куча плюсов.
fse
Не склонен катить бочку на QT, т.к. считаю, что более удачных, открытых и кроссплатформенных фреймворков нет. По крайней мере не знаю таких, если не брать в стороне стоящую яву.
Но с критикой невозможно не согласиться, она имеет место быть.
Первое что пришло в голову — это сети. Нагруженный обмен данными может проигрывать около 20% относительно сокетной реализации (некроссплатформенной). Если обмен делаете в QT малыми датаграммами, то потери выше. Если взять тонкую обвязку над сокетами, типа mongoos-a, то потери пренебрежимо малы.
Как бы это и понятно. Странно было бы считать, что за удобство не платишь. В оправдание QT можно лишь сказать, что если из-за QT провалил обмен на максимум 20%, то из-за среднестатистического программиста провалишь ещё минимум на 20%.
А дело не в плюсах. Дело в устоявшемся плюсовом код-стайле. В среднем, число вызовов, число копирований на стеке и число выделений/реаллокаций памяти в плюсах выше, чем в си. Хотите снизить число вызовов — юзайте вместо обилия геттеров (которые далеко не все инлайнятся) меньший набор функций + жертвуйте конструкторами. Хотите минимизировать число выделений? Юзайте статические массивы вместо контейнеров… В конце пути вас ждёт си.
Вообще, это всё не досужие разговорчики. Я встречал стандарты прямо запрещающие использование динамической аллокации памяти в лайф-критикал приложениях. Причин несколько и они, думаю, ясны.
Безусловно. Но моё личное мнение — повышаются требования к квалификации программиста. Я уверен, что смог бы писать на плюсах код под MCU, который не уступает си, НО! Стиль такого кода — сильно не в тренде, а чтобы его почувствовал на низком уровне специалист нужна квалификация. Предполагаю, что именно поэтому, к примеру, ядро linux, freertos и всевозможные HAL-слои пишутся исключительно на си, стиль кода которого сам по себе не допускает излишеств.
Надеюсь, смог донести мысли.