Qt siirretään STM32:een

Qt siirretään STM32:eenHyvää iltapäivää Olemme mukana projektissa Embox käynnisti Qt:n STM32F7-Discoveryssä ja haluaisin puhua siitä. Kerroimme jo aiemmin, kuinka onnistuimme käynnistämään OpenCV.

Qt on monialustainen kehys, joka sisältää paitsi graafisia komponentteja, myös sellaisia ​​​​asioita kuin QtNetwork, tietokantojen kanssa työskentelyyn tarkoitettujen luokkien sarja, Qt for Automation (mukaan lukien IoT-toteutus) ja paljon muuta. Qt-tiimi on ollut ennakoivasti käyttänyt Qt:tä sulautetuissa järjestelmissä, joten kirjastot ovat melko konfiguroitavissa. Kuitenkin viime aikoihin asti harvat ihmiset ajattelivat Qt:n siirtämistä mikro-ohjaimiin, luultavasti siksi, että tällainen tehtävä näyttää vaikealta - Qt on suuri, MCU:t ovat pieniä.

Toisaalta tällä hetkellä on olemassa mikrokontrollereita, jotka on suunniteltu toimimaan multimedian kanssa ja jotka ovat parempia kuin ensimmäiset Pentiumit. Noin vuosi sitten ilmestyi Qt-blogi posti. Kehittäjät tekivät Qt-portin RTEMS-käyttöjärjestelmälle ja julkaisivat esimerkkejä widgeteistä useilla stm32f7-levyillä. Tämä kiinnosti meitä. Oli havaittavissa, ja kehittäjät itse kirjoittavat siitä, että Qt on hidas STM32F7-Discoveryssa. Mietimme, voisimmeko käyttää Qt:tä Emboxin alla, emmekä vain piirtää widgetiä, vaan ajaa animaation.

Qt 4.8 on siirretty Emboxiin pitkään, joten päätimme kokeilla sitä siinä. Valitsimme Moveblocks-sovelluksen - esimerkki joustavasta animaatiosta.

Qt-siirtolohkot QEMU:ssaQt siirretään STM32:een

Aluksi määritämme Qt:n, jos mahdollista, animaation tukemiseen vaadittavien komponenttien vähimmäisjoukolla. Tätä varten on vaihtoehto "-qconfig minimal,small,medium...". Se yhdistää Qt:n asetustiedoston moniin makroihin - mitä ottaa käyttöön / mitä poistaa. Tämän vaihtoehdon jälkeen lisäämme kokoonpanoon muita lippuja, jos haluamme poistaa jonkin muun käytöstä. Tässä on esimerkki meidän kokoonpano.

Jotta Qt toimisi, sinun on lisättävä käyttöjärjestelmän yhteensopivuuskerros. Yksi tapa on toteuttaa QPA (Qt Platform Abstraction). Otimme pohjaksi Qt:ssä olevan valmiin fb_base-liitännäisen, jonka pohjalta QPA for Linux toimii. Tuloksena on pieni laajennus nimeltä emboxfb, joka tarjoaa Qt:lle Emboxin kehyspuskurin ja sitten se piirtyy sinne ilman ulkopuolista apua.

Tältä laajennuksen luominen näyttää

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

Ja tältä uudelleenpiirustus tulee näyttämään

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

Seurauksena oli, että kääntäjän muistin koon -Os optimoinnin ollessa käytössä kirjaston kuvaksi tuli 3.5 MB, mikä ei tietenkään mahdu STM32F746:n päämuistiin. Kuten jo kirjoitimme toisessa OpenCV-artikkelissamme, tällä taululla on:

  • 1 MB ROM
  • 320KB RAM
  • 8 Mt SDRAM
  • 16 Mt QSPI

Koska tuki QSPI:n koodin suorittamiselle on jo lisätty OpenCV:hen, päätimme aloittaa lataamalla koko Embox c Qt -kuvan QSPI:hen. Ja hurraa, kaikki alkoi melkein heti QSPI:stä! Mutta kuten OpenCV:n tapauksessa, kävi ilmi, että se toimii liian hitaasti.

