搞懂ST7789V的SPI通信:命令和数据到底怎么切?
你有没有遇到过这种情况——屏幕通电了,MCU也跑了代码,但显示出来的是花屏、全白、或者干脆没反应?
如果你正在用ST7789V驱动一块1.3寸或1.54寸的小彩屏,那问题很可能出在最基础却最容易被忽视的地方:SPI命令与数据的切换逻辑。
别急着换库、重写初始化序列,先搞清楚这个核心机制:DC引脚到底是干什么的?为什么它比SCK和MOSI还关键?
为什么SPI需要“额外”一个DC引脚?
我们都知道SPI有四根线:SCK(时钟)、MOSI(主机发从机收)、CS(片选),还有一个常被忽略但至关重要的——DC(Data/Command)。
SPI本身只是一个“搬运工”,它不知道自己传的是“打开门”的指令,还是“门后的密码”。而ST7789V作为LCD控制器,必须靠外部主控告诉它:“接下来这几个字节是命令!” 或者 “这些是像素数据!”
于是,DC引脚就成了语义开关:
- DC = 0→ 当前传输的是命令
- DC = 1→ 当前传输的是数据
📌 简单说:没有DC,SPI就只是一条哑巴总线;有了DC,才能让ST7789V听懂你在说什么。
举个例子:
ST7789_Write_Cmd(0x2C); // 写入命令 0x2C(开始写显存) ST7789_Write_Data(pixels, len); // 接着写入成千上万个RGB565数据如果中间DC没切换过来,MCU发出去的数据就会被当成一条条非法命令执行,轻则乱码,重则芯片进入未知状态。
四线SPI怎么接?别小看这五根线
虽然叫“四线SPI”,实际连接ST7789V至少需要5个GPIO:
| MCU引脚 | 连接到 | 功能说明 |
|---|---|---|
| SCK | SCL/SCK | SPI时钟 |
| MOSI | SDA/MOSI | 数据输出 |
| CS | CS | 片选(低有效) |
| DC | DC | 命令/数据选择 |
| RST | RST | 复位(可选但推荐) |
有些模块把RST内部上拉处理了,可以省掉手动控制。但DC绝对不能省,也不能反接!
⚠️ 常见坑点:某些屏幕模块标注为“支持SPI”,但默认DC高为命令、低为数据——这和标准相反!一定要查手册确认极性。
ST7789V是怎么工作的?拆开看看
内部结构简析
ST7789V不是简单的驱动芯片,它集成了:
- 振荡器:自带时钟源,无需外部晶振
- GRAM(图形RAM):240×320×16bit ≈ 153.6KB 显存,直接存在芯片里
- 电源管理单元:支持睡眠、深睡模式
- 接口协议解析器:能识别SPI命令流并做出响应
这意味着你不需要外挂帧缓冲,只要通过SPI把图像数据“倒进去”,它就能自动刷到屏幕上。
核心寄存器一览
几个关键命令你得记住:
| 命令(Hex) | 名称 | 功能描述 |
|---|---|---|
0x01 | SWRESET | 软件复位 |
0x11 | SLPOUT | 退出睡眠模式 |
0x28 | DISPOFF | 关闭显示 |
0x29 | DISPON | 开启显示 |
0x2A | CASET | 设置列地址范围 |
0x2B | RASET | 设置行地址范围 |
0x2C | RAMWR | 开始写GRAM |
0x3A | COLMOD | 设置颜色格式(如RGB565) |
0x36 | MADCTL | 控制显示方向 |
这些命令都不是随便发的,顺序错了、参数不对,都可能导致初始化失败。
实战:如何正确发送一条带参数的命令?
以设置显示区域为例,我们要画一个矩形区域(比如从 (0,0) 到 (239,319)),流程如下:
void ST7789_Set_Address_Window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { // Step 1: 发送 CASET 命令(列起始和结束) ST7789_Write_Cmd(0x2A); uint8_t data[4]; data[0] = x0 >> 8; // X起始高位 data[1] = x0 & 0xFF; // X起始低位 data[2] = x1 >> 8; // X结束高位 data[3] = x1 & 0xFF; // X结束低位 ST7789_Write_Data(data, 4); // Step 2: 发送 RASET 命令(行起始和结束) ST7789_Write_Cmd(0x2B); data[0] = y0 >> 8; data[1] = y0 & 0xFF; data[2] = y1 >> 8; data[3] = y1 & 0xFF; ST7789_Write_Data(data, 4); // Step 3: 准备写显存 ST7789_Write_Cmd(0x2C); // RAMWR }注意这里的细节:
- 每次发命令前都要
DC=0 - 参数全部通过
DC=1的方式发送 - 整个过程中
CS可以保持拉低(提高效率) - 地址是两字节高位在前,别搞反了!
初始化为啥这么重要?顺序不能乱!
很多开发者直接复制别人的初始化代码,结果换了块屏就不行。原因在于:每一步都有依赖关系。
典型的启动流程应该是这样的:
1. 硬件复位(RST拉低10ms) 2. 延时120ms等待电源稳定 3. 发送 0x01 (SWRESET) → 软件复位 4. 延时150ms 5. 发送 0x11 (SLPOUT) → 退出睡眠 6. 延时200ms 7. 配置电压、伽马曲线、色彩格式等... 8. 发送 0x29 (DISPON) → 打开显示其中最关键的是SLPOUT之后必须有足够的延时,否则后续命令可能被忽略。这也是为什么你的屏幕总是“黑屏但能点亮背光”的原因之一。
代码封装技巧:写出可移植的驱动
为了方便在不同平台使用(STM32、ESP32、RP2040等),建议将底层操作抽象化:
// 引脚操作宏(根据不同平台替换) #define LCD_CS_LOW() gpio_write(CS_PIN, 0) #define LCD_CS_HIGH() gpio_write(CS_PIN, 1) #define LCD_DC_CMD() gpio_write(DC_PIN, 0) #define LCD_DC_DATA() gpio_write(DC_PIN, 1) // SPI发送函数(可用HAL、LL或DMA) void lcd_spi_send(uint8_t *buf, size_t len) { spi_write_blocking(SPI_PORT, buf, len); } // 统一写命令/数据接口 void lcd_write_command(uint8_t cmd) { LCD_CS_LOW(); LCD_DC_CMD(); lcd_spi_send(&cmd, 1); } void lcd_write_data(uint8_t *data, size_t len) { LCD_DC_DATA(); lcd_spi_send(data, len); LCD_CS_HIGH(); // 可在此释放CS }这样做的好处是:换芯片时只需改宏定义,不用动核心逻辑。
常见问题排查清单
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 屏幕全白 | 未发送DISPON(0x29)或SLPOUT(0x11)缺失 | 检查初始化序列完整性 |
| 花屏错位 | MADCTL方向设置错误 | 尝试写入 0x00~0x07 或 0x70 测试 |
| 完全无显示 | DC引脚接反或悬空 | 用万用表测DC电平变化 |
| 刷新慢卡顿 | SPI频率太低 | 提升至8~15MHz(注意布线长度) |
| 开机闪一下灭 | 电源不稳或复位太快 | 加大复位延时,检查VCC滤波电容 |
💡 小技巧:可以用逻辑分析仪抓SPI波形,观察DC是否随命令/数据正确翻转。
性能优化建议
1. 提高SPI速率
- STM32:配置SPI到
10~15MHz - ESP32:启用VSPI,关闭WIFI/BT避免干扰
- RP2040:使用硬件SPI+DMA减少CPU占用
⚠️ 注意:高频下走线要短,最好铺地屏蔽,否则容易丢包。
2. 分块刷新降低内存压力
对于没有外部SDRAM的MCU(如STM32F1系列),不要一次性生成整屏图像。采用“窗口刷新”策略:
draw_part_of_screen(0, 0, 120, 160); // 左上角 refresh_lcd(); draw_part_of_screen(120, 0, 239, 160); // 右上角 refresh_lcd();既能节省RAM,又能避免长时间阻塞系统。
3. 动态旋转支持
利用MADCTL寄存器实现横竖屏切换:
| MADCTL值 | 显示方向 |
|---|---|
| 0x00 | 正常(0°) |
| 0x60 | 90°旋转 |
| 0xC0 | 180°旋转 |
| 0xA0 | 270°旋转 |
只需在初始化时写入对应值即可,无需重新布局UI。
和其他驱动IC比,ST7789V强在哪?
| 对比项 | ST7789V | ILI9341 | 备注 |
|---|---|---|---|
| 分辨率 | 240×320 | 240×320 | 相同 |
| 默认初始化 | 更简洁 | 较复杂 | ST7789V更易移植 |
| 极性适配 | 接近现代MCU | 常需反转 | 减少调试成本 |
| 支持协议 | SPI / RGB | SPI / 8080 | 应用场景略有差异 |
| 社区资源 | 丰富(TFT_eSPI等) | 极其丰富 | 都好用 |
总体来看,ST7789V更适合现代嵌入式项目,尤其是基于ESP32、树莓派Pico这类开发板的应用。
最后一句真心话
掌握ST7789V的SPI通信机制,本质上是在理解“如何跟一块沉默的屏幕对话”。
你不只是在发数据,而是在建立一种协议级的信任:每一次DC的切换,都是你在说:“注意,下面这句话很重要。”
当你真正明白了这一点,花屏、黑屏、无法初始化这些问题,也就不再是“玄学”,而是可以一步步追踪、修复的技术细节。
如果你也在做嵌入式显示相关的项目,欢迎留言交流踩过的坑。下次我们可以聊聊:如何用DMA+双缓冲实现流畅动画?
📌关键词:ST7789V、SPI通信、DC引脚、命令与数据、GRAM写入、LCD驱动、嵌入式显示、TFT彩屏、MADCTL、初始化序列、RGB565、MCU图形界面