Luckfox RV1103 + GC9A01 SPI LCD + Camera 实时预览:完整调试记录

记录日期:2026-11-14
硬件:Luckfox Pico Plus Mini / RV1103,GC9A01 240×240 SPI 圆屏,板载/连接的 MIPI 摄像头
软件:Luckfox SDK,Linux 5.10 系列(此前现场为 5.10.160),Buildroot/uClibc,FBTFT,V4L2,RKISP
实际结果:/dev/fb0 已成功注册,纯红画面成功;摄像头实时显示成功,用户观察约 20 FPS;启动 RKAIQ 3A 服务后偏暗问题有所改善。
真实性标注:本文区分【现场确认】【源码确认】【建议/待测】。未提供的真实 DTS 文件、完整内核原始版本、示波器实测 SCLK 不会被虚构。

0. 项目目标、范围与成果

目标是将 RV1103 的 MIPI Camera 采集到的图像直接显示在 1.1 英寸 GC9A01 240×240 圆形 SPI LCD 上,尽量少改代码,优先验证从 Sensor 到屏幕的整条链路。LCD 最初按 ST7789V 的 FBTFT 框架调试;后来临时将 fb_st7789v.c 的初始化寄存器序列替换为 GC9A01 序列,但保留 fb_st7789v 驱动名和 sitronix,st7789v compatible,便于快速验证。这是实验方案,不是正式产品应采用的驱动命名和设备树兼容性设计。

阶段性成果:

  1. 【现场确认】SPI0 被内核枚举;spi0.0 最终绑定 fb_st7789v,并出现 /dev/fb0。
  2. 【现场确认】GC9A01 能够显示红色;DTS 声明 GPIO1_D0 背光、驱动初始化后点亮背光。
  3. 【现场确认】V4L2 枚举到 RKISP mainpath /dev/video11,NV12 格式可用。
  4. 【现场确认】camera_fb.c 运行后摄像头画面能显示到 GC9A01。
  5. 【现场确认】用户观察约 20 FPS,图像偏暗;启动 rkaiq_3A_server 后画面改善。
  6. 【待测】30/40/50 MHz SPI 频率、实际屏幕刷新 FPS、SPI DMA/驱动传输效率、持续运行稳定性。

1. 系统数据通路

MIPI Camera Sensor
   │ MIPI CSI-2,Bayer RAW(取决于 Sensor)
   ▼
Rockchip RKCIF / RKISP
   │ ISP 处理:曝光/白平衡等由 RKAIQ 3A 服务协调
   ▼
/dev/video11  (rkisp_mainpath)
   │ V4L2 VIDEO_CAPTURE_MPLANE
   │ 单个内存平面的 NV12,典型请求 640×480
   ▼
camera_fb.c
   │ VIDIOC_REQBUFS → QUERYBUF → mmap → QBUF
   │ STREAMON → select → DQBUF → QBUF
   │ NV12 → RGB565;中心裁切/最近邻缩放
   ▼
/dev/fb0 (mmap)
   │ 应用逐像素写 framebuffer
   ▼
Linux FBTFT deferred I/O / SPI 总线写入
   ▼
SPI0 → GC9A01 240×240 RGB565 圆屏

重要区分:应用中的 row[x] = px 仅写 framebuffer 的一个 16-bit 像素,不等于立即发送一个 SPI 事务。内核 FBTFT 负责批量刷新。camera_fb.c 统计的是采集并处理的帧率,不保证等于实际 LCD 刷新率。

2. 硬件接线

2.1 已确认的 SPI 屏幕引脚

GC9A01 模块信号RV1103 GPIO说明
CSGPIO1_C0SPI0 硬件片选 CS0
SCL / SCKGPIO1_C1SPI0 时钟
SDA / DIN / MOSIGPIO1_C2SPI0 主机输出
RES / RSTGPIO1_C4GPIO 复位,低有效
DC / A0GPIO1_C5GPIO 命令/数据选择
BL / LEDGPIO1_D0GPIO 背光控制,高有效
VCC按模块规格不要未经核实把 5 V 接到 3.3 V 逻辑模块
GNDGND必须共地

注意:这是 RV1103 的 GPIO 命名,不是 Luckfox 排针的物理脚号。实际接线必须按所用板卡的排针图核对。SPI0 的 C0/C1/C2 需要 pinmux 为 SPI 功能;C4/C5/D0 为 GPIO。背光 GPIO 如果直接驱动 LED,必须确认模块已有合适的限流/驱动电路,不能假定 SoC GPIO 可直接承担任意背光电流。

2.2 摄像头连接

摄像头走 RV1103 的 MIPI CSI 接口,不经过 SPI LCD 的这些引脚。具体 Sensor 型号、FPC 针脚、电源轨及复位脚在本次对话中未提供,故不虚构。系统已枚举 RKCIF/RKISP 节点且用户已成功显示画面,说明当前摄像头链路至少具备可用的视频采集能力。

3. 内核配置和最初排查

最初系统已具备如下配置或驱动能力:

CONFIG_FB=y
CONFIG_FB_TFT=y
CONFIG_FB_TFT_ST7789V=y

系统可看到 /sys/bus/spi/drivers/fb_st7789v,但早期 /dev/fb0 不存在。这说明驱动编译进内核不代表已经匹配到 SPI 设备并完成 Probe。

3.1 检查 SPI 控制器

ls -l /sys/class/spi_master/
ls -l /sys/bus/spi/devices/
ls -l /sys/bus/spi/drivers/

【现场历史输出】系统可看到 spi0 和 spi2;其中 spi2.0 是 spi-nand,负责存储。绝对不要为了屏幕测试去 unbind SPI2 的 NAND 设备,否则可能破坏运行时存储访问。

3.2 解决 SPI0 CS0 节点冲突

早期 spi0.0 绑定到了 spidev(compatible rockchip,spidev),而屏幕设备节点也想使用 reg = <0>。一个 SPI 控制器的同一个 CS 只能绑定一个设备实例,不能让 spidev@0 和 fbtft@0 同时启用。

操作原则:

  1. 在实际板级 DTS/DTSI 中启用 &spi0;
  2. 禁用与 CS0 冲突的 spidev@0;
  3. 在 &spi0 下启用 fbtft@0,reg = <0>;
  4. 保留 SPI0 引脚复用;
  5. 编译烧录并观察 dmesg。

检查绑定:

readlink /sys/bus/spi/devices/spi0.0/driver
dmesg | grep -Ei 'spi0|st7789|fbtft|graphics fb'
ls -l /dev/fb*
cat /proc/fb

【现场确认】曾看到如下成功日志:

[0.160688] fb_st7789v spi0.0: fbtft_property_value: buswidth = 8
[0.160706] fb_st7789v spi0.0: fbtft_property_value: debug = 7
[0.160718] fb_st7789v spi0.0: fbtft_property_value: fps = 30
[0.487839] graphics fb0: fb_st7789v frame buffer, 240x320, 150 KiB video memory, 4 KiB buffer memory, fps=33, spi0.0 at 20 MHz

这里 240x320 是改为 GC9A01 240×240 之前的 ST7789 配置日志,不能当作最终 GC9A01 的日志。它证明 FBTFT / SPI0 / framebuffer 绑定链路当时已打通。

4. GC9A01 内核驱动改造

4.1 为什么临时复用 ST7789 驱动

GC9A01 与 ST7789 都使用 SPI 发送命令和像素数据,但初始化寄存器序列不同。FBTFT 已经提供 SPI 发送、GPIO DC/RESET、framebuffer 注册及刷新机制,因此测试阶段只替换初始化序列即可。

实际最终文件:本工程 fb_st7789v.c。它不是伪代码,而是对话中提供的实际最终版本的副本。

保持:

#define DRVNAME "fb_st7789v"
FBTFT_REGISTER_DRIVER(DRVNAME, "sitronix,st7789v", &display);

变更:

  • 将 init_display() 换成已在 ESP32 上验证过的 GC9A01 寄存器初始化序列;
  • display.width = 240,display.height = 240;
  • 保留 set_var() 的 MADCTL 旋转设置及 blank();
  • 删除原 ST7789 专属的 gamma 写入逻辑;
  • 通过 devm_gpiod_get_optional() 获取 DTS 背光 GPIO;
  • 0x11 Sleep Out 后等待 120 ms,再 0x29 Display On,随后打开背光。

4.2 初始化关键命令

par->fbtftops.reset(par);
mdelay(150);
/* 大量 GC9A01 厂商寄存器配置,完整内容见 fb_st7789v.c */
write_reg(par, 0x36, 0x08); /* MADCTL 初始值 */
write_reg(par, 0x3A, 0x05); /* RGB565 */
write_reg(par, 0x21);       /* Display inversion ON */
write_reg(par, 0x11);       /* Sleep Out */
mdelay(120);
write_reg(par, 0x29);       /* Display On */
mdelay(20);

请勿把以上摘要替代完整寄存器表。0xEF、0xEB、0x62、0x63 等厂商命令完整保留在源码中,来自之前用户已验证的 ESP32 GC9A01 驱动初始化。

注意颜色:init_display() 写入 0x36, 0x08,而 set_var() 可能依据 bgr、rotate 再次写 MADCTL。若出现红蓝交换或旋转异常,应检查 DTS bgr、rotate 和 FBTFT 实际调用顺序,而不是随意调整 RGB565 数据。

4.3 背光 GPIO 的最终代码

#include <linux/gpio/consumer.h>
#include <linux/err.h>

static int init_display(struct fbtft_par *par)
{
    struct gpio_desc *backlight;

    backlight = devm_gpiod_get_optional(&par->spi->dev,
                                       "backlight", GPIOD_OUT_LOW);
    if (IS_ERR(backlight))
        return PTR_ERR(backlight);

    /* GC9A01 初始化 ... */

    write_reg(par, 0x29);
    mdelay(20);

    if (backlight)
        gpiod_set_value_cansleep(backlight, 1);

    return 0;
}

【现场确认】用户反馈“的确可以显示红色”,说明当前临时驱动及背光方案在现场有效。

补充限制:这版背光仅在初始化后打开,没有实现 blank/unbind/suspend 时关闭背光;正式驱动应把背光资源存放在私有上下文,或使用标准 backlight 子系统,避免在重复初始化场景下再次申请同一个 GPIO。

4.4 本工程的 Patch 文件含义

0002-add-dts-backlight-control.patch 是从本次已有的 GC9A01 无背光测试版本到GC9A01 最终背光版本的真实统一差异补丁,可用于理解增加了哪些代码。

它不是对 Luckfox 原版 ST7789 驱动的完整 patch:因为本次对话没有完整保留用户 SDK 中那份原始 fb_st7789v.c 的精确版本。若要复现,直接使用本工程完整 fb_st7789v.c 替换 SDK 对应文件,或在自己 SDK 中用 git diff 生成准确的正式补丁。

5. DTS 配置

本工程包含 gc9a01-spi0-overlay-example.dts 作为板级 DTS 片段示例,不是独立可编译的完整 DTB。实际应合并进所用 Luckfox 板卡的 DTS/DTSI,并保留原有 SPI0 pinctrl。

#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/pinctrl/rockchip.h>

&spi0 {
    status = "okay";

    fbtft@0 {
        compatible = "sitronix,st7789v";
        reg = <0>;
        spi-max-frequency = <20000000>;
        buswidth = <8>;
        fps = <30>;
        debug = <7>;
        width = <240>;
        height = <240>;
        rotate = <0>;
        bgr = <0>;

        reset-gpios = <&gpio1 RK_PC4 GPIO_ACTIVE_LOW>;
        dc-gpios = <&gpio1 RK_PC5 GPIO_ACTIVE_HIGH>;
        backlight-gpios = <&gpio1 RK_PD0 GPIO_ACTIVE_HIGH>;

        status = "okay";
    };
};

DTS 的 GPIO_ACTIVE_HIGH 说明对背光而言逻辑 1 是物理高电平。驱动使用 GPIOD_OUT_LOW 初始化关闭,再用 gpiod_set_value_cansleep(..., 1) 打开。

不要同时配置 GPIO1_D0 的 gpio-hog 和 backlight-gpios,否则可能导致 GPIO 所有权冲突。

reset-gpios、dc-gpios 的实际有效电平和节点名称应优先沿用现场已工作的 DTS;上面仅为记录示例,不能证明与你 SDK 的具体 GPIO 解析路径完全一致。

5.1 SPI 频率验证

当前现场成功配置是 20 MHz。可分阶段尝试:

spi-max-frequency = <30000000>;
/* 稳定后改为 40000000,再考虑 50000000 */