Qt siirretään STM32:een

Siksi päätimme tehdä sen tällä tavalla - kopioimme ensin kuvan QSPI: hen, lataamme sen SDRAM-muistiin ja suoritamme sieltä. SDRAMista tuli hieman nopeampi, mutta silti kaukana QEMU:sta.

Qt siirretään STM32:een

Seuraavaksi oli ajatus sisällyttää liukuluku - Qthan sentään tekee joitain laskelmia neliöiden koordinaateista animaatiossa. Yritimme, mutta täällä emme saaneet näkyvää kiihtyvyyttä, vaikka sisään статье Qt-kehittäjät väittivät, että FPU lisää merkittävästi nopeutta "animaatioiden vetämistä" kosketusnäytöllä. Liukulukulaskelmia voi olla huomattavasti vähemmän siirtolohkoissa, ja tämä riippuu tietystä esimerkistä.

Tehokkain idea oli siirtää kehyspuskuri SDRAM-muistista sisäiseen muistiin. Tätä varten teimme näytön mitoiksi ei 480x272, vaan 272x272. Lasimme myös värisyvyyttä A8R8G8B8:sta R5G6B5:een, mikä pienensi yhden pikselin kokoa 4 tavusta 2 tavuun. Tuloksena oleva kehyspuskurin koko on 272 * 272 * 2 = 147968 tavua. Tämä antoi merkittävän kiihtyvyyden, ehkä huomattavimmin animaatiosta tuli lähes tasainen.

Viimeisin optimointi oli suorittaa Embox-koodi RAM-muistista ja Qt-koodi SDRAM-muistista. Tätä varten yhdistämme ensin staattisesti Emboxin tuttuun tapaan Qt:hen, mutta sijoitamme kirjaston teksti-, rodata-, data- ja bss-segmentit QSPI:hen kopioidaksemme sen SDRAM-muistiin.

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))

Suorittamalla Embox-koodin ROM:lta saimme myös huomattavan kiihtyvyyden. Tämän seurauksena animaatio osoittautui melko sujuvaksi:


Aivan lopussa artikkelia valmistellessa ja erilaisia ​​Embox-kokoonpanoja kokeiltaessa kävi ilmi, että Qt moveblocks toimii loistavasti QSPI:stä SDRAM:n framebufferilla ja pullonkaula oli juuri framebufferin kokoinen! Ilmeisesti alkuperäisen "diaesityksen" voittamiseksi 2-kertainen kiihtyvyys riitti kehyspuskurin koon banaalista pienennyksestä. Mutta tällaista tulosta ei voitu saavuttaa siirtämällä vain Embox-koodia erilaisiin nopeisiin muisteihin (kiihtyvyys ei ollut 2, vaan noin 1.5-kertainen).

Kuinka kokeilla sitä itse

Jos sinulla on STM32F7-Discovery, voit suorittaa Qt:n Emboxin alla itse. Voit lukea kuinka tämä tehdään meidän sivuiltamme wiki.

Johtopäätös

Tämän seurauksena onnistuimme käynnistämään Qt! Tehtävän monimutkaisuus on mielestämme hieman liioiteltua. Luonnollisesti sinun on otettava huomioon mikro-ohjainten erityispiirteet ja yleisesti ymmärrettävä tietokonejärjestelmien arkkitehtuuri. Optimointitulokset viittaavat hyvin tunnettuun tosiasiaan, että laskentajärjestelmän pullonkaula ei ole prosessori, vaan muisti.

Tänä vuonna osallistumme festivaaleille TechTrain. Siellä kerromme sinulle tarkemmin ja näytämme Qt:n, OpenCV:n mikrokontrollereissa ja muut saavutuksemme.

Lähde: will.com

Lisää kommentti