Boa tarde Estamos no projeto
Qt é uma estrutura multiplataforma que inclui não apenas componentes gráficos, mas também coisas como QtNetwork, um conjunto de classes para trabalhar com bancos de dados, Qt for Automation (inclusive para implementação de IoT) e muito mais. A equipe do Qt tem sido proativa no uso do Qt em sistemas embarcados, portanto as bibliotecas são bastante configuráveis. No entanto, até recentemente, poucas pessoas pensavam em portar o Qt para microcontroladores, provavelmente porque tal tarefa parece difícil - o Qt é grande, os MCUs são pequenos.
Por outro lado, atualmente existem microcontroladores projetados para trabalhar com multimídia e superiores aos primeiros Pentiums. Cerca de um ano atrás, o blog Qt apareceu
O Qt 4.8 foi portado para o Embox há muito tempo, então decidimos experimentá-lo. Escolhemos o aplicativo moveblocks - um exemplo de animação elástica.
Moveblocks Qt no QEMU
Para começar, configuramos o Qt, se possível, com o conjunto mínimo de componentes necessários para suportar animação. Para isso existe a opção “-qconfig mínimo, pequeno, médio...”. Ele conecta um arquivo de configuração do Qt com muitas macros - o que habilitar/o que desabilitar. Após esta opção, adicionamos outros flags à configuração caso queiramos desabilitar alguma outra coisa. Aqui está um exemplo do nosso
Para que o Qt funcione, você precisa adicionar uma camada de compatibilidade do sistema operacional. Uma maneira é implementar QPA (Qt Platform Abstraction). Tomamos como base o plugin fb_base pronto incluído no Qt, com base no qual funciona o QPA para Linux. O resultado é um pequeno plugin chamado emboxfb, que fornece ao Qt o framebuffer do Embox e então desenha lá sem qualquer ajuda externa.
É assim que se parece a criação de um 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();
}
E é assim que ficará o redesenho
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;
}
Como resultado, com a otimização do compilador para tamanho de memória -Os habilitada, a imagem da biblioteca acabou sendo de 3.5 MB, o que obviamente não cabe na memória principal do STM32F746. Como já escrevemos em nosso outro artigo sobre OpenCV, esta placa possui:
- ROM de 1 MB
- 320 KB de RAM
- SDRAM de 8MB
- 16MB QSPI
Como o suporte para execução de código do QSPI já foi adicionado ao OpenCV, decidimos começar carregando toda a imagem Embox c Qt no QSPI. E viva, tudo começou quase imediatamente no QSPI! Mas, como no caso do OpenCV, descobriu-se que ele funciona muito devagar.
Portanto, decidimos fazer desta forma - primeiro copiamos a imagem para QSPI, depois carregamos no SDRAM e executamos a partir daí. Do SDRAM ficou um pouco mais rápido, mas ainda longe do QEMU.
A seguir, surgiu a ideia de incluir um ponto flutuante - afinal, o Qt faz alguns cálculos das coordenadas dos quadrados na animação. Tentamos, mas aqui não obtivemos nenhuma aceleração visível, embora em
A ideia mais eficaz foi mover o framebuffer da SDRAM para a memória interna. Para fazer isso, fizemos com que as dimensões da tela não fossem 480x272, mas 272x272. Também reduzimos a profundidade de cor de A8R8G8B8 para R5G6B5, reduzindo assim o tamanho de um pixel de 4 para 2 bytes. O tamanho do framebuffer resultante é 272 * 272 * 2 = 147968 bytes. Isto deu uma aceleração significativa, talvez o mais perceptível, a animação tornou-se quase suave.
A otimização mais recente foi executar o código Embox da RAM e o código Qt da SDRAM. Para fazer isso, primeiro, como de costume, vinculamos estaticamente o Embox ao Qt, mas colocamos os segmentos de texto, rodata, dados e bss da biblioteca no QSPI para depois copiá-los para 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))
Ao executar o código Embox da ROM, também obtivemos uma aceleração notável. Como resultado, a animação ficou bastante suave:
No final, enquanto preparava o artigo e tentava diferentes configurações do Embox, descobriu-se que os moveblocks do Qt funcionam muito bem no QSPI com um framebuffer em SDRAM, e o gargalo era exatamente o tamanho do framebuffer! Aparentemente, para superar o “slideshow” inicial, bastou uma aceleração de 2 vezes devido a uma redução banal no tamanho do framebuffer. Mas não foi possível alcançar tal resultado transferindo apenas o código Embox para várias memórias rápidas (a aceleração não foi 2, mas cerca de 1.5 vezes).
Como experimentar você mesmo
Se você tiver um STM32F7-Discovery, poderá executar o Qt no Embox você mesmo. Você pode ler como isso é feito em nosso
Conclusão
Como resultado, conseguimos lançar o Qt! A complexidade da tarefa, em nossa opinião, é um tanto exagerada. Naturalmente, você precisa levar em consideração as especificidades dos microcontroladores e compreender de maneira geral a arquitetura dos sistemas de computador. Os resultados da otimização apontam para o fato bem conhecido de que o gargalo em um sistema de computação não é o processador, mas sim a memória.
Este ano participaremos do festival
Fonte: habr.com