RV1103 SPI Master 的 50 MHz 上限曾在前面对话引用 Rockchip/Luckfox 资料,但本次现场尚未测到 50 MHz。不要把 50 MHz 当作已经验证稳定的 GC9A01 模块频率。

检查 DT 请求值:

hexdump -C /sys/bus/spi/devices/spi0.0/of_node/spi-max-frequency

20 MHz = 01 31 2d 00;50 MHz = 02 fa f0 80,均为 DT 大端 32 位值。该属性是请求值,不是示波器测得的 SCLK。实际频率须在 GPIO1_C1 测量。

6. 编译和烧录 LCD 驱动

在 Luckfox SDK 根目录下先定位源码:

find sysdrv/source/kernel -name fb_st7789v.c

通常为:

sysdrv/source/kernel/drivers/staging/fbtft/fb_st7789v.c

操作示例:

cp sysdrv/source/kernel/drivers/staging/fbtft/fb_st7789v.c \
   /tmp/fb_st7789v.c.original

cp fb_st7789v.c \
   sysdrv/source/kernel/drivers/staging/fbtft/fb_st7789v.c

./build.sh kernel
./build.sh firmware

构建命令可能随 SDK 版本不同而调整。烧录方式使用你现有已经成功的固件部署流程,不在这里虚构分区或刷写地址。

重启验证:

dmesg | grep -Ei 'st7789|gc9a01|spi0|graphics fb|backlight'
ls -l /dev/fb0
cat /proc/fb

预期分辨率应为 240x240,RGB565 framebuffer 容量按像素计算为 115200 字节(约 112.5 KiB);实际内存分配、缓冲长度可能有额外开销。

6.1 简单刷红色测试

对 16-bit RGB565,纯红色像素值是 0xF800。在小端 ARM CPU 内存中连续字节为 00 f8。可在板上使用已有刷屏程序,或用下面的 Python 伪测试(仅当板上安装 Python):

with open('/dev/fb0', 'wb', buffering=0) as f:
    f.write(b'\x00\xf8' * (240 * 240))

注意:这段简易测试假设 framebuffer 的实际颜色位域为 RGB565、行跨度 480 字节,且 write() 方式被 fbdev 支持;不满足时应以 FBIOGET_*SCREENINFO 查询结果为准。现场已确认红色显示成功。

7. V4L2 摄像头设备枚举

【现场输出】:

rkisp-statistics (platform: rkisp):
    /dev/video19
    /dev/video20

rkcif-mipi-lvds (platform:rkcif):
    /dev/media0

rkcif (platform:rkcif-mipi-lvds):
    /dev/video0 ... /dev/video10

rkisp_mainpath (platform:rkisp-vir0):
    /dev/video11 ... /dev/video18
    /dev/media1

经 v4l2-ctl --info:

  • /dev/video0~/dev/video10:rkcif,Video Capture Multiplanar、Streaming;
  • /dev/video11:rkisp_mainpath,多平面采集,优先作为预览输出;
  • /dev/video12:rkisp_selfpath,备用;
  • /dev/video13:bypasspath;
  • /dev/video17、/dev/video18:rawrd 输入,不适合直接预览;
  • /dev/video19:ISP 统计;
  • /dev/video20:ISP 参数输入。

实际命令:

v4l2-ctl --list-devices
v4l2-ctl -d /dev/video11 --list-formats-ext
v4l2-ctl -d /dev/video12 --list-formats-ext
v4l2-ctl -d /dev/video11 --get-fmt-video

7.1 ISP mainpath 支持的格式

【现场确认】/dev/video11 列出:

UYVY, NV16, NV61, NV21, NV12, NM21, NM12

尺寸范围:32x32 到 2304x1296,步进 8x8。

/dev/video12 除上述格式外,还提供 GREY、XR24、RGBP(RGB565)。这说明未来可以考虑从 selfpath 直接输出 RGB565,以减少软件 YUV 转换,但尚未在本次项目实测 selfpath RGBP 是否能正常采集、颜色/尺寸是否匹配。

7.2 当前格式的关键输出

【现场原始输出】:

Format Video Capture Multiplanar:
    Width/Height      : 2304/1296
    Pixel Format      : 'NV12' (Y/CbCr 4:2:0)
    Number of planes  : 1
    Quantization      : Full Range
    Plane 0:
       Bytes per Line : 2304
       Size Image     : 1119744

关键知识点:VIDEO_CAPTURE_MPLANE 表示 V4L2 API 使用多平面接口,但当前 NV12 的 num_planes = 1,Y 和 UV 在同一内存缓冲区内;程序必须正确设置 struct v4l2_plane,但只需映射这一块缓冲区。

7.3 采集验证命令

v4l2-ctl -d /dev/video11 \
  --stream-mmap \
  --stream-count=30 \
  --stream-to=/tmp/camera.nv12
ls -lh /tmp/camera.nv12

扩展名 .nv12 只是文件名,实际格式仍以协商结果为准。若 STREAMON 超时,应检查 media pipeline、sensor 及 RKAIQ,而不是直接判断 LCD 故障。

8. 用户态 camera_fb.c 完整设计

本工程 camera_fb.c 是此前实际生成的最终版本(非重写示例),约 9 KB,标准 C + Linux UAPI,无 OpenCV/FFmpeg/LVGL 依赖。

8.1 参数与设备打开

./camera_fb /dev/video11 /dev/fb0 640 480

argv[1] 摄像头节点;argv[2] framebuffer;argv[3]/argv[4] 请求采集宽高。默认值就是上述参数。

vfd = open(camera, O_RDWR | O_NONBLOCK);
ioctl(vfd, VIDIOC_QUERYCAP, &cap);

查询并要求 V4L2_CAP_VIDEO_CAPTURE_MPLANE 和 V4L2_CAP_STREAMING。

8.2 格式协商

fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
fmt.fmt.pix_mp.width = requested_w;
fmt.fmt.pix_mp.height = requested_h;
fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
ioctl(vfd, VIDIOC_S_FMT, &fmt);

后续以驱动返回的 width、height、bytesperline、quantization 为准。代码拒绝不符合单内存平面 NV12条件的返回值,避免误把 NV21/NM12 等格式按 NV12 解析。

8.3 framebuffer 查询与 mmap

ioctl(ffd, FBIOGET_FSCREENINFO, &fix);
ioctl(ffd, FBIOGET_VSCREENINFO, &var);
fb = mmap(NULL, fix.smem_len, PROT_READ | PROT_WRITE,
          MAP_SHARED, ffd, 0);

