ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ST7701S驱动实战:从MIPI DSI链路到Linux DRM移植

ST7701S驱动实战:从MIPI DSI链路到Linux DRM移植 简介ST7701S是广泛应用于嵌入式设备的LCD显示控制器这份资源提供基于Linux平台的C/C驱动实现适合正在将ST7701S屏幕接入自研硬件、需要快速完成显示功能验证的嵌入式开发工程师。驱动覆盖初始化、数据传输、屏幕控制与电源管理几大模块初始化负责设定分辨率和时序参数数据链路支持SPI或I2C屏幕控制包含旋转、翻转及亮度调节为不同产品形态提供基本支持。压缩包共6个文件包括3份PDF文档、2个C语言驱动源文件和1个DB格式附件整体大小5.3MBPDF可用于查阅ST7701S规格与应用说明C源码涵盖MTK、展锐平台适配内容便于对照不同SoC的移植差异。目前已有2174人学习下载说明该驱动方案受到较多同类项目开发者关注。结合实例读者可以了解设备树节点配置、帧缓冲子系统注册以及交叉编译和基本显示测试的完整思路从而在自己的Linux环境中复现驱动加载流程并修改适配有效缩短嵌入式GUI开发中显示驱动的调试时间。1. ST7701S 驱动的本质把一段初始化序列按时序送进屏幕接手一块用 ST7701S 的 MIPI DSI 屏最常见的开局是上电白屏、背光亮、但没有图像。芯片手册翻下来三百多页寄存器多到怀疑人生可真正让屏点亮的往往不是学会每个寄存器而是读懂屏厂给的初始化序列、用 C 把这段序列在正确的时序窗口里送进 DSI 链路。ST7701 驱动说穿了就是把“初始化脚本 display mode 上下电时序”做成一份能在目标平台上跑的 C/C 代码。这篇文章面向两类人一类是在裸机上第一次写 MIPI 屏驱动的嵌入式工程师一类是在 Linux 内核里移植 DRM panel 驱动的系统工程师。它会告诉你两条路分别怎么走、参数怎么定、翻车在哪。2. 从屏幕手册到 C 代码ST7701S 的寄存器体系与驱动模型选择2.1 为什么说 ST7701S 的驱动难点不在芯片而在 DSI 链路ST7701S 是一颗典型的 MIPI DSI 接口 TFT-LCD 控制器常见分辨率为 480x854、720x1280 这类中小尺寸内部集成 GRAM、源极驱动和一部分 GIP 电路。它的工作模式是主控通过 MIPI DSI 接口发送 DCS 命令和数据ST7701S 把接收到的时序转换成面板行扫描信号。芯片端确实有大量寄存器但屏厂一般会给一份初始化序列驱动开发真正要处理的不是理解每个 bit而是把这串序列可靠地送过去。我见过不少刚接触 MIPI 屏的工程师拿到驱动第一件事就是翻 datasheet 研究寄存器含义结果卡了两天还在白屏。原因很简单ST7701S 是接收端它能不能工作取决于主控侧的 DSI 物理层是否起来了。lane 数、差分时钟频率、LP/HS 模式切换、DCS 命令包格式哪一项没配对初始化序列写得再对也进不到屏里。所以做 ST7701 驱动第一步永远是确认 DSI 链路通第二步才是写 C 代码发初始化序列。这件事的常见做法是先用示波器或逻辑分析仪抓 D0P/D0N、CLKP/CLKN。如果复位拉高、供电稳定之后CLKP 上能看到稳定的差分时钟D0 上有短暂的 HS burst说明链路已经在跑。如果时钟线纹丝不动问题在主控的 DSI controller 配置不在屏的驱动代码。把这一步作为调试习惯能省掉后面至少一半的排查时间。2.2 裸机 C 与 Linux DRM panel 驱动两条路线怎么选ST7701S 驱动在工程上一般会分成两条路线选择取决于目标平台有没有操作系统。第一条是裸机 C 路线。典型场景是 MCU DSI 屏或者跑 RTOS 的穿戴设备。代码里没有 DRM 框架也没有设备树你需要自己封装一个底层发送函数比如mipi_dsi_write(cmd, data, len)然后把初始化序列放在一个只读数组里上电后按顺序发送。这类代码最大的优点是透明整个流程都是你控制的从哪里开始、延时多久、发什么命令逻辑清晰。缺点是底层硬件相关代码要自己写不同主控的 DSI 外设寄存器差别很大移植成本不低。第二条是 Linux DRM panel 路线。常见于跑 Linux 的产品比如 RK 平台、全志平台或高通的方案。驱动会写成一个 platform driver 或 mipi_dsi_driver挂到 DRM panel 框架下。内核帮你管理 power domain、时钟、reset GPIO还会在合适的时机调用prepare、enable、disable、unprepare回调。这个路线的优点是不用自己处理复杂的系统电源管理而且 DRM 框架自带调试手段比如 modetest、dmesg。缺点是调试链路变长错误可能发生在 panel 驱动之外。两条路线都绕不开 C/C 这个前提。裸机代码用 C 写Linux 驱动也是 C只有在做测试工具或上位机仿真时才会用 C 包一层。我自己的判断标准很简单跑 Linux 就写 DRM driver不要试图绕过内核做裸机初始化跑 MCU 就老老实实写 C 循环发送别搬 Linux 那套框架。混搭往往是翻车的开始。2.3 初始化序列的构成解锁、GIP、Gamma 三层结构屏厂提供的 ST7701S 初始化序列看起来是一串十六进制字节但拆开看通常有三个层次。第一层是解锁命令。ST7701S 有寄存器保护机制直接写常规寄存器是无效的得先通过特定命令把写入权限打开。常见做法是发一串以0xFF开头的厂商命令把 IC 从保护状态切到可写状态。这个序列不同屏厂略有差别必须原样使用少一个字节都不行。第二层是显示模式与 GIP 时序配置。这层设置源极驱动顺序、扫描方向、VCOM、泵压、GD 信号等。GIPGate in Panel相关命令尤其关键因为大多数穿戴屏把 gate 驱动做在面板玻璃上ST7701S 只负责输出控制信号。如果这组参数和面板的玻璃走线不匹配会出现亮一半、暗一半、或者休眠后唤醒异常的怪现象。第三层是 Gamma 和背光相关。Gamma 命令决定灰阶曲线错一点就是偏色背光命令则和主控的 backlight 控制方式相关。写 C 代码时我不建议把所有命令压成一个巨型数组一次性发完而是按层拆分并加注释。这样屏幕出问题时可以快速通过二分法定位是解锁失败、GIP 配置错误还是 Gamma 曲线不对。这算是整个 ST7701S 驱动里性价比最高的代码习惯。3. 用 C 写一个 ST7701S 最小驱动可复现的初始化序列代码3.1 裸机 C 版把初始化序列封装成表驱动结构先看裸机 C 的实现。ST7701S 接到主控上通常走 1 条或 2 条 laneDCS 命令包一般由命令字节加参数组成。发送函数可以抽象成下面这样#include stdint.h /* MIPI DSI DCS command: 0x11 sleep out, 0x29 display on */ #define DCS_SLEEP_OUT 0x11 #define DCS_DISPLAY_ON 0x29 /* 底层发送接口由具体平台实现 */ extern int mipi_dsi_write(uint8_t cmd, const uint8_t *data, uint32_t len); extern void mdelay(uint32_t ms); /* 初始化命令表cmd data len delay */ struct st7701s_init_cmd { uint8_t cmd; const uint8_t *data; uint8_t len; uint32_t delay_ms; /* 发送后的延时 */ }; /* 示例片段实际字节必须以屏厂提供的序列为准 */ static const uint8_t st7701s_unlock[] { 0x77, 0x01, 0x00, 0x00, 0x13, 0x10 }; static const uint8_t st7701s_pwd[] { 0x63, 0x00 }; static const struct st7701s_init_cmd st7701s_init[] { {0xFF, st7701s_unlock, sizeof(st7701s_unlock), 10}, {DCS_SLEEP_OUT, NULL, 0, 120}, {0xBD, st7701s_pwd, sizeof(st7701s_pwd), 0}, /* 中间需要填入GIP、gamma等完整序列 */ }; void st7701s_init_panel(void) { for (uint32_t i 0; i sizeof(st7701s_init) / sizeof(st7701s_init[0]); i) { const struct st7701s_init_cmd *p st7701s_init[i]; mipi_dsi_write(p-cmd, p-data, p-len); if (p-delay_ms 0) mdelay(p-delay_ms); } mipi_dsi_write(DCS_DISPLAY_ON, NULL, 0); }这段代码的逻辑很直白用表驱动方式把命令、参数、延时绑在一起上电后逐个发送。mipi_dsi_write是平台相关函数在 STM32、全志、瑞萨等不同主控上实现方式不同但接口保持一致后续换平台不用改上层逻辑。这里有两个参数要注意。第一个是0xFF解锁命令后面的延时一般要给 5~20ms让 IC 内部状态稳定不要紧跟后续命令。第二个是DCS_SLEEP_OUT后的 120msST7701S 从 sleep 状态切到 normal 状态需要一定时间这个延时给短了后续命令会丢。表驱动结构里放delay_ms字段就是方便调试时针对某一条命令单独加长而不用改代码顺序。3.2 Linux DRM panel 驱动把初始化序列挂进 probe 流程跑 Linux 时驱动要写成 mipi_dsi_driver。下面是一个最小化的 ST7701S panel 驱动骨架重点在 probe 时配置 DSI 参数、注册 panel并在 prepare 回调里发送初始化序列。#include linux/delay.h #include linux/module.h #include linux/mipi_dsi.h #include linux/of_device.h #include drm/drm_panel.h struct st7701s_panel { struct drm_panel panel; struct mipi_dsi_device *dsi; }; static const struct drm_display_mode st7701s_mode { .clock 27000, /* pixel clock, 单位 kHz实际以屏参为准 */ .hdisplay 480, .hsync_start 480 38, .hsync_end 480 38 12, .htotal 480 38 12 20, .vdisplay 854, .vsync_start 854 20, .vsync_end 854 20 4, .vtotal 854 20 4 12, .type DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, }; static int st7701s_prepare(struct drm_panel *panel) { struct st7701s_panel *ctx container_of(panel, struct st7701s_panel, panel); /* 发送初始化序列可复用裸机版本 */ mipi_dsi_dcs_write_buffer(ctx-dsi, st7701s_unlock_seq, sizeof(st7701s_unlock_seq)); mipi_dsi_dcs_set_sleep_out(ctx-dsi); msleep(120); return 0; } static int st7701s_probe(struct mipi_dsi_device *dsi) { struct st7701s_panel *ctx; ctx devm_kzalloc(dsi-dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; ctx-dsi dsi; dsi-lanes 2; dsi-format MIPI_DSI_FMT_RGB666; dsi-mode_flags MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST; drm_panel_init(ctx-panel, dsi-dev, st7701s_panel_funcs, DRM_MODE_CONNECTOR_DSI); drm_panel_add(ctx-panel); return mipi_dsi_attach(dsi); } static const struct mipi_dsi_device_id st7701s_id[] { { st7701s, 0 }, { } }; MODULE_DEVICE_TABLE(mipi_dsi, st7701s_id); static struct mipi_dsi_driver st7701s_driver { .driver { .name panel-st7701s, }, .probe st7701s_probe, .id_table st7701s_id, }; module_mipi_dsi_driver(st7701s_driver);这段代码里dsi-lanes、dsi-format、dsi-mode_flags必须在mipi_dsi_attach之前设置。lanes写错会导致物理层训练失败format写错会导致颜色错乱。mode_flags用MIPI_DSI_MODE_VIDEO_BURST表示视频突发模式这是大多数 ST7701S 穿戴屏推荐的方式带宽利用率高对时钟抖动容忍度也更好。另外注意drm_panel_init传入的连接器类型是DRM_MODE_CONNECTOR_DSI不要写成DRM_MODE_CONNECTOR_eDP或其它类型。prepare回调只负责发送初始化序列不开背光背光通常在enable回调里打开。这个顺序是 DRM panel 框架约定好的破坏它会出现“先亮背光后出图”的闪烁问题。3.3 代码里的三个关键参数reset 时序、DSI 模式与 clock 频率写 ST7701S 驱动时有三组参数最容易踩坑。第一组是 reset GPIO 时序。ST7701S 的 reset 引脚通常是低有效上电后要经历“拉低至少 10ms → 拉高 → 延时 50~120ms → 发送初始化序列”。有些平台把 reset 引脚接到 SoC 的 GPIO驱动里要用devm_gpiod_get获取并按GPIO_ACTIVE_LOW标记。如果 reset 拉低时间不够IC 可能没有完全复位后续命令部分生效表现出来的现象就是屏幕偶尔能亮、偶尔白屏。第二组是 DSI 模式选择。ST7701S 支持命令模式和视频模式穿戴屏大多用视频模式。在视频模式下主控要持续向屏推数据所以MIPI_DSI_MODE_VIDEO必须带上。如果不小心配成命令模式主控只在刷新时才发数据屏会出现刷新率降低、闪烁甚至长期显示残影。MIPI_DSI_MODE_VIDEO_BURST适合带宽紧张的产品但要求主控的 DSI controller 支持 burst 模式老平台可能只支持 sync event。第三组是关键的计算参数像素时钟和 lane 速率。像素时钟可以从屏厂给的参数里直接取或者自己算pixel_clock htotal * vtotal * fps。以 480x85460 为例如果 htotal 是 540、vtotal 是 890那么像素时钟约 28.8MHz。DSI 每条 lane 的比特率大约是pixel_clock * bpp / lanesRGB666 按 18 或 24bit 算具体看主控的打包方式。这个值会在 devicetree 或驱动里直接配置配太高会导致 EMI 问题配太低会导致带宽不足、花屏。4. 时序与配置ST7701S 正常出图的 9 个必调参数4.1 从屏参看时序hfp、hbp、vfp、vbp 怎么填屏厂的规格书里会给一组 timing写成hfp/hbp/vfp/vbp。这组值最终要转换成 DRM 的drm_display_mode转换关系是hsync_start hdisplay hfphsync_end hsync_start hsync_pulsehsync 宽度htotal hdisplay hfp hsync_pulse hbp垂直方向同理vsync_start vdisplay vfpvtotal vdisplay vfp vsync_pulse vbp我见过有人直接把hfp填进hsync_start结果画面整体偏移屏幕边缘出现黑带。正确做法是先把屏厂的 hfp、hbp、hsync 宽度拆开再换算成hsync_start/hsync_end/htotal。没有 DRM 框架的裸机驱动则要把这组值填进主控的 DSI timing 寄存器原理相同。除了数值本身水平同步极性HSYNC_POLARITY和垂直同步极性VSYNC_POLARITY也要和屏参一致。老工程师都知道时序极性反了不会白屏但会出现画面抖动的玄学问题示波器抓不出明显异常最后拿屏厂参考驱动一对比才发现极性写反。所以在填时序时永远先把极性这一栏抄对。4.2 像素时钟、lane 速率与 RGB 格式的换算ST7701S 的接入速率受两个因素限制主控 DSI controller 的 PLL 可输出频率范围和屏体本身能接受的 DSI 信号速率。有些屏虽然分辨率不高但 lane 数只有 1导致单 lane 速率被迫抬高超过了主控的稳定范围这时候就需要在“加 lane 数”和“降刷新率”之间做取舍。一个常见计算例子480x854、RGB666、60Hz假设 htotal540、vtotal890像素时钟约为 28.8MHz。如果走 2 lane、按 24bit 打包每 lane 比特率约 345.6Mbps这个速率对大多数平台很轻松。如果屏厂要求按 18bit 松打包每 lane 比特率降到约 259.2Mbps。差别在于 RGB666 在 DSI 链路上有两种打包方式主控和屏必须一致否则花屏加偏色一起来。这个参数的调试顺序我一般是这样先用屏厂推荐的目标速率确认出图再往下调 10%看有没有闪烁再往上调 10%看主控有没有报 FIFO 溢出。花屏如果只在快速滑动时出现大概率是链路带宽临界优先检查 lane 数和打包格式而不是急着改初始化序列。表格式地看9 个参数里大部分问题都可以定位到下面几类参数典型配置异常表现lane 数2 lane配错会通信失败或半屏花RGB 格式RGB666 / RGB888与屏参不一致时偏色像素时钟按 htotalvtotalfps 计算过高时闪烁或纹波命令/视频模式视频模式命令模式会闪屏reset 时序低 10ms高 50ms初始化不稳定sleep out 延时120ms命令丢失背光时序enable 最后开先亮会闪白上下电顺序VCI→VDDI→reset顺序反了会漏电GIP 配置屏厂序列亮一半或黑块4.3 背光与上下电时序不开机、闪烁、白屏的根源ST7701S 的电源一般分两组VCI 模拟电源和 VDDI 数字电源有的面板还有 VSP/VSN 偏压。屏厂规格书里会画一个上电时序图通常要求 VCI 先稳、VDDI 跟上然后 reset 拉低、稳定后拉高最后再发 DSI 命令。这个顺序不能省。有些平台的 PMIC 上电顺序由硬件时序控制软件改动不了但驱动里至少要保证 reset 是在电源稳定之后才释放。背光和屏幕的关系是另一个高频坑。很多工程师把 backlight 使能和DCS_DISPLAY_ON放在同一时刻结果开机瞬间闪一下白屏。正确顺序一般是先发送完整初始化序列再发 sleep out等延时过后发 display on然后才打开背光。在 DRM 框架里这对应prepare里做 reset 和 initenable里开背光。反过来关机时顺序要颠倒先关背光再发 display off然后 sleep in最后断电。顺序反了屏幕上会残留一道亮线严重时还会损伤面板。这种问题在单台样机上看不出来量产时不良率会突然升高所以驱动里一定要按屏厂时序写。4.4 命令模式与视频模式的具体选择ST7701S 本身内置 GRAM支持命令模式意味着主控不需要持续刷数据只在内容变化时发送帧数据。这种模式适合追求低功耗的穿戴屏缺点是主控侧要自己做局部刷新如果整帧重绘带宽需求不降反升。视频模式则要求主控持续输出屏幕显示稳定但功耗偏高。实际项目里跑 Linux DRM 的一般选视频模式因为 Linux 的渲染合成管线天然是持续扫帧的。跑 MCU 轻量 UI 的优先考虑命令模式可以配合 ST7701S 的 GRAM 做局部刷新省电效果明显。这里没有绝对优劣势只有和你的应用场景匹配与否。如果代码已经写成视频模式想改成命令模式需要同步改主控 DSI controller 的配置和 panel 驱动的mode_flags不是一个宏就能切过来的。5. ST7701S 驱动移植常见问题5 条踩坑记录与排查路径5.1 白屏不亮初始化序列里漏了解锁或寄存器写失败现象上电后背光亮屏幕全白没有图像。dmesg 或串口看不到报错初始化函数好像被调用了但屏就是没反应。原因最常见的是解锁序列没发成功。ST7701S 在默认状态下禁止写内部寄存器必须先发0xFF开头的厂商解锁命令。如果解锁序列里少一个字节或者发送顺序不对后面所有寄存器配置都进不去。另一个常见原因是 DSI 时钟配置太低命令包在链路上没被正确接收。解决先用逻辑分析仪抓 DSI 的 D0 通道确认初始化期间确实有 HS 数据包发出。然后把初始化序列拆成每一条命令单独执行每发一条就延时 10ms通过屏的状态变化判断哪一步开始失效。如果确认链路有数据、但屏依然白屏基本可以断定是解锁字节的问题找屏厂重新核对第一个0xFF命令的参数。5.2 花屏撕裂DSI 参数和屏参对不上现象屏幕能显示但画面横纹、撕裂或者刷新时上半屏和下半屏不同步。原因像素时钟和 DSI 链路带宽不匹配或者 htotal/vtotal 计算错误导致主控发送的数据量超过屏的 GRAM 接收能力。视频模式下尤其明显因为数据是持续流屏端一旦 FIFO 溢出就丢包表现出来就是撕裂。解决先用 modetest 或串口确认当前 mode 的 htotal/vtotal 和 clock 值再和屏厂规格书逐项核对。像素时钟偏高时优先降低后肩 hbp而不是直接砍刷新率。如果是在裸机上检查主控 DSI controller 的mipi_dsi_pixel_format是否和屏一致RGB666 的打包格式错了也会造成类似花屏。5.3 开机闪一下黑屏sleep out 与 backlight 使能顺序现象开机时屏幕先闪白随后恢复正常显示或者开机后黑屏要等几秒才亮。原因背光使能时机早于 sleep out 完成。ST7701S 在 sleep 状态下没有显示内容此时开背光会看到白屏如果 sleep out 的 120ms 延时不够紧接着的 display on 命令可能被 IC 内部状态机丢掉屏幕就一直黑着。解决重新整理 panel 驱动的准备和使能顺序。裸机代码里在背光 GPIO 拉高之前必须已经完成 sleep out 和延时。DRM 框架里把初始化逻辑全部放在prepare背光放在enable不要图省事合并。黑屏等待几秒的现象通常是链路重训或 DRM framework 重试本质还是时序不满足。5.4 偏色RGB 顺序与 byte order 配置错现象图像能显示但红色和蓝色互换或者色温明显不对白色偏黄偏蓝。原因主控侧 DSI controller 的 byte order 和 ST7701S 配置的 RGB 通道顺序不一致。有些主控默认是 RGB有些默认是 BGR而 ST7701S 内部也有 RGB 顺序的寄存器配置两边必须一致。解决先改主控 DSI controller 的 byte order 寄存器这是最快验证手段。如果主控没有该寄存器再改屏端初始化序列里的通道交换寄存器。修改后要重新发送初始化序列并做白平衡验证。偏色问题不建议先调 gammagamma 是曲线问题通道交换是顺序问题两者混淆会越调越乱。5.5 排查工具用 modetest 和 dmesg 定位驱动还是链路现象屏幕完全无显示不确定是驱动问题、设备树问题还是硬件链路问题。原因ST7701S 驱动的排查链条很长从 devicetree 的 compatible 匹配到 DSI 设备注册再到 DRM panel 探测中间任何一环断掉都会表现为无显示。解决跑 Linux 的内核先看 dmesg重点搜st7701、mipi_dsi、drm_panel关键字。modetest -M可以列出当前 DRM 管线里的 connector、encoder、crtc 和 mode。如果 modetest 里能看到 480x854 的 mode说明 panel 驱动 probe 成功问题在显示链路如果 mode 都不存在说明 panel 根本没有注册成功回头查mipi_dsi_attach和 devicetree 的panel0节点。裸机上没有 modetest就抓链路信号先确认 CLK 和 D0 有波形再断言屏本身有问题。6. 让 ST7701S 驱动少走弯路的验证手法把 init sequence 一段段录下来ST7701S 驱动做到最后真正能快速定位问题的手段不是多看 datasheet而是把上电后的 DSI 信号完整录下来对着屏厂初始化序列一段段比对。我现在每调一个新屏都会先用逻辑分析仪抓一次 CLKP/CLKN 和 D0P/D0N长度覆盖整个初始化过程然后展开 HS 数据包一条命令一条命令地核对命令字节和参数长度。这个做法能验证三件事。第一reset 拉高到第一条 DCS 命令之间到底隔了多久是否满足屏要求的 50ms 以上。第二解锁命令后面的参数是否与屏厂文档完全一致少一个字节都能看出来。第三sleep out到display on之间的延时是否被主控的调度打断过。很多随机性白屏其实就是延时被其他任务抢占后命令拆成了两个时间片屏端状态机不理解。信号录下来后这类问题从“怀疑驱动”变成“直接看到”定位效率完全不一样。另一个我习惯做的验证是在初始化完成后回读 ST7701S 的寄存器。如果屏支持回读用 DCS read 命令把关键寄存器读回来和写入值比对。回读失败通常说明 DSI 链路只支持单向写或者屏已经进入异常状态。这样能在硬件改版前就发现隐患而不是等到量产后才暴露。做完这些验证我总会花十分钟把初始化序列整理成带注释的 C 数组备注每条命令的作用和来源。这个习惯救过我很多次——换主控平台时可以直接把数组平移过去不用重新查文档。希望帮到你。本文还有配套的精品资源点击获取
返回列表