Qt'yi STM32'ye taşıma

Qt'yi STM32'ye taşımaTünaydın Projedeyiz em kutusu Qt'yi STM32F7-Discovery'de başlattı ve bunun hakkında konuşmak istiyorum. Daha önce, lansmanı nasıl başardığımızı zaten anlatmıştık. OpenCV.

Qt, yalnızca grafik bileşenleri değil aynı zamanda QtNetwork, veritabanlarıyla çalışmaya yönelik bir dizi sınıf, Otomasyon için Qt (IoT uygulaması dahil) ve çok daha fazlasını içeren platformlar arası bir çerçevedir. Qt ekibi, Qt'nin gömülü sistemlerde kullanılması konusunda proaktif davrandığından kütüphaneler oldukça yapılandırılabilir. Ancak yakın zamana kadar çok az kişi Qt'yi mikrodenetleyicilere taşımayı düşündü, bunun nedeni muhtemelen böyle bir görevin zor görünmesiydi - Qt büyük, MCU'lar küçük.

Öte yandan şu anda multimedya ile çalışacak şekilde tasarlanmış ve ilk Pentium'lardan daha üstün mikrodenetleyiciler var. Yaklaşık bir yıl önce Qt blogu ortaya çıktı postalamak. Geliştiriciler, RTEMS işletim sistemi için bir Qt bağlantı noktası oluşturdular ve stm32f7 çalıştıran çeşitli kartlarda widget içeren örnekler başlattılar. Bu bizi ilgilendiriyordu. Qt'nin STM32F7-Discovery'de yavaş olduğu dikkat çekiciydi ve geliştiricilerin kendileri de bunun hakkında yazıyor. Qt'yi Embox altında çalıştırıp çalıştıramayacağımızı ve yalnızca bir widget çizmekle kalmayıp bir animasyon çalıştırıp çalıştıramayacağımızı merak ediyorduk.

Qt 4.8 uzun süredir Embox'a taşındı, bu yüzden denemeye karar verdik. Esnek animasyonun bir örneği olan moveblocks uygulamasını seçtik.

QEMU'da Qt hareket bloklarıQt'yi STM32'ye taşıma

Başlangıç ​​olarak, mümkünse Qt'yi animasyonu desteklemek için gereken minimum bileşen seti ile yapılandırıyoruz. Bunun için “-qconfig minimal,small,medium...” seçeneği var. Qt'deki bir yapılandırma dosyasını birçok makroyla (neyin etkinleştirileceği / neyin devre dışı bırakılacağı) bağlar. Bu seçenekten sonra eğer başka bir şeyi devre dışı bırakmak istiyorsak konfigürasyona başka flaglar da ekliyoruz. İşte bizim bir örneğimiz yapılandırma.

Qt'nin çalışması için bir işletim sistemi uyumluluk katmanı eklemeniz gerekir. Bunun bir yolu QPA'yı (Qt Platform Soyutlaması) uygulamaktır. Linux için QPA'nın çalıştığı, Qt'de bulunan hazır fb_base eklentisini temel aldık. Sonuç, emboxfb adı verilen, Qt'ye Embox'ın çerçeve arabelleğini sağlayan küçük bir eklentidir ve daha sonra herhangi bir dış yardım olmadan oraya çizim yapar.

Bir eklenti oluşturmak böyle görünür

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();
}

Ve yeniden çizim böyle görünecek

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;
}

Sonuç olarak, -Os bellek boyutu için derleyici optimizasyonu etkinleştirildiğinde, kitaplık görüntüsünün 3.5 MB olduğu ortaya çıktı ve bu elbette STM32F746'nın ana belleğine sığmıyor. OpenCV ile ilgili diğer yazımızda da yazdığımız gibi bu kurulda şunlar bulunmaktadır:

  • 1MB ROM
  • 320 KB RAM
  • 8MBSDRAM
  • 16 MB QSPI