检查 var.bits_per_pixel == 16、RGB565 通道长度 5/6/5。fix.line_length 是实际每行字节数,不能一概假定等于 240*2。

8.4 V4L2 缓冲区

VIDIOC_REQBUFS (4 buffers)
  → VIDIOC_QUERYBUF
  → mmap
  → VIDIOC_QBUF
  → VIDIOC_STREAMON

主循环:

select()
  → VIDIOC_DQBUF
  → 从 NV12 采样、中心裁切、缩放、转换为 RGB565
  → 写 framebuffer
  → VIDIOC_QBUF

退出时:

VIDIOC_STREAMOFF → munmap V4L2 buffers → munmap fb → close

8.5 NV12 的内存布局

单内存平面 NV12:

Y plane:  stride × height
UV plane: stride × (height / 2)

每个像素一个 Y;每个 2×2 像素共享一对 U、V。

int Y = yp[y * stride + x];
int U = uv[(y/2) * stride + (x & ~1)] - 128;
int V = uv[(y/2) * stride + (x & ~1) + 1] - 128;

当前 nv12_pixel() 一次转换一个输出像素,返回一个 uint16_t RGB565;每帧 240×240 共调用 57600 次。它不是一次转换一整行,也不是直接发送 SPI。

源码按 V4L2 的 full-range / limited-range 标记选用不同 YUV→RGB 公式,再压缩为 RGB565。

8.6 裁切和缩放

对于请求 640×480,先从中间裁出 480×480,再用最近邻缩放到 240×240。这样不把 4:3 图像拉伸变形,但左右边缘会被裁掉。

int crop = width < height ? width : height;
int cropx = (width - crop) / 2;
int cropy = (height - crop) / 2;

for (unsigned y = 0; y < out_h; y++) {
    int sy = cropy + (uint64_t)y * crop / out_h;
    for (unsigned x = 0; x < out_w; x++) {
        int sx = cropx + (uint64_t)x * crop / out_w;
        uint16_t px = nv12_pixel(src, sx, sy, stride, height, full_range);
        row[x] = px; /* 简化表示:原代码会检查 RGB/BGR 位域 */
    }
}

row[x] = px 一次写一个 16-bit 像素;写满 240 个像素是一行(480 字节);240 行是一帧(115200 字节)。

8.7 当前版本的已知边界

  • 只支持单内存平面的 NV12,不能直接处理 NV21、UYVY、RGBP;
  • 最近邻缩放,优先低 CPU 开销而非最佳画质;
  • 无 RGA 硬件加速;
  • 直接写 framebuffer,不保证与 FBTFT 异步刷新同步,可能有撕裂;
  • 现有程序使用 full_range 判断,某些驱动的颜色范围标记可能不可靠;
  • 使用简单中心裁切,不处理圆屏四角之外的复杂 UI;
  • 缺少每个环节的单独耗时统计;
  • 源码假设 NV12 UV 起始地址为 stride * height,在当前典型布局成立;如果驱动存在额外平面对齐/偏移,需要按实际驱动布局修正。

9. Makefile 和交叉编译

参考用户指定的 Luckfox 官方 WebBee 交叉编译教程,针对 RV1103 Buildroot/uClibc,使用 SDK 中的 arm-rockchip830-linux-uclibcgnueabihf- 工具链。如果你的系统是 Ubuntu/glibc rootfs,则不能机械套用这一工具链。

本工程 Makefile 原文:

# Luckfox RV1103 Buildroot (uClibc) cross compilation
SDK_ROOT ?= $(HOME)/luckfox-pico
TOOLCHAIN_BIN ?= $(SDK_ROOT)/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin
CROSS_COMPILE ?= $(TOOLCHAIN_BIN)/arm-rockchip830-linux-uclibcgnueabihf-
CC := $(CROSS_COMPILE)gcc

TARGET := camera_fb
SRCS := camera_fb.c
CFLAGS := -O2 -std=gnu11 -Wall -Wextra
LDFLAGS :=
LDLIBS :=

.PHONY: all clean check-toolchain
all: $(TARGET)

check-toolchain:
	@test -x "$(CC)" || { echo "ERROR: compiler not found: $(CC)"; echo "Set SDK_ROOT or TOOLCHAIN_BIN to your SDK path"; exit 1; }

$(TARGET): $(SRCS) | check-toolchain
	$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS) $(LDLIBS)

clean:
	rm -f $(TARGET)

编译:

make SDK_ROOT=$HOME/luckfox-pico
file camera_fb

如工具链路径不同:

find ~/luckfox-pico -type f -name '*rockchip830*gcc'
make TOOLCHAIN_BIN=/actual/path/to/bin

上传:

scp camera_fb root@<开发板IP>:/oem/

板上执行:

chmod +x /oem/camera_fb
/oem/camera_fb /dev/video11 /dev/fb0 640 480

本记录中的 Makefile 路径是此前工程的默认路径,不是对用户 SDK 目录结构的现场验证。可按实际安装位置覆盖变量。

10. 图像偏暗问题及 RKAIQ

【现场现象】Camera→LCD 图像能显示,但明显偏暗。

可能原因有:自动曝光 AE 未运行、Sensor 初始曝光时间过短、增益偏低、IQ 文件与 Sensor 不匹配、颜色范围转换错误、LCD 本身亮度不足等。不能仅凭“偏暗”就认定 ISP 有问题。

10.1 启动 RKAIQ 3A 服务

先检查:

ps | grep -E 'rkaiq|rkisp|rkipc'
which rkaiq_3A_server
ls /oem/usr/share/iqfiles/

若服务存在且没有其他进程控制 ISP:

rkaiq_3A_server &
sleep 3
/oem/camera_fb /dev/video11 /dev/fb0 640 480

【现场确认】用户反馈“确实好了一些,凑合可以看了”。这表明启动 RKAIQ 3A 对画面亮度有正面作用,但不代表曝光/ISP 参数已经完全正确。

10.2 软件提亮(备选,未现场应用)

若临时需要更亮,可在 nv12_pixel() 读取 Y 后加入:

Y = Y * 3 / 2 + 10;
if (Y > 255) Y = 255;

这是建议性补丁,不包含在本工程最终 camera_fb.c 中。它会放大噪声、造成高光饱和,不能替代正确曝光。

