
简介ST7701三线SPI初始化代码包面向嵌入式/MCU开发者旨在解决小尺寸TFT LCD在3线SPI模式下的时序模拟与寄存器配置问题尤其适配STM32平台。压缩包内共2个文件C源文件封装了GPIO模拟SPI的完整初始化和写命令/数据流程DOCX波形图展示了各阶段GPIO信号的时序变化便于对照排查通信异常。资源包仅367KB轻量易用便于快速参考与移植。目前已有3354人学习该资源参考价值获得认可。通过研读代码和波形图可掌握ST7701的上电配置、分辨率设置、电源控制等关键步骤并理解如何用普通GPIO模拟SPI与屏幕通信有助于实现定制化LCD显示方案。 做嵌入式显示这行和屏厂打交道是躲不开的日常。绝大多数时候你拿到的不是一块屏而是一个连接器和一串文件名像st7701_3线SPI初始化代码.zip这样的压缩包里面躺着几个 c 文件、h 文件外加几行不知道从哪个项目里抄来的注释。我第一次看到这类包也是一头雾水后来连续调过几个项目才摸清里面那套 3线SPI 初始化逻辑其实是整个屏幕驱动里最该吃透的部分。今天就把我拆解这类代码包的完整思路写出来顺便把 3线SPI 的坑和调试手法都整理清楚。这个代码包解决的事情其实很小用 3线SPI 给 ST7701 这颗显示驱动 IC 灌入初始化序列。ST7701 常见于 4~7 寸的 TFT-LCD像 480x854、720x1280甚至 1080x2340 这类分辨率显示主通路走 MIPI DSI但上电之初的寄存器配置基本都交给 SPI。SPI 相当于“告诉芯片怎么开始工作”的前台DSI 才是后面送画面的干线。适合正在调 STM32、ESP32、FPGA 屏幕方案的工程师参考也适合第一次拿到屏驱代码包的初学者快速上手。1. 拿到初始化代码包先搞懂这四件事1.1 ST7701 是什么为什么一帧图像都显示不出来先说几句 ST7701。它不是单片机外设而是一颗专门驱动 TFT-LCD 面板的显示控制 IC内部集成了 MIPI DSI 接收端、时基控制器、源极驱动和栅极驱动逻辑。屏幕能不能亮、颜色准不准、刷新稳不稳都是这颗芯片说了算。你平时看到的那些 LCD 驱动芯片比如 ST7789、ILI9488属于并行/MCU 接口的小尺寸屏驱而 ST7701 主要是走 DSI 通道的中大尺寸屏初始化方式就不一样。为什么一定要初始化因为芯片上电之后内部寄存器的默认值是一个非常保守的“安全状态”既不知道面板的分辨率也不知道电源电压该给多少更不知道 Gamma 曲线应该长什么样。必须靠主机在开机瞬间快速写入几十上百条寄存器配置芯片才能在正确的电压、时序、显示模式下工作。顺序错了或者漏了某一条表现就是各种奇葩白屏、花屏、颜色发紫、画面一闪而过然后熄灭。这个过程很像是给新电脑装显卡驱动不装驱动系统也能跑但显卡永远工作在兼容模式下画面惨不忍睹。这个阶段特别容易踩的一个坑是拿“上电默认能点亮”来偷懒。有些屏幕模组上电确实能看到测试画面或花屏那是模组自检图案不是正常工作画面。真正要显示自己要的图像必须把初始化代码完整执行一遍而且执行顺序不能乱。代码包之所以存在就是因为屏厂已经把这套序列调好了我们需要做的是原样搬过去而不是自己发挥。1.2 代码包的文件结构和初始化序列构成正常屏厂给的 SPI 初始化代码包一般包含这几个部分底层驱动文件通常是 GPIO 模拟 SPI或者基于某颗主控的 SPI 外设封装。初始化表是一大段 const 数组里面是寄存器和参数按顺序排好。屏参文件定义分辨率、DSI 通道数、porch 参数等。简短调用说明告诉你要先上电、再复位、再执行 init。这里最关键的是初始化表它决定了屏幕的一切行为。我拆过不少屏厂代码ST7701 的初始化表一般在 400 到 1000 字节之间里面的寄存器大致分成几个区段切页命令、电源设置、VCOM 校准、panel timing、Gamma、显示控制。不同屏厂在同一颗 IC 上的寄存器值会不一样不要以为换个屏厂、同一颗 IC 的初始化表就能直接通用很多参数是和具体面板相关的。所以在移植前我建议先把表里面的开头几条弄懂。ST7701 有个特点很多寄存器分布在不同 bank 里需要通过 0xFF 写一串参数来“切页”。比如常见的0xFF 0x77 0x01 0x00 0x00 0x13 0x10就是切到 page0 的典型序列。后面再改寄存器针对的就是这个页里的寄存器。如果切页忘了写或者参数少了后续的寄存器地址会对不上代码抄过来怎么看都不对其实就是在这种地方翻车。2. 3线SPI 和普通 SPI/I2C 的根本差异2.1 3线SPI 的针脚定义和 9bit 传输格式接触这类屏之前我一直以为 3线SPI 就是把 CS、SCK、MOSI 三根线接上传输方式和标准 SPI 一模一样。实际完全不是。ST7701 模组上常说的三线指的是 SCL、SDA、CS 三根线没有单独的 DC数据/命令选择脚。没有 DC 脚那怎么区分这次发的是命令还是数据答案是借用数据线上的第九个 bit。具体来说每传输一个字节实际会在总线上产生 9 个时钟脉冲。主机先发一个 DC 标志位0 表示后面 8 位是命令1 表示后面 8 位是数据。如果要用代码表示每次要写一个 uint16_t 的值最高位第 9 位放 DC 标志低 8 位放真正的字节。比如发送命令 0x36总线上的完整数据是 0x0036发送数据 0x60总线上的值是 0x0160。这跟标准 8bit SPI 有本质区别很多人在 GPIO 模拟实现时循环从 i8 到 i0写 9 位这就对了。如果写 8 位芯片会把第一字节的最高位当成 DC然后整个字节错位。时序上SCL 空闲为低每个 bit 在上升沿采样这是 SPI Mode 0和大多数 LCD 保持一致。下面这个代码就是 9bit 发送的核心循环void spi_write_9bit(uint16_t data) { for (int i 8; i 0; i--) { SCL_LOW(); if (data (1 i)) { SDA_HIGH(); } else { SDA_LOW(); } SCL_HIGH(); // 可加一个 nop 让波形稳定 } SCL_LOW(); }2.2 硬件片选与软件片选以及和 I2C 的选择三线方案里CS 的存在也不能忽视。有的模组把 CS 引出有的干脆固定拉低这时主机就不需要再操作 CS。如果 CS 是悬空的状态没处理芯片可能进入错误的通讯状态。有些屏厂为节省 pin 脚会把 CS 直接接地那就变成真正意义上的“两线”通讯了但代码包里依然叫三线因为屏体上确实有三根信号的输入。我自己的习惯是能用软件 GPIO 控制 CS 就尽量用软件尤其在初始化这种低速场景下软件片选更灵活。硬件片选在 SPI 传输结束时会自动拉高但有时候会在中途多产生一次片选跳变对某些 LCD 芯片来说容易误判一帧数据的结束。反过来在高速刷屏或 DSP 之类场景硬件片选可以省 CPU但 ST7701 初始化阶段根本不需要高速所以软件控制反而更稳。再对比一下 I2C。同样是接两根信号线I2C 需要设备地址、需要 ACK 应答主机还要处理总线的 START/STOP 条件代码量大速度也上不去。SPI 是主从同步主机准备好时钟和数据就能发端口的时序更直接。我遇到过代码包里同时带 I2C 和 SPI 两套初始化实测 SPI 版本的兼容性最好所以优先考虑 3线SPI。3. 核心代码拆解和移植的自用结构3.1 底层封装把 9bit 变成命令和数据的组合有了 spi_write_9bit 之后我们就能封装两个更上层的函数一个写命令一个写数据这样后面初始化表就非常清晰了void ST7701_Write_Cmd(uint8_t cmd) { spi_write_9bit((uint16_t)cmd); // DC0 } void ST7701_Write_Data(uint8_t data) { spi_write_9bit((uint16_t)data | 0x0100); // DC1 }这里就是关键中的关键data 用| 0x0100把第 9 位置 1。如果你写顺手了忘掉| 0x0100后面的延时和参数表对着 datasheet 对不对都没用整条 SPI 线收到的全是命令屏幕连背光都异常。这种错误的隐蔽性很高因为一条命令和一条数据的 8bit 本体相同要靠第 9 位区分写错就是全部错位。初始化序列的执行流程也可以封装一下for (uint16_t i 0; i init_len; i) { ST7701_Write_Cmd(init_table[i].cmd); for (uint8_t j 0; j init_table[i].len; j) { ST7701_Write_Data(init_table[i].data[j]); } if (init_table[i].delay_ms) { delay_ms(init_table[i].delay_ms); } }这里有个细节延时不能随意删除。比如退出睡眠0x11之后芯片内部电源电路要重新稳定厂商通常会要求等待 120ms 左右再去写 0x29 开显示。你要是把延时删掉强行提速屏幕上就会出现初始化后画面闪一下就没了的问题。做量产固件时这段延时宁多勿少。3.2 初始化表结构设计三种常见写法对比代码包里见到最多的初始化表有三种写法。第一种是纯内存流把命令和数据全部铺开靠某几个特殊值表示延时或切页信息。这种最省空间但可读性差改一行参数还要数偏移量我不推荐。第二种是二维数组const uint8_t init_table[][2] { {0xFF, 0x77}, {0xFF, 0x01}, // ... {0x29, 0x00}, };这种写法简单直观但没法表达多参数命令因为一行只有两个字节。第三种是我个人最常用的结构体数组它把命令、参数个数、参数指针、延时时间都放在一个结构体里每个表项自行解释自己。拆代码包的时候我习惯把某个寄存器段的参数提出来单独做成一个数组比如 Gamma 表、porch 表然后在主表里以指针方式引用。看起来复杂一点但后续维护、对照数据手册标注比压成一长串数组省心太多。如果你拿到的是一个纯 uint8_t 数组的原始包第一步建议把它翻译成上述结构体或者至少用注释把每一段分隔开。翻译的过程等于通读一遍寄存器配置是排查很多疑难问题的必经之路。3.3 初始化表里几个高频寄存器的理解说几个必然出现在 ST7701 初始化表里的寄存器或命令遇到背光不亮、颜色不对时可以回来检查。0xFF 切页命令前面说过是 ST7701 区别于很多小屏驱动 IC 的地方。有些初始化表开头会连写好几条 0xFF每一条都带不同参数这是在切换不同的寄存器 bank。千万不要把这几条 0xFF 当成重复项删掉。0x3A 像素格式。0x3A 通常设置为 0x77表示 24bit 的 RGB888。如果你看到的是 DSI 屏但颜色通道配错了画面上会出现颜色取样错乱的特征绿草变紫、红蓝反转。这条命令一般写在 0x11 之前或之后要按芯片手册顺序。0x36 显示扫描方向。它决定画面是从左上角开始还是从右下角开始影响横屏、竖屏以及镜像方向。代码包里大多是屏厂默认值如果你的产品需要旋转 90 度或 180 度改的是这个寄存器而不是去旋转图像数据。0x29 开显示0x11 退出睡眠。这两条是初始化收尾阶段必备的顺序不能反时间间隔要够。我看到有些代码包在 0x11 前写一遍 Gamma在 0x11 后又会写一遍 Gamma这是因为切页之后某些寄存器默认值会变化屏厂这么处理有它的原因不建议随便合并删减。4. 调试实录与常见问题排查4.1 白屏、花屏、闪屏的排查顺序把所有初始化代码都移植完了屏却不亮这是最让人崩溃的场景。我的经验是从上电到画面出现的整个链路按下面顺序排查先看背光。背光亮不亮如果不亮优先怀疑背光电源和使能脚和 SPI 关系不大。再看复位。ST7701 的 RESET 脚是低电平复位拉低至少 5ms再拉高然后等上电稳定。这一步没做好的话芯片可能停留在复位状态或未完全退出复位SPI 写什么都不会有响应。接着看 SPI 波形。用逻辑分析仪夹在 SCL、SDA、CS 上抓一段 init 执行过程肉眼观察是否存在额外的毛刺、时钟丢拍、CS 提前置高等问题。特别注意抓完波形后要确认第一帧命令的第九个 bit 是 0命令如果看到第九位是 1说明 DC 标志反了。最后确认初始化完成后有没有切到 DSI。ST7701 不像 ST7789 那样靠 SPI 直接送像素初始化完成之后数据通路要由 MIPI DSI 接管。如果 DSI lane 数、时钟频率、帧参数配置错了即使 SPI 初始化全对屏幕也可能白屏或者只亮背光。4.2 常见问题速查表故障现象可能原因排查优先级白屏SPI 时序错误、DC 标志位错误、CS 极性先抓 SPI 波形花屏且颜色错乱0x3A 像素格式不对、Gamma 表缺失核对 0x3A 和 Gamma只有背光无画面DSI 初始化/参数不对检查 lane 数和时钟画面抖闪0x11 后延时不够、电源不稳定增加延时、测电源纹波画面偏移或镜像0x36 方向配置错误检查扫描方向设置这里再强调一句GPIO 模拟 SPI 时如果在中断密集的环境里用软件打波形必须在初始化函数里加上临界区保护否则突然来的中断极有可能打断时钟周期导致屏幕某次初始化偶发失败。我在 STM32 上遇到过“十次开机三次白屏”的情况最后排查发现就是滴答定时器中断把 SPI 发到一半的波形切断了。后来把整个 init 包在关中断里执行问题立刻消失。4.3 关于硬件 SPI 和 DMA 的一点补充有朋友会问能不能用硬件 SPI 做这件事可以但要做一点变通。如果模组把 DC 脚单独引出来了那恭喜你直接用标准 4 线 SPI 就行一个 GPIO 控制 DCSPI 外设只管 8bit 数据即可。如果模组没有 DC 脚那就只能用 9bit 方式。在支持 9bit SPI 字长的 MCU部分 Cortex-M 系列支持 9bit 模式上可以配置为 9bit 硬件 SPI然后把命令或数据值直接填到数据寄存器里发送效率比 GPIO 模拟高很多。但要注意 ST7701 的 SPI 时钟频率不要超过屏厂规格datasheet 一般会给到 16MHz 或更高实际使用留一半余量更稳。CubeMX 里配置 SPI 时记得设置 CPOL0、CPHA0即 Mode 0。如果你用 ESP32 这类 MCU硬件 SPI 默认也是 8bit如果不支持 9bit 模式一种做法是把初始化表每个命令和数据自行拆成 9bit再用 GPIO 移位模拟另一种做法是用 RMT 输出自定义时序但那样维护成本高不如直接软件模拟直观。至于 SD 卡共享 SPI 的问题屏幕初始化是在开机早期完成的SD 卡读写又集中在文件系统层只要分时复用并处理好 CS 片选两者共享 SPI 总线是可行的。初始化完成后把屏幕的 CS 拉到固定电平防止总线上的后段操作误伤屏幕寄存器就行。拆过的初始化代码包多了我现在拿到st7701_3线SPI初始化代码.zip反而觉得比那些封装得花里胡哨的 SDK 更亲切。因为底层就那么回事一根时钟、一根数据、一根片选按 9bit 一位一位喂进去剩下的是耐心核对寄存器表。我个人的习惯是每调完一种屏都会把初始化表按照“切页、电源、时序、Gamma、显示控制”分块加注释再做一份精简版这样下次再遇到同系列 IC 的屏基本半天就能点起来。如果你也正在被一块白屏折磨不妨试试把注意力先从“代码哪里写错了”挪到“波形和寄存器表对不对”上多半能少走很多弯路。本文还有配套的精品资源点击获取