QSPI'dan kod yürütme desteği OpenCV'ye zaten eklendiğinden, Embox c Qt görüntüsünün tamamını QSPI'ye yükleyerek başlamaya karar verdik. Ve yaşasın, her şey neredeyse anında QSPI'den başladı! Ancak OpenCV örneğinde olduğu gibi çok yavaş çalıştığı ortaya çıktı.

Qt'yi STM32'ye taşıma

Bu nedenle bunu bu şekilde yapmaya karar verdik - önce görüntüyü QSPI'ye kopyalıyoruz, ardından SDRAM'a yüklüyoruz ve oradan çalıştırıyoruz. SDRAM'den biraz daha hızlı hale geldi, ancak yine de QEMU'dan uzak.

Qt'yi STM32'ye taşıma

Daha sonra, kayan nokta ekleme fikri ortaya çıktı; sonuçta Qt, animasyonda karelerin koordinatlarına ilişkin bazı hesaplamalar yapıyor. Denedik ama burada gözle görülür bir hızlanma elde edemedik. Makale Qt geliştiricileri, FPU'nun dokunmatik ekranda "animasyonu sürüklemek" için önemli bir hız artışı sağladığını iddia etti. Hareket bloklarında önemli ölçüde daha az kayan nokta hesaplaması olabilir ve bu, belirli örneğe bağlıdır.

En etkili fikir çerçeve arabelleğini SDRAM'den dahili belleğe taşımaktı. Bunun için ekran boyutlarını 480x272 değil 272x272 yaptık. Ayrıca renk derinliğini A8R8G8B8'den R5G6B5'e düşürdük, böylece bir pikselin boyutunu 4 bayttan 2 bayta düşürdük. Ortaya çıkan çerçeve arabelleği boyutu 272 * 272 * 2 = 147968 bayttır. Bu önemli bir hızlanma sağladı, belki de en dikkat çekici olanı animasyonun neredeyse pürüzsüz hale gelmesiydi.

En son optimizasyon, Embox kodunu RAM'den ve Qt kodunu SDRAM'den çalıştırmaktı. Bunu yapmak için, önce her zamanki gibi Embox'ı Qt'ye statik olarak bağlarız, ancak kitaplığın metin, rodata, veri ve bss bölümlerini daha sonra SDRAM'a kopyalamak için QSPI'ye yerleştiririz.

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 kodunu ROM'dan çalıştırarak da gözle görülür bir hızlanma elde ettik. Sonuç olarak animasyon oldukça düzgün çıktı:


En sonunda, makaleyi hazırlarken ve farklı Embox yapılandırmalarını denerken, Qt hareket bloklarının SDRAM'deki bir çerçeve arabelleğiyle QSPI'den harika çalıştığı ve darboğazın tam olarak çerçeve arabelleğinin boyutunda olduğu ortaya çıktı! Görünüşe göre, ilk "slayt gösterisinin" üstesinden gelmek için, çerçeve arabelleğinin boyutundaki banal azalma nedeniyle 2 kat hızlanma yeterliydi. Ancak sadece Embox kodunu çeşitli hızlı hafızalara aktararak böyle bir sonuca ulaşmak mümkün değildi (hızlanma 2 değil yaklaşık 1.5 kattı).

Kendiniz nasıl deneyebilirsiniz

STM32F7-Discovery'niz varsa Qt'yi Embox altında kendiniz çalıştırabilirsiniz. Bunun nasıl yapıldığını sitemizde okuyabilirsiniz. wiki.

Sonuç

Sonuç olarak Qt!'yi başlatmayı başardık! Bize göre görevin karmaşıklığı biraz abartılıyor. Doğal olarak mikrodenetleyicilerin özelliklerini dikkate almanız ve genel olarak bilgisayar sistemlerinin mimarisini anlamanız gerekir. Optimizasyon sonuçları, bir bilgi işlem sistemindeki darboğazın işlemci değil bellek olduğu iyi bilinen bir gerçeğe işaret etmektedir.

Bu yıl festivale katılacağız TechTrain. Orada size daha ayrıntılı olarak anlatacağız ve mikrodenetleyiciler üzerinde Qt, OpenCV ve diğer başarılarımızı göstereceğiz.

Kaynak: habr.com

Yorum ekle