如何用4根线点亮一块LCD屏?——深入浅出解析4线SPI驱动原理与实战
你有没有遇到过这种情况:想给自己的嵌入式项目加个显示屏,翻遍模块手册却发现引脚密密麻麻,光控制线就七八根,MCU的GPIO眼看就不够用了?
别急。今天我们就来聊一个“四两拨千斤”的解决方案:只用4根信号线,就能驱动一块彩色TFT LCD屏——这就是广泛应用于中小尺寸屏幕的4线SPI接口技术。
它不是什么黑科技,也不是牺牲功能换来的妥协方案,而是在资源、速度和稳定性之间找到的最佳平衡点。接下来,我会带你从硬件连接讲到软件实现,从协议底层讲到工程避坑,让你真正搞懂:为什么这4根线,能撑起一片显示天地。
一、为什么是“4线”?SPI接口的本质与变种
说到SPI(Serial Peripheral Interface),很多人第一反应是“四根线”:SCLK、MOSI、MISO、CS。但如果你拆开一块常见的0.96英寸或1.8英寸TFT彩屏,会发现背面只有5~6个有效引脚,其中数据输入只有一个——根本没有MISO。
这是怎么回事?
答案是:在LCD应用中,我们用的其实是一种“定制化”的SPI模式,业内常称为“伪4线SPI”或“3线扩展模式”。真正的四条核心信号线其实是:
- SCLK:串行时钟,主控输出,同步每一位数据;
- MOSI:主出从入,传输命令和图像数据;
- CS:片选,决定是否响应通信;
- DC:数据/命令选择,这是关键中的关键!
📌 注意:这里的“4线”已经不再是标准SPI定义下的四线了。MISO被舍弃(因为LCD不需要回传像素数据),取而代之的是非标准但至关重要的DC(Data/Command)引脚。
你可以把它理解为一种“为显示服务而生”的精简版SPI协议。虽然少了双向通信能力,但却换来极高的集成度和低引脚成本。
二、这4根线都干什么?逐条拆解信号功能
我们来看一张典型的硬件连接图(文字描述):
MCU (STM32 / ESP32 / Arduino等) | |--- PA5 ---> SCLK ---> LCD_SCK |--- PA7 ---> MOSI ---> LCD_MOSI (或叫SDIN) |--- PA4 ---> CS ---> LCD_CS (低电平有效) |--- PA6 ---> DC ---> LCD_DC (也标作A0/D/C) |--- PB1 ---> RST ---> LCD_RESET (可选) | |--- 3.3V ---> VCC |--- GND ---> GND1. SCLK(Serial Clock)
由MCU产生,决定数据传输的节奏。每个时钟周期传送一位数据,通常工作在1MHz~10MHz之间。频率越高刷新越快,但也越容易受干扰。
2. MOSI(Master Out Slave In)
主设备发送数据的通道。所有命令和像素值都通过这条线“推”给LCD控制器。
3. CS(Chip Select)
片选信号,相当于“喊名字”。当CS拉低时,LCD才开始监听总线;一旦拉高,立即停止接收。允许多个SPI设备共享SCLK和MOSI线路。
4. DC(Data/Command Control)
这才是灵魂所在!
它的作用是告诉LCD:“接下来我给你的是命令还是数据?”
-DC = 0→ 后续字节是控制指令,比如“清屏”、“设置方向”、“进入睡眠”;
-DC = 1→ 后续字节是真实数据,可能是颜色值、字符编码或者图像片段。
没有DC,你就没法区分“我要画红色”和“我真的要显示数字0xF800”。
⚠️ 很多初学者烧录后屏幕无反应,问题往往出在这里:代码里忘了切换DC电平,导致数据被当成命令执行,直接跑飞。
三、它是怎么工作的?一次完整的写操作流程
让我们以“在屏幕上显示字母‘A’”为例,走一遍全过程:
Step 1: CS = 0 // 拉低片选,唤醒LCD Step 2: DC = 0 // 声明接下来发的是命令 Step 3: 发送 0x2C // 写内存命令(Write GRAM) Step 4: DC = 1 // 切换为数据模式 Step 5: 发送 'A' 的ASCII码(0x41) Step 6: CS = 1 // 结束通信整个过程就像两个人打电话:
- “喂,听得到吗?”(CS=0)
- “我现在要说的是菜单项。”(DC=0)
- “第28号菜。”(发送命令)
- “现在说具体内容。”(DC=1)
- “来一份红烧肉。”(发送数据)
正是因为这种分时复用机制,才能用最少的引脚完成复杂的交互。
四、背后的功臣:LCD控制器芯片究竟做了什么?
你以为MCU是在直接操控每一个像素?错。真正在幕后操盘的是那颗小小的LCD控制器芯片,例如:
- ST7735:常见于1.8寸TFT,支持RGB565格式;
- ILI9341:2.4寸主力选手,性能强,资料全;
- SSD1306:虽用于OLED,但通信逻辑高度相似;
- PCD8544:诺基亚5110经典屏专用。
这些芯片内部集成了:
| 模块 | 功能说明 |
|---|---|
| 接口解析器 | 识别SCLK边沿、采样MOSI数据、判断DC状态 |
| 命令译码器 | 把0x21这样的十六进制数翻译成“开启反显”等动作 |
| 显存(GRAM) | 存储当前要显示的像素数据,如128×160×16bit ≈ 40KB |
| 地址指针管理器 | 自动递增写入地址,支持窗口式更新 |
| 驱动时序发生器 | 生成行扫描、列驱动脉冲,控制液晶偏转 |
换句话说,你只需要告诉它“往哪写、写什么”,剩下的刷新、驱动、时序全由它搞定。这对主控来说简直是减负神器。
五、实际性能如何?和其他接口比到底强在哪?
我们不妨做个横向对比,看看4线SPI处在什么位置:
| 特性 | 并行8位 | I²C | 4线SPI |
|---|---|---|---|
| 使用IO数 | ≥10 | 2 | 4 |
| 最大速率 | ~20MB/s | ~0.4MB/s | ~10MB/s |
| 是否需要地址寻址 | 是 | 是 | 否(靠状态机) |
| 支持DMA | 是 | 否 | 可支持 |
| 开发难度 | 高 | 低 | 中 |
| PCB布线复杂度 | 高 | 极低 | 低 |
可以看到:
-I²C最省IO,但太慢,刷个图都要好几秒;
-并行接口最快,但吃IO,适合FPGA或高性能MPU;
-4线SPI正好居中:速度快、占用少、控制灵活,特别适合STM32、ESP32这类主流MCU。
更重要的是,配合DMA使用时,甚至可以在不占用CPU的情况下批量刷图,极大提升系统效率。
六、代码长什么样?教你写出第一个SPI写函数
下面是基于STM32 HAL库的一个典型实现:
// 写命令 void LCD_WriteCommand(uint8_t cmd) { HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_RESET); // DC=0 HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET); // CS=0 HAL_SPI_Transmit(&hspi1, &cmd, 1, 100); HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET); // CS=1 } // 写数据 void LCD_WriteData(uint8_t data) { HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET); // DC=1 HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(&hspi1, &data, 1, 100); HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET); }就这么简单?没错。只要把握住两个要点:
1.每次操作前必须正确设置DC;
2.CS要在传输前后做好升降沿。
后续你就可以组合调用它们进行初始化:
LCD_WriteCommand(0x11); // 退出睡眠模式 HAL_Delay(120); LCD_WriteCommand(0x29); // 开启显示是不是有种“原来如此”的感觉?
七、那些年踩过的坑:常见问题排查指南
❌ 问题1:屏幕乱码、花屏、闪屏
可能原因:SPI模式配置错误!
SPI有四种模式(CPOL和CPHA组合),而不同LCD控制器要求不同。例如:
- ILI9341 常用 Mode 0:CPOL=0(空闲低),CPHA=0(上升沿采样)
- ST7735 有些型号要用 Mode 3:CPOL=1,CPHA=1
🔧 解法:查数据手册确认SPI mode,或尝试四种组合调试。
❌ 问题2:只能执行命令,无法显示图像
现象:初始化成功,但写字没反应
根源:DC信号未切换!数据被当作命令丢弃。
🔧 解法:
- 检查DC引脚是否接错;
- 在关键位置加调试打印或逻辑分析仪抓波形;
- 确保LCD_WriteData()中确实设置了DC=1。
❌ 问题3:完全无响应,像没通电一样
排查思路:
- CS是否默认上拉?是否由MCU控制?
- 电源电压是否匹配?5V MCU接3.3V LCD需电平转换;
- 复位序列是否完整?有些屏必须先拉低RST再初始化;
- 初始化命令是否齐全?删减关键命令会导致死锁。
八、设计建议:让系统更稳定可靠的7个技巧
电源去耦不可少
在LCD的VCC引脚附近放置0.1μF陶瓷电容,抑制高频噪声。信号线上拉电阻
若CS、DC为开漏输出,建议加上4.7kΩ上拉电阻,防止误触发。走线尽量短且平行
SCLK与MOSI保持等长,避免串扰,尤其高速下更为重要。注意电压兼容性
5V系统驱动3.3V屏务必加电平转换芯片(如TXB0108、MAX3378),否则可能损坏模块。严格遵循初始化序列
不同厂商略有差异,最好采用官方推荐的初始化代码,不要随意删改。善用局部刷新
修改部分区域时,先设置地址窗口(Column Address Set + Page Address Set),再写数据,避免全屏重绘。封装抽象层便于移植
将底层SPI读写封装成独立函数,未来更换平台(如从STM32迁移到ESP-IDF)时只需重写驱动层。
九、不止于显示:现代嵌入式GUI的起点
掌握4线SPI驱动LCD,远不只是点亮一块屏那么简单。
随着轻量级图形库的发展,比如:
-LVGL:功能强大,支持触摸、动画、主题;
-TFT_eSPI + GUIslice:专为ESP32优化,资源占用低;
-LittlevGL + TouchGFX Lite:可在Cortex-M上运行精美界面;
你完全可以用一颗STM32F1,在没有操作系统的情况下,构建出带有按钮、滑动条、图表的完整用户界面。
而这套系统的起点,正是这看似简单的4根线。
如果你正在做物联网终端、智能仪表、便携设备,或是想为你的DIY项目增添视觉表现力,那么4线SPI驱动LCD的技术,值得你花时间深入掌握。
它不炫技,却足够实用;它不复杂,却蕴含巧思。
用最少的资源,实现最大的价值——这正是嵌入式工程的魅力所在。
💬 如果你在实际项目中遇到SPI-LCD相关难题,欢迎留言交流。我们可以一起分析波形、检查时序、优化代码,把每一块屏都点亮得清清楚楚。