RK3568嵌入式Linux多屏显示开发指南(Qt应用层实现)
在RK3568平台上实现多屏显示需要结合DRM框架和Qt的显示系统,下面我将详细说明开发流程并提供完整的C++代码示例。
一、技术架构概览
二、开发环境准备
-
系统要求:
- Linux内核版本 ≥ 5.10(支持Rockchip DRM驱动)
- Qt版本 ≥ 5.15(支持eglfs_kms后端)
- libdrm库(≥ 2.4.104)
-
配置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");
五、常见问题解决方案
-
屏幕无法识别:
# 检查DRM连接器状态 modetest -M rockchip # 查看内核日志 dmesg | grep -i drm
-
分辨率不正确:
// 强制设置特定分辨率 qputenv("QT_QPA_EGLFS_WIDTH", "1920"); qputenv("QT_QPA_EGLFS_HEIGHT", "1080");
-
多屏同步问题:
// 使用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)
七、部署与测试
-
系统服务配置:
# /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
-
启动脚本:
#!/bin/bash # 关闭默认显示管理器 systemctl stop lightdm.service # 设置DRM权限 chmod 666 /dev/dri/card0 # 启动应用 /usr/bin/MultiScreenApp
八、高级功能扩展
- 动态屏幕热插拔检测:
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();
// 清理相关资源
}
};
- 跨屏拖拽交互:
// 启用全局鼠标跟踪
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)可以提供更大的灵活性。