Chuyển Qt sang STM32

Chuyển Qt sang STM32Chào buổi chiều Chúng tôi đang ở trong dự án Hộp thư đã ra mắt Qt trên STM32F7-Discovery và muốn nói về nó. Trước đó, chúng tôi đã nói về cách chúng tôi có thể khởi chạy OpenCV.

Qt là một framework đa nền tảng không chỉ bao gồm các thành phần đồ họa mà còn bao gồm những thứ như QtNetwork, một tập hợp các lớp để làm việc với cơ sở dữ liệu, Qt cho Tự động hóa (bao gồm cả việc triển khai IoT) và hơn thế nữa. Nhóm Qt đã chủ động sử dụng Qt trong các hệ thống nhúng, vì vậy các thư viện có thể cấu hình khá dễ dàng. Tuy nhiên, cho đến gần đây, ít người nghĩ đến việc chuyển Qt sang vi điều khiển, có lẽ vì nhiệm vụ như vậy có vẻ khó khăn - Qt thì lớn, MCU thì nhỏ.

Mặt khác, hiện tại có những bộ vi điều khiển được thiết kế để hoạt động với đa phương tiện và vượt trội hơn những chiếc Pentium đầu tiên. Khoảng một năm trước, blog Qt xuất hiện gửi. Các nhà phát triển đã tạo một cổng Qt cho hệ điều hành RTEMS và đưa ra các ví dụ với các tiện ích trên một số bo mạch chạy stm32f7. Điều này làm chúng tôi quan tâm. Điều đáng chú ý là chính các nhà phát triển đã viết về điều đó rằng Qt chạy chậm trên STM32F7-Discovery. Chúng tôi đang tự hỏi liệu chúng tôi có thể chạy Qt trong Embox và không chỉ vẽ tiện ích mà còn chạy hoạt ảnh hay không.

Qt 4.8 đã được chuyển sang Embox từ lâu nên chúng tôi quyết định dùng thử. Chúng tôi đã chọn ứng dụng Moveblocks - một ví dụ về hoạt ảnh có độ đàn hồi.

Khối di chuyển Qt trên QEMUChuyển Qt sang STM32

Để bắt đầu, chúng tôi định cấu hình Qt, nếu có thể, với bộ thành phần tối thiểu cần thiết để hỗ trợ hoạt ảnh. Đối với điều này, có một tùy chọn “-qconfig tối thiểu, nhỏ, trung bình…”. Nó kết nối một tệp cấu hình từ Qt với nhiều macro - nên bật/tắt cái gì. Sau tùy chọn này, chúng tôi thêm các cờ khác vào cấu hình nếu chúng tôi muốn tắt một thứ khác. Đây là một ví dụ của chúng tôi cấu hình.

Để Qt hoạt động, bạn cần thêm lớp tương thích với hệ điều hành. Một cách là triển khai QPA (Tóm tắt nền tảng Qt). Chúng tôi lấy plugin fb_base làm sẵn có trong Qt làm cơ sở, trên cơ sở đó QPA dành cho Linux hoạt động. Kết quả là một plugin nhỏ có tên emboxfb, cung cấp Qt với bộ đệm khung của Embox và sau đó nó vẽ ở đó mà không cần bất kỳ sự trợ giúp nào từ bên ngoài.

Đây là giao diện tạo một 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();
}

Và bản vẽ lại sẽ trông như thế này

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

Kết quả là, khi bật tính năng tối ưu hóa trình biên dịch cho kích thước bộ nhớ -Os, hình ảnh thư viện hóa ra là 3.5 MB, tất nhiên là không vừa với bộ nhớ chính của STM32F746. Như chúng tôi đã viết trong bài viết khác về OpenCV, bảng này có:

  • 1 MB bộ nhớ
  • RAM 320 KB
  • SDRAM 8 MB
  • QSPI 16 MB

Vì hỗ trợ thực thi mã từ QSPI đã được thêm vào OpenCV nên chúng tôi quyết định bắt đầu bằng cách tải toàn bộ hình ảnh Embox c Qt vào QSPI. Và hoan hô, mọi thứ gần như bắt đầu ngay lập tức từ QSPI! Nhưng như trường hợp của OpenCV, hóa ra nó hoạt động quá chậm.