10.3 建议后续检查

  • RKAIQ 启动日志是否识别正确 Sensor 和 IQ 文件;
  • AE 是否在运行,曝光时间/模拟增益是否随环境变化;
  • NV12 full/limited range 是否正确;
  • 摄像头直接抓取原始 NV12 后在 PC 上查看,与 LCD 画面对比;
  • 背光电压和模块供电是否正常。

11. 帧率与 SPI 带宽分析

【现场观察】预览约 20 FPS。当前已确认成功的 SPI 频率是 20 MHz。

240×240 RGB565 的纯像素数据:

240 × 240 × 2 = 115200 byte/frame
240 × 240 × 16 = 921600 bit/frame

在 20 MHz SPI 下,理论纯传输时间:

921600 / 20000000 = 0.04608 s = 46.08 ms

理论上限:

1 / 0.04608 ≈ 21.70 FPS
SPI SCLK理论整屏数据传输时间理论上限 FPS
10 MHz92.16 ms10.85
20 MHz46.08 ms21.70
30 MHz30.72 ms32.55
40 MHz23.04 ms43.40
50 MHz18.43 ms54.25

这是理想的单线 SPI 8-bit 传输模型,未包含命令开销、CS 间隙、内核调度、刷新策略、摄像头采集/CPU 转换和驱动额外开销。表中数字不是实测。

重要纠正:camera_fb.c 打印的 preview FPS 是采集/处理循环速率;即使它约 20 FPS,也不能单独证明 LCD 的实际物理刷新就是 20 FPS。需要在 FBTFT 刷新函数或 SPI 逻辑分析仪处测量。

11.1 SPI 提速测试计划

建议按 20→30→40→50 MHz 逐级测试,每一级记录:

  • LCD 是否花屏、颜色错误、间歇黑屏;
  • camera_fb.c 的 preview FPS;
  • 内核 FBTFT 的实际刷新次数/耗时;
  • SPI0 SCLK 实测频率;
  • 连续运行 10~30 分钟稳定性。

11.2 fps DTS 参数的影响

此前日志有 fps=33,DTS 片段示例中是 fps=<30>。这些值可能限制 FBTFT 刷新调度;要注意它们不是摄像头帧率,也不是实际 SPI 传输能力。SPI 提速后如果 FPS 不变,检查 FBTFT deferred I/O 周期及是否真正刷新了每帧。

12. 常见故障及排查顺序

症状优先检查判断方法
没有 /dev/fb0驱动是否启用、SPI0 设备绑定、compatible、spidev 冲突dmesg、readlink .../driver
/dev/fb0 有但屏幕不亮背光 GPIO、RESET/DC 极性、SPI pinmux、初始化表GPIO 电平、SCLK 波形
显示纯色正常但相机无图V4L2 节点、格式、media pipeline、STREAMONv4l2-ctl
图像颜色异常MADCTL BGR、FB RGB bitfields、NV12 色彩范围红/绿/蓝测试图
图像旋转/镜像DTS rotate、MADCTL、Sensor 方向图案方向对照
图像过暗RKAIQ 3A、Sensor 曝光、IQ 文件、YUV range启动 3A 前后对比
FPS 接近 20SPI 20 MHz 带宽、FBTFT 刷新周期SCLK、内核刷新计数
提高 SPI 后花屏LCD 模块频率上限、线长、时序、电源完整性逐级降频复测
编译器找不到SDK_ROOT/TOOLCHAIN_BIN 不匹配find ... '*rockchip830*gcc'
可执行文件无法运行libc/ABI/动态链接器不匹配file、readelf -l

13. 推荐的正式产品化方向

当前验证方案已经完成目标。后续如产品化:

  1. 单独建立 fb_gc9a01.c 驱动,使用真实 compatible,不再冒用 ST7789;
  2. 用标准 backlight 子系统实现亮度控制、休眠/唤醒;
  3. 优化 FBTFT SPI 刷新与 DMA,测真实 LCD 刷新 FPS;
  4. 调整 RKAIQ AE/AWB/IQ 参数,解决暗画面;
  5. 对比 /dev/video12 直接 RGB565 输出与 /dev/video11 NV12 转换;
  6. 如果 SoC/SDK 的 RGA 能稳定使用,再考虑硬件缩放和色彩转换;
  7. 加入摄像头断流恢复、SPI 异常处理、进程守护、开机启动;
  8. 在显示预览稳定后再引入 LVGL 叠加文字/图形。

14. 文件清单及用途

RV1103_GC9A01_Camera_Debug/
├── DEBUG_REPORT.md                        # 本完整调试报告
├── fb_st7789v.c                           # 最终 GC9A01 + 背光完整驱动
├── fb_st7789v_gc9a01_without_backlight.c  # 背光修改前的 GC9A01 版本
├── 0002-add-dts-backlight-control.patch   # 两版 GC9A01 驱动的真实 diff
├── gc9a01-spi0-overlay-example.dts        # 板级 DTS 示例片段
├── camera_fb.c                            # 最终用户态 Camera→FB 程序
└── Makefile                               # Buildroot/uClibc 交叉编译

复现建议顺序

  1. 核对 GPIO 接线、供电、SPI0 pinmux;
  2. 合并 DTS、禁用冲突 spidev;
  3. 用完整 fb_st7789v.c 替换 SDK 原驱动,重新编译内核;
  4. 确认 /dev/fb0、240×240、纯色正常;
  5. 枚举 /dev/video11、确认 NV12;
  6. 使用 Makefile 交叉编译 camera_fb.c;
  7. 运行 camera_fb 并确认实时画面;
  8. 启动 RKAIQ 3A 改善曝光;
  9. 记录实际 FPS,最后逐级测试 SPI 提速。

15. 本次现场结果与待完成项

已完成并现场确认:

  • FBTFT 成功绑定 SPI0;
  • GC9A01 红色画面显示成功;
  • DTS 背光 GPIO 与驱动打开背光方案有效;
  • /dev/video11 提供 NV12,camera_fb.c 实时预览成功;
  • 约 20 FPS 的用户侧观测;
  • RKAIQ 3A 启动后画面变亮。

尚未现场确认:

  • SPI 30/40/50 MHz 稳定性与真实 SCLK;
  • GC9A01 实际刷新帧率及刷新函数耗时;
  • RKAIQ 完整曝光参数及 Sensor IQ 匹配情况;
  • /dev/video12 直接 RGB565 的性能;
  • 长时间运行、休眠/唤醒、开机自动启动。

