Portando Qt para STM32

Boa tarde Nós, no projeto Embox , lançamos o Qt no STM32F7-Discovery e gostaríamos de contar sobre isso. Anteriormente, já dissemos como conseguimos lançar o OpenCV .

O Qt é uma estrutura de plataforma cruzada 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 (incluindo a implementação da IoT) e muito mais. Os desenvolvedores da equipe do Qt previram o uso do Qt em sistemas embarcados, portanto as bibliotecas estão muito bem configuradas. No entanto, até recentemente, poucas pessoas pensavam em transferir o Qt para microcontroladores, provavelmente porque essa tarefa parece complicada - o Qt é grande, os MCUs são pequenos.

Por outro lado, no momento existem microcontroladores projetados para trabalhar com multimídia e são superiores aos primeiros Pentiums. Há cerca de um ano, apareceu um post no blog Qt. Os desenvolvedores criaram uma porta Qt no sistema operacional RTEMS e lançaram exemplos com widgets em várias placas executando stm32f7. Isso nos interessou. Foi notável, e os próprios desenvolvedores escrevem sobre isso que o Qt diminui a velocidade no STM32F7-Discovery. Nós nos perguntamos se poderíamos executar o Qt no Embox, e não apenas desenhar um widget, mas iniciar a animação.

O Embt está portando o Qt 4.8 há muito tempo, então decidimos experimentá-lo. Escolhemos o aplicativo moveblocks - um exemplo de animação flexível.

Qt moveblocks no QEMU


Para começar, configuramos o Qt, se possível, com o conjunto mínimo de componentes necessários para suportar a animação. Para isso, existe a opção "-qconfig mínimo, pequeno, médio ...". Ele inclui um arquivo de configuração do Qt com muitas macros - o que ativar / desativar. Após esta opção, adicione outros sinalizadores à configuração, se quisermos desativar outra coisa. Aqui está um exemplo de nossa configuração .

Para que o Qt funcione, você precisa adicionar uma camada de compatibilidade do SO. Uma maneira é implementar o QPA (Qt Platform Abstraction). A base foi o plugin pronto fb_base como parte do Qt, com base no qual o QPA for Linux funciona. O resultado é um pequeno plug-in emboxfb que fornece o framebuffer Embox Qt e, em seguida, ele é desenhado sem ajuda.

É assim que a criação de plugins se parece
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 então aqui o redesenho ficará
 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; } 


Como resultado, com a otimização incluída do compilador para o tamanho da memória -Os, a imagem da biblioteca ficou em 3,5 MB, o que obviamente não se encaixa na memória principal do STM32F746. Como já escrevemos em nosso outro artigo sobre o OpenCV, este fórum possui:

  • 1 MB ROM
  • 320 Kb de RAM
  • 8 MB SDRAM
  • 16 MB QSPI

Como o suporte à execução de código do QSPI já foi adicionado ao OpenCV, decidimos começar carregando a imagem inteira do Embox c Qt no QSPI. E viva, tudo quase imediatamente começou com o QSPI! Mas, como no OpenCV, acabou sendo muito lento.



Portanto, decidimos fazer isso - primeiro copie a imagem para o QSPI, depois carregue-a no SDRAM e execute a partir daí. Da SDRAM, ficou um pouco mais rápido, mas ainda longe do QEMU.



A seguir, foi apresentada a ideia de incluir um ponto flutuante - porque Qt faz alguns cálculos das coordenadas dos quadrados na animação. Eles tentaram, mas não obtiveram aceleração visível, embora os desenvolvedores do Qt tenham afirmado no artigo que a FPU fornece um aumento significativo na velocidade para "arrastar a animação" na tela sensível ao toque. Talvez os moveblocks tenham significativamente menos cálculos de ponto flutuante, e isso depende de um exemplo específico.

A idéia mais eficaz foi transferir o buffer de quadros da SDRAM para a memória interna. Para fazer isso, não tínhamos um tamanho de tela de 480 x 272, mas 272 x 272. Também reduzimos a profundidade de cor de A8R8G8B8 para R5G6B5, reduzindo assim o tamanho de um pixel de 4 para 2 bytes. Temos o tamanho do buffer de quadros 272 * 272 * 2 = 147968 bytes. Isso deu uma aceleração significativa, talvez a mais notável, a animação se tornou quase suave.

A última otimização foi a execução do código Embox da RAM e o Qt da SDRAM. Para fazer isso, primeiro, como sempre, vinculamos estaticamente a Embox e o Qt, mas colocamos os segmentos das bibliotecas de texto, rodata, dados e bss no QSPI, para que possamos copiá-los posteriormente para o 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)) 

Devido à execução do código, o Embox da ROM também recebeu aceleração tangível. Como resultado, a animação acabou sendo bastante suave:


Já no final, enquanto preparava um artigo e tentava várias configurações da Embox, verificou-se que os QB moveblocks funcionam bem no QSPI com um framebuffer em SDRAM, e o tamanho do framebuffer era o gargalo! Aparentemente, para superar o “slideshow” inicial, a aceleração em 2 vezes foi suficiente devido à redução banal do tamanho do buffer de imagem. Mas não foi possível alcançar esse resultado transferindo apenas o código Embox para várias memórias rápidas (a aceleração foi obtida não por 2, mas por cerca de 1,5 vezes).

Como tentar você mesmo


Se você possui o STM32F7-Discovery, pode executar o Qt no Embox. Você pode ler como fazer isso em nosso wiki .

Conclusão


Como resultado, conseguimos lançar o Qt! A complexidade da tarefa, em nossa opinião, é um pouco exagerada. Naturalmente, você precisa levar em consideração as especificidades dos microcontroladores e geralmente entender a arquitetura dos sistemas de computação. Os resultados da otimização indicam um fato bem conhecido de que o gargalo no sistema de computação não é o processador, mas a memória.

Este ano estaremos participando do festival TechTrain . Lá, contaremos com mais detalhes e mostraremos Qt, OpenCV em microcontroladores e outras conquistas.

Source: https://habr.com/ru/post/pt459730/


All Articles