Chuyển Qt sang STM32

Do đó, chúng tôi quyết định thực hiện theo cách này - đầu tiên chúng tôi sao chép hình ảnh sang QSPI, sau đó tải nó vào SDRAM và thực thi từ đó. Từ SDRAM nó trở nên nhanh hơn một chút nhưng vẫn kém xa QEMU.

Chuyển Qt sang STM32

Tiếp theo, có một ý tưởng bao gồm một dấu phẩy động - xét cho cùng, Qt thực hiện một số phép tính về tọa độ của các hình vuông trong hoạt ảnh. Chúng tôi đã cố gắng, nhưng ở đây chúng tôi không nhận được bất kỳ khả năng tăng tốc rõ ràng nào, mặc dù ở Bài viết Các nhà phát triển Qt tuyên bố rằng FPU giúp tăng tốc độ đáng kể cho việc “kéo hoạt hình” trên màn hình cảm ứng. Có thể có ít phép tính dấu phẩy động hơn đáng kể trong khối di chuyển và điều này phụ thuộc vào ví dụ cụ thể.

Ý tưởng hiệu quả nhất là chuyển bộ đệm khung từ SDRAM sang bộ nhớ trong. Để làm điều này, chúng tôi đã tạo kích thước màn hình không phải 480x272 mà là 272x272. Chúng tôi cũng giảm độ sâu màu từ A8R8G8B8 xuống R5G6B5, do đó giảm kích thước của một pixel từ 4 xuống 2 byte. Kích thước bộ đệm khung kết quả là 272 * 272 * 2 = 147968 byte. Điều này mang lại khả năng tăng tốc đáng kể, có lẽ đáng chú ý nhất là hoạt ảnh trở nên gần như mượt mà.

Tối ưu hóa mới nhất là chạy mã Embox từ RAM và mã Qt từ SDRAM. Để thực hiện điều này, trước tiên, như thường lệ, chúng tôi liên kết tĩnh Embox với Qt, nhưng chúng tôi đặt các phân đoạn văn bản, rodata, dữ liệu và bss của thư viện trong QSPI để sau đó sao chép nó và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))

Bằng cách thực thi mã Embox từ ROM, chúng tôi cũng nhận được khả năng tăng tốc đáng chú ý. Kết quả là hoạt ảnh trở nên khá mượt mà:


Cuối cùng, trong khi chuẩn bị bài viết và thử các cấu hình Embox khác nhau, hóa ra các khối di chuyển Qt hoạt động rất tốt từ QSPI với bộ đệm khung trong SDRAM và nút cổ chai chính xác là kích thước của bộ đệm khung! Rõ ràng, để vượt qua "trình chiếu" ban đầu, khả năng tăng tốc gấp 2 lần là đủ do kích thước của bộ đệm khung giảm đi một cách tầm thường. Nhưng không thể đạt được kết quả như vậy nếu chỉ chuyển mã Embox sang nhiều bộ nhớ nhanh khác nhau (gia tốc không phải là 2 mà là khoảng 1.5 lần).

Làm thế nào để tự mình thử nó

Nếu bạn có STM32F7-Discovery, bạn có thể tự chạy Qt trong Embox. Bạn có thể đọc cách thực hiện điều này trên wiki.

Kết luận

Kết quả là chúng tôi đã khởi chạy được Qt! Theo chúng tôi, sự phức tạp của nhiệm vụ có phần bị cường điệu hóa. Đương nhiên, bạn cần phải tính đến các chi tiết cụ thể của bộ vi điều khiển và hiểu chung về kiến ​​​​trúc của hệ thống máy tính. Các kết quả tối ưu hóa chỉ ra một thực tế rõ ràng rằng nút cổ chai trong hệ thống máy tính không phải là bộ xử lý mà là bộ nhớ.

Năm nay chúng tôi sẽ tham gia lễ hội TechĐào tạo. Ở đó chúng tôi sẽ cho bạn biết chi tiết hơn và hiển thị Qt, OpenCV trên bộ vi điều khiển và những thành tựu khác của chúng tôi.

Nguồn: www.habr.com

Thêm một lời nhận xét