结论:项目已成功实现 RV1103 的 MIPI Camera → RKISP → V4L2 → 用户态 NV12/RGB565 转换 → Linux framebuffer → SPI0 → GC9A01 的实时显示链路。当前采用 FBTFT + 用户态 CPU 转换的简单方案,足以作为后续 LVGL、图像处理和 SPI 显示性能优化的基础。

diff --git a/sysdrv/source/kernel/arch/arm/boot/dts/rv1103-luckfox-pico-ipc.dtsi b/sysdrv/source/kernel/arch/arm/boot/dts/rv1103-luckfox-pico-ipc.dtsi
index d7b50e115..38ab19fc4 100755
--- a/sysdrv/source/kernel/arch/arm/boot/dts/rv1103-luckfox-pico-ipc.dtsi
+++ b/sysdrv/source/kernel/arch/arm/boot/dts/rv1103-luckfox-pico-ipc.dtsi
@@ -316,21 +316,24 @@ &spi0 {
        #address-cells = <1>;
        #size-cells = <0>;

-       spidev@0 {
-               compatible = "rockchip,spidev";
-               spi-max-frequency = <50000000>;
-               reg = <0>;
-       };
        fbtft@0 {
                compatible = "sitronix,st7789v";
                reg = <0>;
-               spi-max-frequency = <20000000>;
+               spi-max-frequency = <60000000>;
                fps = <30>;
                buswidth = <8>;
                debug = <0x7>;
-               led-gpios = <&gpio0 RK_PA4 GPIO_ACTIVE_HIGH>;//BL
-               dc-gpios = <&gpio1 RK_PA2 GPIO_ACTIVE_HIGH>;//DC
-               reset-gpios = <&gpio1 RK_PC3 GPIO_ACTIVE_LOW>;//RES
+
+               width = <240>;
+               height = <240>;
+
+               rotate = <0>;
+               bgr = <0>;
+        reset-gpios = <&gpio1 RK_PC4 GPIO_ACTIVE_LOW>;
+        dc-gpios = <&gpio1 RK_PC5 GPIO_ACTIVE_HIGH>;
+
+        /* GPIO1_D0:背光,高电平有效 */
+        backlight-gpios = <&gpio1 RK_PD0 GPIO_ACTIVE_HIGH>;
        };
 };
 // I2C
