RK3568嵌入式Linux多屏显示开发指南

RK3568嵌入式Linux多屏显示开发指南(Qt应用层实现)

在RK3568平台上实现多屏显示需要结合DRM框架和Qt的显示系统,下面我将详细说明开发流程并提供完整的C++代码示例。

一、技术架构概览

Qt应用程序
Qt平台插件
libdrm用户层接口
Rockchip DRM驱动
RK3568 VOP硬件
HDMI显示
MIPI-DSI显示
LVDS显示

二、开发环境准备

  1. 系统要求

    • Linux内核版本 ≥ 5.10(支持Rockchip DRM驱动)
    • Qt版本 ≥ 5.15(支持eglfs_kms后端)
    • libdrm库(≥ 2.4.104)
  2. 配置Qt编译选项

./configure -platform linux-g++ \
    -opengl es2 \
    -eglfs \
    -kms \
    -no-xcb \
    -prefix /usr/local/qt5.15

三、多屏显示实现方案

方案1:使用Qt内置的多屏支持(推荐)

// main.cpp
#include <QApplication>
#include <QScreen>
#include <QDebug>

int main(int argc, char *argv[])
{
    qputenv("QT_QPA_EGLFS_KMS_CONFIG", "/etc/qt-kms.conf");
    qputenv("QT_QPA_EGLFS_INTEGRATION", "eglfs_kms");
    
    QApplication app(argc, argv);
    
    // 获取所有可用屏幕
    QList<QScreen*> screens = QGuiApplication::screens();
    qDebug() << "Detected" << screens.size() << "screens";
    
    // 为每个屏幕创建独立窗口
    for (int i = 0; i < screens.size(); ++i) {
        QScreen* screen = screens[i];
        qDebug() << "Screen" << i << ":" 
                 << screen->name() 
                 << screen->geometry();
        
        QWindow* window = new QWindow();
        window->setScreen(screen);
        window->setGeometry(screen->geometry());
        window->setTitle("Display " + QString::number(i));
        window->show();
    }
    
    return app.exec();
}

配置文件 /etc/qt-kms.conf

{
  "device": "/dev/dri/card0",
  "hwcursor": true,
  "outputs": [
    {
      "name": "HDMI-A-1",
      "mode": "1920x1080",
      "position": "0,0"
    },
    {
      "name": "DSI-1",
      "mode": "800x1280",
      "position": "1920,0",
      "rotation": "90"
    }
  ]
}

方案2:直接使用libdrm API(精细控制)

#include <xf86drm.h>
#include <xf86drmMode.h>
#include <QtWidgets>

class DrmDisplay : public QObject {
    Q_OBJECT
public:
    DrmDisplay(const QString& connectorName) {
        // 打开DRM设备
        int fd = open("/dev/dri/card0", O_RDWR);
        if (fd < 0) {
            qFatal("Failed to open DRM device");
        }

        // 获取资源
        drmModeRes* res = drmModeGetResources(fd);
        if (!res) {
            qFatal("Failed to get DRM resources");
        }

        // 查找指定连接器
        for (int i = 0; i < res->count_connectors; ++i) {
            drmModeConnector* conn = drmModeGetConnector(fd, res->connectors[i]);
            if (conn && conn->connection == DRM_MODE_CONNECTED) {
                QString name = getConnectorName(conn->connector_type);
                if (name == connectorName) {
                    setupDisplay(fd, conn);
                    break;
                }
            }
            drmModeFreeConnector(conn);
        }

        drmModeFreeResources(res);
    }

    void createWindow() {
        QWindow* window = new QWindow();
        window->setGeometry(m_geometry);
        window->setTitle("DRM: " + m_connectorName);
        
        // 使用DRM作为Qt的后端
        window->setProperty("_q_platform_hwnd", m_framebufferId);
        window->show();
    }

private:
    void setupDisplay(int fd, drmModeConnector* conn) {
        m_fd = fd;
        m_connectorId = conn->connector_id;
        m_connectorName = getConnectorName(conn->connector_type);
        
        // 选择最佳显示模式
        drmModeModeInfo mode = findBestMode(conn);
        m_geometry = QRect(0, 0, mode.hdisplay, mode.vdisplay);
        
        // 创建framebuffer
        createFramebuffer(mode);
        
        // 设置CRTC
        drmModeCrtc* crtc = drmModeGetCrtc(fd, conn->encoder_id);
        drmModeSetCrtc(fd, crtc->crtc_id, m_framebufferId, 
                       0, 0, &m_connectorId, 1, &mode);
        drmModeFreeCrtc(crtc);
    }

    // 其他辅助函数...
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    
    // 创建HDMI显示
    DrmDisplay hdmi("HDMI-A");
    hdmi.createWindow();
    
    // 创建MIPI-DSI显示
    DrmDisplay dsi("DSI");
    dsi.createWindow();
    
    return app.exec();
}

四、关键开发要点

1. 屏幕旋转处理(竖屏显示)

// 在qt-kms.conf中配置旋转
{
  "outputs": [
    {
      "name": "DSI-1",
      "rotation": "90"
    }
  ]
}

// 或者在代码中设置
QWindow* window = new QWindow();
QScreen* screen = window->screen();
screen->setOrientationUpdateMask(Qt::PrimaryOrientation | 
                                 Qt::LandscapeOrientation |
                                 Qt::PortraitOrientation);

2. 多屏协同工作模式

// 扩展桌面模式(默认)
// 每个屏幕显示独立内容

