Хабрахабр

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

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

Разработчики команды Qt заранее предусмотрели использование Qt во встроенных системах, поэтому библиотеки довольно хорошо конфигурируются. Qt — это кроссплатформенный фреймворк, который включает в себя не только графические компоненты, но и такие вещи как QtNetwork, набор классов для работы с базами данных, Qt for Automation (в том числе, для реализации IoT) и многое другое. Около года назад в блоге Qt появился пост. Однако до недавних пор, мало кто задумывался о портировании Qt на микроконтроллеры, вероятно потому, что такая задача выглядит сложной — Qt большое, MCU маленькие.
С другой стороны, на данный момент существуют микроконтроллеры, предназначенные для работы с мультимедиа и превосходящие первые Pentium-ы. Нас это заинтересовало. Разработчики сделали порт Qt под ОС RTEMS, и запустили примеры с виджетами на нескольких платах под управлением stm32f7. Нам стало интересно, сможем ли мы запустить Qt под Embox, при этом не просто нарисовать виджет, а запустить анимацию. Было заметно, и сами разработчики об этом пишут, что Qt тормозит на STM32F7-Discovery.

8, поэтому решили попробовать на нем. В Embox уже давно портировано Qt 4. Выбрали приложение moveblocks — пример пружинистой анимации.

Qt moveblocks на QEMU

Для начала конфигурируем Qt по возможности с минимальным набором компонент, требуемым для поддержки анимации. Для этого существует опция “-qconfig minimal,small,medium ...”. Она подключает конфигурационный файл из состава Qt c множеством макросов — что включить / что отключить. После этой опции добавляем в конфигурацию другие флаги, если хотим еще что-то отключить дополнительно. Вот пример нашей конфигурации.

Один из способов — реализовать QPA (Qt Platform Abstraction). Для того, чтобы Qt заработало, нужно добавить слой совместимости с ОС. В итоге получился небольшой плагин emboxfb, который предоставляет Qt фреймбуфер Embox’a, а дальше оно рисует туда уже без посторонней помощи. За основу взяли уже готовый плагин fb_base в составе Qt, на базе которого работает QPA для Линукс.

Вот так выглядит создание плагина

QEmboxFbIntegration::QEmboxFbIntegration() : fontDb(new QGenericUnixFontDatabase())
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 оказалось, что работает слишком медленно.

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

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

Для этого мы сделали размеры экрана не 480x272, а 272x272. Самым же эффективным оказалась идея перенести фреймбуфер из SDRAM во внутреннюю память. Получили размер фреймбуфера 272 * 272 * 2 = 147968 байт. Еще понизили глубину цвета с A8R8G8B8 до R5G6B5, таким образом сократив размер одного пикселя с 4 до 2 байт. Это дало значительное ускорение, пожалуй, самое заметное, анимация стала почти плавной.

Для этого мы сначала как обычно линкуем статически Embox вместе с Qt, но сегменты text, rodata, data и bss библиотеки размещаем в QSPI, с тем чтобы потом скопировать в SDRAM. Последней оптимизацией стало выполнение кода Embox из RAM, а Qt из 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! Сложность задачи, на наш взгляд, несколько преувеличена. Естественно нужно учитывать специфику микроконтроллеров и вообще понимать архитектуру вычислительных систем. Результаты оптимизации указывают на известный факт, что самое узкое место в вычислительной системе, это не процессор, а память.

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

Показать больше

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»