diff --git a/sysdrv/source/kernel/arch/arm/boot/dts/rv1103g-luckfox-pico-mini.dts b/sysdrv/source/kernel/arch/arm/boot/dts/rv1103g-luckfox-pico-mini.dts
index 85ce5b81d..4043196e1 100755
--- a/sysdrv/source/kernel/arch/arm/boot/dts/rv1103g-luckfox-pico-mini.dts
+++ b/sysdrv/source/kernel/arch/arm/boot/dts/rv1103g-luckfox-pico-mini.dts
@@ -60,10 +60,7 @@ &usbdrd_dwc3 {
 /**********SPI**********/
 /* SPI0_M0 */
 &spi0 {
-       status = "disabled";
-       spidev@0 {
-               spi-max-frequency = <50000000>;
-       };
+       status = "okay";
 };

 /**********I2C**********/
diff --git a/sysdrv/source/kernel/arch/arm/configs/luckfox_rv1106_linux_defconfig b/sysdrv/source/kernel/arch/arm/configs/luckfox_rv1106_linux_defconfig
index 203ce2997..e7b1f10a2 100755
--- a/sysdrv/source/kernel/arch/arm/configs/luckfox_rv1106_linux_defconfig
+++ b/sysdrv/source/kernel/arch/arm/configs/luckfox_rv1106_linux_defconfig
@@ -6,7 +6,6 @@ CONFIG_NO_HZ=y
 CONFIG_HIGH_RES_TIMERS=y
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
-CONFIG_LOG_BUF_SHIFT=14
 CONFIG_CGROUPS=y
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_CC_OPTIMIZE_FOR_SIZE=y
diff --git a/sysdrv/source/kernel/drivers/staging/fbtft/fb_st7789v.c b/sysdrv/source/kernel/drivers/staging/fbtft/fb_st7789v.c
index 0a2dbed9f..19795b9f2 100644
--- a/sysdrv/source/kernel/drivers/staging/fbtft/fb_st7789v.c
+++ b/sysdrv/source/kernel/drivers/staging/fbtft/fb_st7789v.c
@@ -1,270 +1,141 @@
-// SPDX-License-Identifier: GPL-2.0+
 /*
- * FB driver for the ST7789V LCD Controller
- *
- * Copyright (C) 2015 Dennis Menschel
+ * GC9A01 test implementation inside the original fb_st7789v driver.
+ * Deliberately keep the driver name and OF compatible unchanged.
+ * Initialization register values ported from the user's working ESP32 GC9A01 sequence.
  */
-
-#include <linux/bitops.h>
+#include <linux/errno.h>
 #include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/err.h>
 #include <video/mipi_display.h>
-
 #include "fbtft.h"

 #define DRVNAME "fb_st7789v"
+#define MADCTL_BGR BIT(3)
+#define MADCTL_MV  BIT(5)
+#define MADCTL_MX  BIT(6)
+#define MADCTL_MY  BIT(7)

-#define DEFAULT_GAMMA \
-       "70 2C 2E 15 10 09 48 33 53 0B 19 18 20 25\n" \
-       "70 2C 2E 15 10 09 48 33 53 0B 19 18 20 25"
-
-#define HSD20_IPS_GAMMA \
-       "D0 05 0A 09 08 05 2E 44 45 0F 17 16 2B 33\n" \
-       "D0 05 0A 09 08 05 2E 43 45 0F 16 16 2B 33"
-
-#define HSD20_IPS 1
-
-/**
- * enum st7789v_command - ST7789V display controller commands
- *
- * @PORCTRL: porch setting
- * @GCTRL: gate control
- * @VCOMS: VCOM setting
- * @VDVVRHEN: VDV and VRH command enable
- * @VRHS: VRH set
- * @VDVS: VDV set
- * @VCMOFSET: VCOM offset set
- * @PWCTRL1: power control 1
- * @PVGAMCTRL: positive voltage gamma control
- * @NVGAMCTRL: negative voltage gamma control
- *
- * The command names are the same as those found in the datasheet to ease
- * looking up their semantics and usage.
- *
- * Note that the ST7789V display controller offers quite a few more commands
- * which have been omitted from this list as they are not used at the moment.
- * Furthermore, commands that are compliant with the MIPI DCS have been left
- * out as well to avoid duplicate entries.
- */
-enum st7789v_command {
-       PORCTRL = 0xB2,
-       GCTRL = 0xB7,
-       VCOMS = 0xBB,
-       VDVVRHEN = 0xC2,
-       VRHS = 0xC3,
-       VDVS = 0xC4,
-       VCMOFSET = 0xC5,
-       PWCTRL1 = 0xD0,
-       PVGAMCTRL = 0xE0,
-       NVGAMCTRL = 0xE1,
-};
-
-#define MADCTL_BGR BIT(3) /* bitmask for RGB/BGR order */
-#define MADCTL_MV BIT(5) /* bitmask for page/column order */
-#define MADCTL_MX BIT(6) /* bitmask for column address order */
-#define MADCTL_MY BIT(7) /* bitmask for page address order */
-
-/**
- * init_display() - initialize the display controller
- *
- * @par: FBTFT parameter object
- *
- * Most of the commands in this init function set their parameters to the
- * same default values which are already in place after the display has been
- * powered up. (The main exception to this rule is the pixel format which
- * would default to 18 instead of 16 bit per pixel.)
- * Nonetheless, this sequence can be used as a template for concrete
- * displays which usually need some adjustments.
- *
- * Return: 0 on success, < 0 if error occurred.
- */
static int init_display(struct fbtft_par *par)
 {
-       par->fbtftops.reset(par);
-
-       /* turn off sleep mode */
-       write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
-       mdelay(120);
-
-       /* set pixel format to RGB-565 */
-       write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT);
-       if (HSD20_IPS)
-               write_reg(par, PORCTRL, 0x05, 0x05, 0x00, 0x33, 0x33);
-
-       else
-               write_reg(par, PORCTRL, 0x08, 0x08, 0x00, 0x22, 0x22);
-
-       /*
-        * VGH = 13.26V
-        * VGL = -10.43V
-        */
-       if (HSD20_IPS)
-               write_reg(par, GCTRL, 0x75);
-       else
-               write_reg(par, GCTRL, 0x35);
-
-       /*
-        * VDV and VRH register values come from command write
-        * (instead of NVM)
-        */
-       write_reg(par, VDVVRHEN, 0x01, 0xFF);
-
-       /*
-        * VAP =  4.1V + (VCOM + VCOM offset + 0.5 * VDV)
-        * VAN = -4.1V + (VCOM + VCOM offset + 0.5 * VDV)
-        */
-       if (HSD20_IPS)
-               write_reg(par, VRHS, 0x13);
-       else
-               write_reg(par, VRHS, 0x0B);
-
-       /* VDV = 0V */
-       write_reg(par, VDVS, 0x20);
-
-       /* VCOM = 0.9V */
-       if (HSD20_IPS)
-               write_reg(par, VCOMS, 0x22);
-       else
-               write_reg(par, VCOMS, 0x20);
-
-       /* VCOM offset = 0V */
-       write_reg(par, VCMOFSET, 0x20);
-
-       /*
-        * AVDD = 6.8V
-        * AVCL = -4.8V
-        * VDS = 2.3V
-        */
-       write_reg(par, PWCTRL1, 0xA4, 0xA1);
-
-       write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
-
-       if (HSD20_IPS)
-               write_reg(par, MIPI_DCS_ENTER_INVERT_MODE);
-
-       return 0;
+        struct gpio_desc *backlight;
+
+        /* DTS: backlight-gpios = <&gpio1 RK_PD0 GPIO_ACTIVE_HIGH>; */
+        backlight = devm_gpiod_get_optional(&par->spi->dev,
+                                           "backlight", GPIOD_OUT_LOW);
+        if (IS_ERR(backlight)) {
+                dev_err(&par->spi->dev,
+                        "failed to request backlight GPIO: %ld\n",
+                        PTR_ERR(backlight));
+                return PTR_ERR(backlight);
+        }
+
+        par->fbtftops.reset(par);
+        mdelay(150);
+
+        write_reg(par, 0xEF);
+        write_reg(par, 0xEB, 0x14);
+        write_reg(par, 0xFE);
+        write_reg(par, 0xEF);
+        write_reg(par, 0xEB, 0x14);
+        write_reg(par, 0x84, 0x40);
+        write_reg(par, 0x85, 0xFF);
+        write_reg(par, 0x86, 0xFF);
+        write_reg(par, 0x87, 0xFF);
+        write_reg(par, 0x88, 0x0A);
+        write_reg(par, 0x89, 0x21);
+        write_reg(par, 0x8A, 0x00);
+        write_reg(par, 0x8B, 0x80);
+        write_reg(par, 0x8C, 0x01);
+        write_reg(par, 0x8D, 0x01);
+        write_reg(par, 0x8E, 0xFF);
+        write_reg(par, 0x8F, 0xFF);
+        write_reg(par, 0xB6, 0x00, 0x20);
+        write_reg(par, 0x3A, 0x05);
+        write_reg(par, 0x90, 0x08, 0x08, 0x08, 0x08);
+        write_reg(par, 0xBD, 0x06);
+        write_reg(par, 0xBC, 0x00);
+        write_reg(par, 0xFF, 0x60, 0x01, 0x04);
+        write_reg(par, 0xC3, 0x13);
+        write_reg(par, 0xC4, 0x13);
+        write_reg(par, 0xC9, 0x22);
+        write_reg(par, 0xBE, 0x11);
+        write_reg(par, 0xE1, 0x10, 0x0E);
+        write_reg(par, 0xDF, 0x21, 0x0C, 0x02);
+        write_reg(par, 0xED, 0x1B, 0x0B);
+        write_reg(par, 0xAE, 0x77, 0x00, 0x00);
+        write_reg(par, 0xCD, 0x63);
+        write_reg(par, 0xE8, 0x34, 0x02, 0x00);
+        write_reg(par, 0x62, 0x18, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x18, 0x0F, 0x71, 0xEF, 0x70, 0x70);
+        write_reg(par, 0x63, 0x18, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x18, 0x13, 0x71, 0xF3, 0x70, 0x70);
+        write_reg(par, 0x64, 0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00);
+        write_reg(par, 0x66, 0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00);
+        write_reg(par, 0x67, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98);
+        write_reg(par, 0x74, 0x10, 0x85, 0x80, 0x00, 0x00, 0x4E, 0x00);
+        write_reg(par, 0x98, 0x3E, 0x07);
+        write_reg(par, 0x35, 0x00);
+        write_reg(par, 0x36, 0x08);
+        write_reg(par, 0x3A, 0x05);
+        write_reg(par, 0x21);
+        write_reg(par, 0x11);
+        mdelay(120);
+        write_reg(par, 0x29); /* Display ON */
+        mdelay(20);
+
+        /* Enable panel backlight after GC9A01 is initialized. */
+        if (backlight) {
+                gpiod_set_value_cansleep(backlight, 1);
+                dev_info(&par->spi->dev, "GC9A01 backlight enabled\n");
+        } else {
+                dev_warn(&par->spi->dev,
+                         "no backlight-gpios in DT; skip backlight control\n");
+        }
+        return 0;
 }