// 镜像模式
void enableMirrorMode() {
    QList<QScreen*> screens = QGuiApplication::screens();
    if (screens.size() < 2) return;
    
    QScreen* primary = screens.first();
    for (QScreen* screen : screens) {
        if (screen != primary) {
            // 设置相同分辨率
            screen->setCustomGeometry(primary->geometry());
        }
    }
}

// 主从模式
void setPrimaryDisplay(QScreen* primary) {
    QGuiApplication::setPrimaryScreen(primary);
    for (QWidget* widget : QApplication::topLevelWidgets()) {
        if (widget->isWindow()) {
            widget->setScreen(primary);
        }
    }
}

3. 性能优化技巧

// 使用硬件加速渲染
QSurfaceFormat format;
format.setRenderableType(QSurfaceFormat::OpenGLES);
format.setVersion(2, 0);
QWindow::setDefaultFormat(format);

// 启用Rockchip RGA加速
qputenv("QT_EGLFS_IMX6_NO_FB_MULTI_BUFFER", "1");
qputenv("QT_EGLFS_IMX6_DISABLE_GPU", "1");

五、常见问题解决方案

  1. 屏幕无法识别

    # 检查DRM连接器状态
    modetest -M rockchip
    
    # 查看内核日志
    dmesg | grep -i drm
    
  2. 分辨率不正确

    // 强制设置特定分辨率
    qputenv("QT_QPA_EGLFS_WIDTH", "1920");
    qputenv("QT_QPA_EGLFS_HEIGHT", "1080");
    
  3. 多屏同步问题

    // 使用DRM的atomic模式设置
    drmModeAtomicReq* req = drmModeAtomicAlloc();
    drmModeAtomicAddProperty(req, crtc_id, prop_vsync_enable, 1);
    drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
    

六、完整项目结构示例

project/
├── CMakeLists.txt
├── include/
│   ├── DrmDisplay.h
│   └── ScreenManager.h
├── src/
│   ├── main.cpp
│   ├── DrmDisplay.cpp
│   └── ScreenManager.cpp
├── config/
│   └── qt-kms.conf
└── scripts/
    └── setup_displays.sh

CMakeLists.txt 关键配置:

cmake_minimum_required(VERSION 3.5)
project(MultiScreenApp)

set(CMAKE_CXX_STANDARD 17)

find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED)
find_package(Libdrm REQUIRED)

add_executable(${PROJECT_NAME}
    src/main.cpp
    src/DrmDisplay.cpp
    src/ScreenManager.cpp
)

target_link_libraries(${PROJECT_NAME}
    Qt5::Core
    Qt5::Gui
    Qt5::Widgets
    ${LIBDRM_LIBRARIES}
)

install(TARGETS ${PROJECT_NAME} DESTINATION /usr/bin)
install(DIRECTORY config/ DESTINATION /etc)

七、部署与测试

  1. 系统服务配置

    # /etc/systemd/system/multiscreen.service
    [Unit]
    Description=Multi-Screen Qt Application
    After=graphical.target
    
    [Service]
    Environment=QT_QPA_PLATFORM=eglfs
    Environment=QT_QPA_EGLFS_KMS_CONFIG=/etc/qt-kms.conf
    ExecStart=/usr/bin/MultiScreenApp
    Restart=always
    
    [Install]
    WantedBy=multi-user.target
    
  2. 启动脚本

    #!/bin/bash
    # 关闭默认显示管理器
    systemctl stop lightdm.service
    
    # 设置DRM权限
    chmod 666 /dev/dri/card0
    
    # 启动应用
    /usr/bin/MultiScreenApp
    

八、高级功能扩展

  1. 动态屏幕热插拔检测
class ScreenWatcher : public QObject {
    Q_OBJECT
public:
    ScreenWatcher() {
        connect(qApp, &QGuiApplication::screenAdded, 
                this, &ScreenWatcher::onScreenAdded);
        connect(qApp, &QGuiApplication::screenRemoved, 
                this, &ScreenWatcher::onScreenRemoved);
    }

private slots:
    void onScreenAdded(QScreen* newScreen) {
        qDebug() << "Screen added:" << newScreen->name();
        // 创建新窗口
        QWindow* window = new QWindow();
        window->setScreen(newScreen);
        window->show();
    }
    
    void onScreenRemoved(QScreen* removedScreen) {
        qDebug() << "Screen removed:" << removedScreen->name();
        // 清理相关资源
    }
};
  1. 跨屏拖拽交互
// 启用全局鼠标跟踪
QApplication::setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, true);

// 自定义拖拽事件处理
void Window::mouseMoveEvent(QMouseEvent* event) {
    if (event->buttons() & Qt::LeftButton) {
        // 计算相对屏幕位置
        QPoint screenPos = event->globalPosition().toPoint();
        
        // 检测是否移动到其他屏幕
        QScreen* targetScreen = QGuiApplication::screenAt(screenPos);
        if (targetScreen && targetScreen != screen()) {
            // 转移到新屏幕
            setScreen(targetScreen);
            setGeometry(targetScreen->geometry());
        }
    }
}

通过以上方案,您可以在RK3568平台上实现灵活的多屏显示控制。对于大多数应用场景,推荐使用Qt内置的多屏支持(方案1),它提供了更简洁的API和更好的兼容性。当需要更底层的硬件控制时,libdrm直接访问方案(方案2)可以提供更大的灵活性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值