点亮效果预览 (用 USB Camera 拍摄 RDK X5 上的 3.5" SPI LCD 桌面画面)
RDK X5 驱动 Waveshare 3.5" SPI LCD (ST7796S) 完整实战指南
一、项目概述
本文详细介绍如何在 RDK X5 开发板上驱动 Waveshare 3.5inch RPi LCD (F)(ST7796S 控制器,320×480 IPS,SPI 接口 + GT911 电容触摸)。
最终实现:
内核态 DRM/KMS 驱动,硬件加速帧传输
X11 桌面直接输出到 SPI LCD
GT911 触摸精确校准
GPIO 背光控制
方案选型
| 方案 | 优点 | 缺点 |
|---|---|---|
| 无需编译内核 | 帧率极低(~16FPS)、CPU 占用高 | |
| 内核态 DRM panel-mipi-dbi | 硬件 SPI DMA、~30FPS、系统原生集成 | 需编译内核模块 |
本文采用 DRM panel-mipi-dbi 方案。
二、硬件连接
2.1 引脚映射(RDK X5 40PIN)
| 功能 | RDK X5 引脚 | 设备树 GPIO 引用 | 备注 |
|---|---|---|---|
| SPI SCK | Pin 23 (SPI_CLK) | - | SPI1 总线 |
| SPI MOSI | Pin 19 (SPI_MOSI) | - | |
| SPI MISO | Pin 21 (SPI_MISO) | - | ST7796S 读取不可靠,驱动设为 write-only |
| SPI CS | Pin 26 (CS1) | reg = <1> |
必须用 CS1,避免与板载 IMU 冲突 |
| DC | Pin 15 (BCM 22) | &ls_gpio0_porta 9 |
数据/命令选择 |
| Reset | Pin 13 (BCM 27) | &ls_gpio0_porta 0 |
屏幕复位 |
| Backlight | Pin 12 (BCM 18) | &dsp_gpio_porta 10 |
sysfs GPIO 421,高电平点亮 |
| TP SDA | Pin 3 (I2C_SDA) | - | I2C5 总线 |
| TP SCL | Pin 5 (I2C_SCL) | - | I2C5 总线 |
| TP INT | Pin 22 (BCM 4) | &dsp_gpio_porta 9 |
下降沿触发 |
| TP RST | - | &ls_gpio1_porta 7 |
低电平有效 |
| VCC | Pin 4 (5V) | - | 逻辑电压 3.3V |
| GND | Pin 6 | - |
重要提示: 确认引脚映射时可使用板载
hb_gpioinfo工具查询物理引脚与设备树引用的对应关系。
三、框架设计
3.1 整体架构
┌─────────────────────────────────────────────────┐
│ X11 / 应用层 │
│ (modesetting 驱动) │
├─────────────────────────────────────────────────┤
│ DRM/KMS 子系统 │
│ /dev/dri/card0 (SPI LCD) │
├─────────────────────────────────────────────────┤
│ panel-mipi-dbi 驱动 │
│ ┌──────────────┬──────────────────────┐ │
│ │ drm_mipi_dbi │ drm_gem_dma_helper │ │
│ └──────────────┴──────────────────────┘ │
├─────────────────────────────────────────────────┤
│ SPI Controller (DMA) │ GPIO (DC/RST/BL) │
│ /dev/spi1.x │ sysfs / gpiochip │
├─────────────────────────────────────────────────┤
│ ST7796S LCD Panel │
└─────────────────────────────────────────────────┘
3.2 关键组件
| 组件 | 作用 | 来源 |
|---|---|---|
panel-mipi-dbi.ko |
DRM 面板驱动,解析固件初始化序列 | Linux 内核 |
drm_mipi_dbi.ko |
MIPI DBI 传输层(SPI 命令封装) | Linux 内核 |
drm_gem_dma_helper.ko |
DRM GEM 内存管理 | Linux 内核 |
gpio_backlight.ko |
GPIO 背光控制 | Linux 内核 |
panel-mipi-dbi-spi.bin |
ST7796S 初始化命令固件 | 自行生成 |
overlay-st7796s.dtbo |
LCD 设备树 overlay | 自行编写 |
overlay-gt911.dtbo |
触摸屏设备树 overlay | 自行编写 |
3.3 数据流
应用绘制 → DRM GEM buffer → SPI DMA → ST7796S VRAM → LCD 面板
四、实现步骤
4.1 环境准备
板子信息确认:
uname -r # 确认内核版本(如 6.1.83)
hb_gpioinfo # 确认 GPIO 映射
ls /dev/spidev* # 确认 SPI 设备
ls /dev/i2c* # 确认 I2C 设备
安装内核头文件(如未安装):
apt install hobot-kernel-headers
ln -sf /usr/src/linux-headers-6 /lib/modules/$(uname -r)/build
准备编译工具:
# 在内核头文件目录编译构建脚本
cd /usr/src/linux-headers-6
make scripts
4.2 获取驱动源码
从 D-Robotics 内核仓库下载所需的 4 个源文件:
BASE_URL="https://raw.githubusercontent.com/D-Robotics/x5-kernel/main"
# DRM MIPI DBI 核心
curl -fsSL -o drm_mipi_dbi.c "$BASE_URL/drivers/gpu/drm/drm_mipi_dbi.c"
# 面板驱动
curl -fsSL -o panel-mipi-dbi.c "$BASE_URL/drivers/gpu/drm/tiny/panel-mipi-dbi.c"
# GEM DMA 辅助
curl -fsSL -o drm_gem_dma_helper.c "$BASE_URL/drivers/gpu/drm/drm_gem_dma_helper.c"
# GPIO 背光
curl -fsSL -o gpio_backlight.c "$BASE_URL/drivers/video/backlight/gpio_backlight.c"
4.3 关键补丁:ST7796S 复位时序
这是最容易踩坑的地方! ST7796S 复位需要至少 10ms,但内核默认只有 20μs。
# 修改 drm_mipi_dbi.c 第 603 行
sed -i 's/usleep_range(20, 1000)/usleep_range(10000, 15000)/' drm_mipi_dbi.c
不修改的话,面板初始化会随机失败。
4.4 编译内核模块
mkdir -p /tmp/mipi_dbi_build
cp drm_mipi_dbi.c drm_gem_dma_helper.c panel-mipi-dbi.c /tmp/mipi_dbi_build/
cat > /tmp/mipi_dbi_build/Makefile << 'EOF'
KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
obj-m += drm_mipi_dbi.o
obj-m += drm_gem_dma_helper.o
obj-m += panel-mipi-dbi.o
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
EOF
cd /tmp/mipi_dbi_build
make -j$(nproc)
单独编译背光模块:
mkdir -p /tmp/bl_build
cp gpio_backlight.c /tmp/bl_build/
cat > /tmp/bl_build/Makefile << 'EOF'
KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
obj-m += gpio_backlight.o
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
EOF
cd /tmp/bl_build
make -j$(nproc)
安装模块:
DRM_DIR=/lib/modules/$(uname -r)/kernel/drivers/gpu/drm
mkdir -p $DRM_DIR/tiny
cp /tmp/mipi_dbi_build/drm_mipi_dbi.ko $DRM_DIR/
cp /tmp/mipi_dbi_build/drm_gem_dma_helper.ko $DRM_DIR/
cp /tmp/mipi_dbi_build/panel-mipi-dbi.ko $DRM_DIR/tiny/
cp /tmp/bl_build/gpio_backlight.ko /lib/modules/$(uname -r)/kernel/drivers/video/backlight/
depmod -a
配置开机自动加载:
cat > /etc/modules-load.d/mipi-dbi-lcd.conf << 'EOF'
drm_gem_dma_helper
drm_mipi_dbi
gpio_backlight
panel-mipi-dbi
EOF
4.5 生成 ST7796S 初始化固件
固件格式说明:
- 文件头:15 字节 Magic
"MIPI DBI\0\0\0\0\0\0\0"+ 1 字节版本号0x01 - 命令格式:
[CMD] [参数个数] [参数...] - 延时命令:
[0x00] [0x01] [延时毫秒数]
#!/usr/bin/env python3
"""generate_st7796s_fw.py - 生成 panel-mipi-dbi-spi.bin"""
import struct
MAGIC = b"MIPI DBI\x00\x00\x00\x00\x00\x00\x00"
VERSION = 1
def build_commands():
cmds = bytearray()
def add_cmd(cmd, *params):
cmds.append(cmd)
cmds.append(len(params))
cmds.extend(params)
def add_delay(ms):
cmds.append(0x00)
cmds.append(0x01)
cmds.append(ms & 0xFF)
add_cmd(0x01) # Software Reset
add_delay(120)
add_cmd(0x11) # Sleep Out
add_delay(120)
add_cmd(0x3A, 0x55) # 16bit/pixel (RGB565)
add_cmd(0xB6, 0x80, 0x02, 0x3B) # Display Function Control
add_cmd(0xB7, 0xC6) # Entry Mode Set
add_cmd(0xC5, 0xA0) # Display Output Ctrl Adjust
add_cmd(0xD0, 0xA7, 0x41, 0x1D) # Power Control 1
# Gamma
add_cmd(0xE0, 0xF0, 0x09, 0x0B, 0x06, 0x04, 0x15, 0x2F,
0x54, 0x42, 0x3C, 0x17, 0x14, 0x18, 0x1B)
add_cmd(0xE1, 0xE0, 0x09, 0x0B, 0x06, 0x04, 0x03, 0x2B,
0x43, 0x42, 0x3B, 0x16, 0x14, 0x17, 0x1B)
add_cmd(0x21) # Display Inversion On
add_delay(120)
# MADCTL: 横向显示 (480x320)
# MY=0, MX=0, MV=1, ML=0, BGR=1, MH=0 => 0x28
add_cmd(0x36, 0x28)
add_cmd(0x29) # Display ON
add_delay(120)
return bytes(cmds)
header = MAGIC + struct.pack("B", VERSION)
commands = build_commands()
with open("panel-mipi-dbi-spi.bin", "wb") as f:
f.write(header + commands)
print(f"Generated: {len(header) + len(commands)} bytes")
python3 generate_st7796s_fw.py
cp panel-mipi-dbi-spi.bin /lib/firmware/
4.6 编写设备树 Overlay
LCD Overlay (overlay-st7796s.dts)
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target-path = "/";
__overlay__ {
backlight: backlight {
compatible = "gpio-backlight";
gpios = <&dsp_gpio_porta 10 0>;
default-on;
};
};
};
fragment@1 {
target-path = "/soc/a55_apb0/spi@34010000";
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
/* 禁用 CS1 上的 spidev 和 IMU,避免冲突 */
spidev@1 { status = "disabled"; };
bmi08a@1 { status = "disabled"; };
panel-mipi-dbi@1 {
compatible = "panel-mipi-dbi-spi";
reg = <1>; /* CS1 */
spi-max-frequency = <40000000>; /* 40MHz */
dc-gpios = <&ls_gpio0_porta 9 0>; /* BCM 22 */
reset-gpios = <&ls_gpio0_porta 0 0>; /* BCM 27 */
backlight = <&backlight>;
write-only; /* ST7796S 读取不可靠 */
width-mm = <85>;
height-mm = <53>;
panel-timing {
clock-frequency = <0>;
hactive = <480>;
vactive = <320>;
hfront-porch = <0>;
hsync-len = <0>;
hback-porch = <0>;
vfront-porch = <0>;
vsync-len = <0>;
vback-porch = <0>;
};
};
};
};
};
触摸屏 Overlay (overlay-gt911.dts)
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target-path = "/soc/a55_apb0/i2c@341c0000";
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
gt911@5d {
compatible = "goodix,gt911";
reg = <0x5d>;
interrupt-parent = <&dsp_gpio_porta>;
interrupts = <9 2>; /* 下降沿触发 */
reset-gpios = <&ls_gpio1_porta 7 1>; /* 低电平有效 */
touchscreen-size-x = <480>;
touchscreen-size-y = <320>;
/* 注意:不要在 DTS 中加 invert/swap 属性,坐标变换在 X11 层处理 */
};
};
};
};
编译并部署 Overlay:
dtc -@ -I dts -O dtb -o overlay-st7796s.dtbo overlay-st7796s.dts
dtc -@ -I dts -O dtb -o overlay-gt911.dtbo overlay-gt911.dts
cp overlay-st7796s.dtbo /boot/overlays/
cp overlay-gt911.dtbo /boot/overlays/
4.7 配置 Boot 加载 Overlay
编辑 /boot/config.txt:
dtoverlay=overlay-st7796s
dtoverlay=overlay-gt911
末尾必须有空行,否则最后一个 dtoverlay 不会被识别。
4.8 配置 X11
LCD 输出配置 (/etc/X11/xorg.conf.d/99-spi-lcd.conf):
Section "Device"
Identifier "SPI LCD"
Driver "modesetting"
Option "kmsdev" "/dev/dri/card0"
EndSection
必须用
modesetting驱动,不要用fbdev。kmsdev指向 SPI LCD 对应的 DRM card(通常是 card0)。
禁用冲突的默认配置:
# 禁用 HDMI 分辨率配置和 GPU 加速配置
mv /etc/X11/xorg.conf.d/1-resolution.conf /etc/X11/xorg.conf.d/1-resolution.conf.disable
mv /etc/X11/xorg.conf.d/2-dr-accel.conf /etc/X11/xorg.conf.d/2-dr-accel.conf.disable
触摸校准 (/etc/X11/xorg.conf.d/90-touchscreen.conf):
Section "InputClass"
Identifier "Goodix TouchScreen Calibration"
MatchProduct "Goodix Capacitive TouchScreen"
MatchDriver "libinput"
Option "CalibrationMatrix" "0 -0.6667 1 1 0 0 0 0 1"
EndSection
CalibrationMatrix 含义(将竖屏触摸坐标旋转映射到横屏显示):
屏幕X = 0 * touchX + (-0.6667) * touchY + 1
屏幕Y = 1 * touchX + 0 * touchY + 0
如果触摸方向不对,可以尝试以下组合:
"0 1 0 -1 0 1 0 0 1"— 180° 翻转"0 -1 1 -1 0 1 0 0 1"— 旋转 + 双轴翻转"0 1 0 1 0 0 0 0 1"— 翻转 Y 轴
4.9 背光服务(备用)
如果 gpio-backlight 模块工作正常,背光会自动由 DRM 管理。作为备用方案,创建 systemd 服务:
cat > /etc/systemd/system/st7796s-backlight.service << 'EOF'
[Unit]
Description=ST7796S LCD Backlight
After=multi-user.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/sh -c 'echo 421 > /sys/class/gpio/export 2>/dev/null; echo out > /sys/class/gpio/gpio421/direction; echo 1 > /sys/class/gpio/gpio421/value'
ExecStop=/bin/sh -c 'echo 0 > /sys/class/gpio/gpio421/value'
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable st7796s-backlight.service
五、验证清单
重启板子后,逐项确认:
# 1. 内核模块加载
lsmod | grep -E 'panel_mipi_dbi|drm_mipi_dbi|drm_gem_dma|gpio_backlight'
# 2. DRM 设备
ls /dev/dri/
# 预期: card0 (SPI LCD), card1 (HDMI)
# 3. 面板连接状态
cat /sys/class/drm/card0-SPI-1/status
# 预期: connected
# 4. Framebuffer
cat /sys/class/graphics/fb0/name
# 预期: panel-mipi-dbid
# 5. 背光
ls /sys/class/backlight/
# 预期: backlight
# 6. X11 输出
grep 'card0\|modesetting\|480x320' /var/log/Xorg.0.log
# 7. 触摸
grep 'Goodix\|CalibrationMatrix' /var/log/Xorg.0.log
六、常见问题排查
Q1: 面板 probe 失败,dmesg 显示 “deferred probe pending”
原因: 背光设备未就绪(gpio_backlight.ko 未加载)。
解决: 确认 gpio_backlight 模块已安装并在 modules-load.d 中配置。
Q2: 面板初始化失败/读 ID 报错
原因: ST7796S SPI 读取不可靠,且默认复位时序不足。
解决:
- 设备树添加
write-only属性 - 修改
drm_mipi_dbi.c复位延时:usleep_range(20, 1000)→usleep_range(10000, 15000)
Q3: 触摸坐标偏移/镜像
原因: DTS 中不应使用 touchscreen-inverted-x 等属性,会导致 libinput 归一化错误。
解决: DTS 保持原始竖屏参数 (480×320),所有坐标变换通过 X11 CalibrationMatrix 完成。
Q4: X11 渲染异常/GLX 错误
原因: 使用了 fbdev 驱动。
解决: 强制使用 modesetting 驱动。
Q5: CS 冲突导致 SPI 通信失败
原因: CS0 被板载 IMU (BMI088) 占用。
解决: 设备树指定 reg = <1> (CS1),并显式 status = "disabled" 禁用 CS1 上的 spidev。
Q6: config.txt 中最后一个 overlay 不生效
原因: 文件末尾没有空行。
解决: 确保 /boot/config.txt 末尾有一个空行。
七、一键部署脚本
将以上所有步骤整合为一键脚本,在板子上执行即可完成全部部署:
#!/bin/bash
set -e
echo "=== RDK X5 SPI LCD 一键部署 ==="
# 安装内核头文件
apt install -y hobot-kernel-headers
ln -sf /usr/src/linux-headers-6 /lib/modules/$(uname -r)/build
cd /usr/src/linux-headers-6 && make scripts
# 编译模块
BUILD_DIR="/tmp/mipi_dbi_build"
rm -rf "$BUILD_DIR" && mkdir -p "$BUILD_DIR" && cd "$BUILD_DIR"
BASE_URL="https://raw.githubusercontent.com/D-Robotics/x5-kernel/main"
curl -fsSL -o drm_mipi_dbi.c "$BASE_URL/drivers/gpu/drm/drm_mipi_dbi.c"
curl -fsSL -o drm_gem_dma_helper.c "$BASE_URL/drivers/gpu/drm/drm_gem_dma_helper.c"
curl -fsSL -o panel-mipi-dbi.c "$BASE_URL/drivers/gpu/drm/tiny/panel-mipi-dbi.c"
# 关键补丁
sed -i 's/usleep_range(20, 1000)/usleep_range(10000, 15000)/' drm_mipi_dbi.c
cat > Makefile << 'EOF'
KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
obj-m += drm_mipi_dbi.o
obj-m += drm_gem_dma_helper.o
obj-m += panel-mipi-dbi.o
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
EOF
make -j$(nproc)
# 背光模块
BL_DIR="/tmp/bl_build"
rm -rf "$BL_DIR" && mkdir -p "$BL_DIR" && cd "$BL_DIR"
curl -fsSL -o gpio_backlight.c "$BASE_URL/drivers/video/backlight/gpio_backlight.c"
cat > Makefile << 'EOF'
KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
obj-m += gpio_backlight.o
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
EOF
make -j$(nproc)
# 安装模块
DRM_DIR=/lib/modules/$(uname -r)/kernel/drivers/gpu/drm
mkdir -p $DRM_DIR/tiny
cp $BUILD_DIR/drm_mipi_dbi.ko $DRM_DIR/
cp $BUILD_DIR/drm_gem_dma_helper.ko $DRM_DIR/
cp $BUILD_DIR/panel-mipi-dbi.ko $DRM_DIR/tiny/
cp $BL_DIR/gpio_backlight.ko /lib/modules/$(uname -r)/kernel/drivers/video/backlight/
depmod -a
# 模块自动加载
cat > /etc/modules-load.d/mipi-dbi-lcd.conf << 'EOF'
drm_gem_dma_helper
drm_mipi_dbi
gpio_backlight
panel-mipi-dbi
EOF
echo "=== 模块编译安装完成 ==="
echo "请手动完成以下步骤:"
echo "1. 生成固件: python3 generate_st7796s_fw.py && cp panel-mipi-dbi-spi.bin /lib/firmware/"
echo "2. 部署 overlay: dtc -@ -I dts -O dtb -o *.dtbo *.dts && cp *.dtbo /boot/overlays/"
echo "3. 配置 /boot/config.txt"
echo "4. 配置 X11"
echo "5. reboot"
八、参考链接
- Waveshare 3.5inch RPi LCD (F) Wiki
- RDK X5 GPIO 配置
- RDK X5 SPI 配置
- RDK X5 驱动开发
- RDK X5 系统源码 (x5-rdk-gen)
- RDK X5 内核源码 (x5-kernel)
- 相关参考帖
九、
一键部署包下载(免编译)
为让大家快速复现,我把所有已编译的内核模块、设备树 overlay、固件和配置文件打包好了,下载后在板子上跑一行命令即可完成部署。
下载
rdk-x5-lcd-st7796s-deploy.zip (578 KB)
包内结构
rdk-x5-lcd-st7796s-deploy/
├── install.sh # 一键安装
├── verify.sh # 验证脚本
├── README.md # 说明文档
├── config.txt # /boot/config.txt 追加内容
├── kernel/ # 4 个已编译 .ko (for kernel 6.1.83)
│ ├── drm_mipi_dbi.ko
│ ├── drm_gem_dma_helper.ko
│ ├── panel-mipi-dbi.ko
│ └── gpio_backlight.ko
├── overlays/ # 设备树 overlay (.dtbo + .dts 源码)
│ ├── overlay-st7796s.dtbo
│ ├── overlay-gt911.dtbo
│ ├── overlay-st7796s.dts
│ └── overlay-gt911.dts
├── firmware/
│ └── panel-mipi-dbi-spi.bin # ST7796S 初始化固件
├── x11/ # X11 modesetting + 触摸校准
├── systemd/ # 背光服务
└── modules-load/ # 开机自动加载列表
3 步部署
# 1) 上传到板子
scp rdk-x5-lcd-st7796s-deploy.zip root@<RDK_X5_IP>:/tmp/
# 2) 解压并安装
ssh root@<RDK_X5_IP>
cd /tmp
unzip rdk-x5-lcd-st7796s-deploy.zip
sudo bash install.sh
# 3) 重启
sudo reboot
# 重启后验证
sudo bash verify.sh
部署包中的
.ko模块基于内核 6.1.83 编译。若板子内核版本不同,请先uname -r确认;如不一致可参考本文第四章自行编译。
验证脚本输出示例
=== RDK X5 SPI LCD 验证 ===
[模块加载]
[✓] drm_gem_dma_helper
[✓] drm_mipi_dbi
[✓] panel_mipi_dbi
[✓] gpio_backlight
[DRM 设备]
[✓] /dev/dri/card0 (SPI LCD)
[✓] /dev/dri/card1 (HDMI)
[✓] SPI-1 connected
[Framebuffer]
[✓] fb0 = panel-mipi-dbid
[背光]
[✓] backlight 设备存在
[X11]
[✓] X11 使用 modesetting
[✓] X11 480x320 模式
[✓] 触摸 CalibrationMatrix