-/**
- * set_var() - apply LCD properties like rotation and BGR mode
- *
- * @par: FBTFT parameter object
- *
- * Return: 0 on success, < 0 if error occurred.
- */
 static int set_var(struct fbtft_par *par)
 {
-       u8 madctl_par = 0;
-
-       if (par->bgr)
-               madctl_par |= MADCTL_BGR;
-       switch (par->info->var.rotate) {
-       case 0:
-               break;
-       case 90:
-               madctl_par |= (MADCTL_MV | MADCTL_MY);
-               break;
-       case 180:
-               madctl_par |= (MADCTL_MX | MADCTL_MY);
-               break;
-       case 270:
-               madctl_par |= (MADCTL_MV | MADCTL_MX);
-               break;
-       default:
-               return -EINVAL;
-       }
-       write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, madctl_par);
-       return 0;
-}
-
-/**
- * set_gamma() - set gamma curves
- *
- * @par: FBTFT parameter object
- * @curves: gamma curves
- *
- * Before the gamma curves are applied, they are preprocessed with a bitmask
- * to ensure syntactically correct input for the display controller.
- * This implies that the curves input parameter might be changed by this
- * function and that illegal gamma values are auto-corrected and not
- * reported as errors.
- *
- * Return: 0 on success, < 0 if error occurred.
- */
-static int set_gamma(struct fbtft_par *par, u32 *curves)
-{
-       int i;
-       int j;
-       int c; /* curve index offset */
-
-       /*
-        * Bitmasks for gamma curve command parameters.
-        * The masks are the same for both positive and negative voltage
-        * gamma curves.
-        */
-       static const u8 gamma_par_mask[] = {
-               0xFF, /* V63[3:0], V0[3:0]*/
-               0x3F, /* V1[5:0] */
-               0x3F, /* V2[5:0] */
-               0x1F, /* V4[4:0] */
-               0x1F, /* V6[4:0] */
-               0x3F, /* J0[1:0], V13[3:0] */
-               0x7F, /* V20[6:0] */
-               0x77, /* V36[2:0], V27[2:0] */
-               0x7F, /* V43[6:0] */
-               0x3F, /* J1[1:0], V50[3:0] */
-               0x1F, /* V57[4:0] */
-               0x1F, /* V59[4:0] */
-               0x3F, /* V61[5:0] */
-               0x3F, /* V62[5:0] */
-       };
-
-       for (i = 0; i < par->gamma.num_curves; i++) {
-               c = i * par->gamma.num_values;
-               for (j = 0; j < par->gamma.num_values; j++)
-                       curves[c + j] &= gamma_par_mask[j];
-               write_reg(par, PVGAMCTRL + i,
-                         curves[c + 0],  curves[c + 1],  curves[c + 2],
-                         curves[c + 3],  curves[c + 4],  curves[c + 5],
-                         curves[c + 6],  curves[c + 7],  curves[c + 8],
-                         curves[c + 9],  curves[c + 10], curves[c + 11],
-                         curves[c + 12], curves[c + 13]);
-       }
-       return 0;
+        u8 madctl = 0;
+
+        if (par->bgr)
+                madctl |= MADCTL_BGR;
+
+        switch (par->info->var.rotate) {
+        case 0:
+                break;
+        case 90:
+                madctl |= MADCTL_MV | MADCTL_MY;
+                break;
+        case 180:
+                madctl |= MADCTL_MX | MADCTL_MY;
+                break;
+        case 270:
+                madctl |= MADCTL_MV | MADCTL_MX;
+                break;
+        default:
+                return -EINVAL;
+        }
+
+        write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, madctl);
+        return 0;
 }

-/**
- * blank() - blank the display
- *
- * @par: FBTFT parameter object
- * @on: whether to enable or disable blanking the display
- *
- * Return: 0 on success, < 0 if error occurred.
- */
 static int blank(struct fbtft_par *par, bool on)
 {
-       if (on)
-               write_reg(par, MIPI_DCS_SET_DISPLAY_OFF);
-       else
-               write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
-       return 0;
+        write_reg(par, on ? MIPI_DCS_SET_DISPLAY_OFF :
+                             MIPI_DCS_SET_DISPLAY_ON);
+        return 0;
 }

 static struct fbtft_display display = {
-       .regwidth = 8,
-       .width = 240,
-       .height = 320,
-       .gamma_num = 2,
-       .gamma_len = 14,
-       .gamma = HSD20_IPS_GAMMA,
-       .fbtftops = {
-               .init_display = init_display,
-               .set_var = set_var,
-               .set_gamma = set_gamma,
-               .blank = blank,
-       },
+        .regwidth = 8,
+        .width = 240,
+        .height = 240,
+        .fbtftops = {
+                .init_display = init_display,
+                .set_var = set_var,
+                .blank = blank,
+        },
 };

 FBTFT_REGISTER_DRIVER(DRVNAME, "sitronix,st7789v", &display);
@@ -274,6 +145,6 @@ MODULE_ALIAS("platform:" DRVNAME);
 MODULE_ALIAS("spi:st7789v");
 MODULE_ALIAS("platform:st7789v");

-MODULE_DESCRIPTION("FB driver for the ST7789V LCD Controller");
+MODULE_DESCRIPTION("FB driver for ST7789V (GC9A01 init test)");
 MODULE_AUTHOR("Dennis Menschel");
 MODULE_LICENSE("GPL");

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