news 2026/1/11 17:23:00

MicroPython控制ST7789:SPI驱动模块快速理解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MicroPython控制ST7789:SPI驱动模块快速理解

MicroPython 驱动 ST7789:从 SPI 通信到屏幕点亮的实战全解析

你有没有遇到过这种情况——手头有一块小小的圆形彩屏,引脚标着SCLSDADCRST,网上搜了一堆代码,复制粘贴后屏幕不是白屏就是花屏?明明用的是 MicroPython,语法简单了,怎么控制一块 TFT 屏反而比写个 LED 闪烁还难?

问题不在于你不会编程,而在于大多数示例代码只告诉你“怎么做”,却没讲清楚“为什么这么做”。尤其是像ST7789这类需要复杂初始化流程的显示驱动芯片,盲目调用封装好的库函数,一旦出错就无从下手。

今天我们就来一次彻底拆解:不用任何第三方库,从零开始,用原生 MicroPython 精确控制 ST7789 液晶屏。目标不仅是让屏幕亮起来,更要让你明白每一行代码背后的硬件逻辑。


为什么是 ST7789?它凭什么成为小尺寸彩屏的首选?

在当前的嵌入式开发中,图形界面不再是奢侈功能。无论是 DIY 温湿度监测仪、音频可视化设备,还是智能手表原型,一块能显示彩色内容的小屏幕极大提升了交互体验。

而在这类应用中,ST7789几乎成了默认选项。原因很简单:

  • 支持高达240×320 分辨率
  • 色彩深度达65,536 色(RGB565)
  • 可适配多种外形:矩形、圆形(常见 240x240)
  • 接口简洁:仅需 5 个 GPIO 引脚即可驱动

相比老一代驱动如 ILI9341,ST7789 更轻量、响应更快,且对现代 MCU 的 SPI 总线利用率更高。更重要的是,它的初始化序列虽然精细,但结构清晰,非常适合通过 MicroPython 实现底层控制。


核心机制:SPI 是如何把数据“送进”屏幕的?

要搞懂 ST7789 的工作方式,先得理解它和主控之间的通信桥梁 ——SPI 协议

SPI 不只是“发数据”,而是有节奏的对话

很多人以为 SPI 就是“不停地往 MOSI 发数据”,其实不然。ST7789 并不能自动分辨哪段是命令、哪段是参数。它依赖额外的控制信号来建立上下文。

我们使用的四线 SPI + 两个 GPIO 控制组合如下:

信号线功能说明
SCK(SCLK)时钟线,由主控生成,决定传输速率
MOSI主机输出,发送命令和像素数据
CS(Chip Select)片选,低电平有效,表示开始一次通信
DC(Data/Command)关键!高=数据,低=命令
RST(Reset)硬件复位引脚,确保上电状态一致

⚠️ 注意:尽管名字叫“四线 SPI”,实际需要6 条物理连接(含 DC 和 RST),别漏接!

数据与命令切换:DC 引脚的灵魂作用

这是最容易被忽视的一点:ST7789 所有的操作都始于一个字节的命令码。比如你想设置显示区域,必须先发0x2A(列地址设定),再跟上起始列和结束列的数据。

如果没有正确切换DC引脚的状态,芯片就会误解你的意图 —— 把本该是命令的内容当成数据写入显存,结果就是花屏或无响应。

举个生活化的比喻:
你可以把 SPI 通信想象成去银行办业务。
-CS是叫号机取号(开始服务)
-DC是你递上的单据类型:红色单据=开户(命令),蓝色单据=存款金额(数据)
-MOSI是你填写的信息内容

如果柜员拿错了单据颜色,哪怕内容是对的,也会办错事。


初始化不是“走流程”,而是给屏幕“校准生命体征”

很多开发者发现,即使接线正确、代码运行无报错,屏幕依然不亮。最常见的原因就是:初始化序列错误或缺失关键步骤

ST7789 上电后处于未知状态,内部电源、伽马曲线、内存映射等全部需要重新配置。这个过程就像给一台刚组装好的电脑安装操作系统和驱动程序。

正确的初始化顺序至关重要

以下是经过验证的标准流程骨架:

def init_display(): reset() # 第一步:硬件复位 time.sleep_ms(150) # 等待内部电路稳定 # 开始发送配置命令 write_cmd(0x36) # 内存访问控制 write_data(b'\x00') # 设置方向:横向、正常扫描 write_cmd(0x3A) # 像素格式设置 write_data(b'\x05') # 16位色(RGB565) # 电源管理相关配置... write_cmd(0xB7); write_data(b'\x35') write_cmd(0xBB); write_data(b'\x19') write_cmd(0xC0); write_data(b'\x2C') write_cmd(0xC6); write_data(b'\x0F') # 60Hz帧率 # 伽马校正(影响色彩表现) write_cmd(0xE0); write_data(b'\xD0\x04\x0D\x11\x13\x2B\x3F\x54\x4C\x18\x0D\x0B\x1F\x23') write_cmd(0xE1); write_data(b'\xD0\x04\x0C\x11\x13\x2C\x3F\x44\x51\x2F\x1F\x1F\x3F\x3F') write_cmd(0x11) # 退出睡眠模式 time.sleep_ms(120) # 必须等待至少 120ms! write_cmd(0x29) # 开启显示

看到这么多write_cmdwrite_data,是不是觉得眼花缭乱?其实它们可以分为几类:

类型典型命令作用
显示控制0x11,0x29控制睡眠/唤醒、开显示
内存配置0x36,0x3A设置旋转方向、颜色格式
电源管理0xB7,BB,C0设置 VCOM、VGHL 电压
刷新率0xC6设定帧频(影响功耗与流畅度)
伽马调节0xE0,0xE1调整色彩对比度与饱和度

其中最易出错的是0x3A0x36
- 若将0x3A设为0x03,则启用 12 位色,颜色严重失真;
- 若0x36参数设置不当,图像会倒置、镜像甚至偏移。

这些参数并非“通用”,不同厂商的模组可能略有差异。建议首次使用时查阅模块说明书,或参考卖家提供的 Arduino 示例。


底层通信封装:构建可靠的命令通道

所有高级操作都建立在两个基础函数之上:发送命令发送数据

下面是基于 ESP32 或 RP2040 的标准实现:

from machine import Pin, SPI import time # 初始化 SPI(Mode 0,最高支持 40MHz) spi = SPI(1, baudrate=40_000_000, polarity=0, phase=0, sck=Pin(14), mosi=Pin(13), miso=None) # 控制引脚定义 cs = Pin(12, Pin.OUT, value=1) # 默认禁用片选 dc = Pin(15, Pin.OUT, value=0) rst = Pin(11, Pin.OUT, value=1) def write_cmd(cmd): cs.off() # 拉低 CS,选中设备 dc.off() # DC=0,表示接下来是命令 spi.write(bytes([cmd])) cs.on() # 完成通信,释放总线 def write_data(buf): cs.off() dc.on() # DC=1,表示接下来是数据 spi.write(buf) cs.on()

✅ 最佳实践:每次通信后立即拉高CS,避免干扰其他 SPI 设备。

这两个函数构成了整个驱动的基石。后续所有绘图、清屏、设窗口操作,都是它们的组合调用。


如何真正“画”出第一个像素?GRAM 访问全流程

终于到了激动人心的时刻:我们要向屏幕写入真正的图像数据。

但请注意,ST7789不会自动刷新整个屏幕。你必须明确告诉它:“我要更新哪个区域”,然后才能开始传像素。

Step 1:定义绘图窗口(CASET & RASET)

def set_window(x0, y0, x1, y1): write_cmd(0x2A) # Column Address Set write_data( bytes([x0 >> 8, x0 & 0xFF, x1 >> 8, x1 & 0xFF]) ) write_cmd(0x2B) # Row Address Set write_data( bytes([y0 >> 8, y0 & 0xFF, y1 >> 8, y1 & 0xFF]) )

这相当于划定一块“画布”。例如要更新全屏(240x240 圆形屏):

set_window(0, 0, 239, 239)

Step 2:进入“写 GRAM”模式(0x2C)

write_cmd(0x2C) # Memory Write

此后所有通过write_data()发送的数据都会被视为连续的 RGB565 像素值,并按行依次填入 GRAM。

Step 3:发送像素流

假设我们要填充整个屏幕为红色(RGB565 中红色为0xF800):

# 构造一个红色像素(大端格式) red_pixel = b'\xF8\x00' # 填充整个 240x240 屏幕(共 57,600 像素) buffer = red_pixel * (240 * 240) write_data(buffer)

⚠️ 警告:直接构造如此大的缓冲区会在内存紧张的设备(如 ESP32-S2)上引发MemoryError。生产环境中应分块发送或使用framebuf预渲染。


常见坑点与调试秘籍

❌ 白屏 / 花屏 → 检查这三个地方

  1. SPI 速率太高
    杜邦线+面包板系统建议不超过 20MHz;PCB 板载可尝试 40MHz。

  2. DC 引脚接反或未连接
    这是最常见的接线错误。可用万用表测量电平变化确认。

  3. 缺少关键延时
    尤其是在0x11(Sleep Out)之后,必须等待 ≥120ms 才能发0x29

🎨 颜色异常(偏绿/倒色)→ 查看字节序与扫描方向

  • RGB565 数据是否以大端(MSB 在前)发送?
  • 0x36命令中的MY(行反转)、MX(列反转)、MV(行列交换)位是否正确?

例如,对于常见的竖屏显示,可能需要:

write_cmd(0x36) write_data(b'\x60') # MX=1, MV=1 → 旋转 90°

🐢 刷新卡顿 → 优化策略在这里

  • 批量写入优于逐像素更新:合并多个小write_data调用
  • 使用 framebuf 模块预合成图像
import framebuf # 创建内存缓冲区 fb = framebuf.FrameBuffer(bytearray(240*240*2), 240, 240, framebuf.RGB565) fb.fill(0) # 清屏黑 fb.text("Hello!", 100, 100, 1) # 写文字 write_data(fb.buffer()) # 一次性刷屏

这种方式显著减少 SPI 事务开销,提升响应速度。


工程级建议:如何写出稳定可维护的驱动代码?

当你准备将这段代码用于正式项目时,请考虑以下几点:

1. 封装成类,提高复用性

class ST7789: def __init__(self, spi, cs, dc, rst, width=240, height=240): self.spi = spi self.cs = cs self.dc = dc self.rst = rst self.width = width self.height = height self.init_display() def write_cmd(self, cmd): ... def write_data(self, buf): ... def set_window(self, x0, y0, x1, y1): ... def fill_screen(self, color): ...

这样可以在不同项目中轻松替换引脚或分辨率。

2. 添加错误日志与降级模式

在初始化失败时尝试降低 SPI 频率重试,或切换至基本黑白模式提示用户。

3. 注意电源设计

ST7789 工作电流可达 50mA 以上,尤其在全亮白色时。建议:
- 使用独立 LDO 供电
- 并联 10μF + 0.1μF 电容滤波
- 避免与电机、继电器共用电源路径

4. PCB 布局建议

  • SPI 走线尽量短且平行
  • 远离高频信号线(如 Wi-Fi 天线)
  • RSTDC引脚加 10kΩ 上拉电阻防误触发

结语:掌握原理,才能自由创造

现在你应该已经明白,驱动一块 ST7789 屏幕并不是神秘的技术黑箱。它的每一个行为都有迹可循:

  • 通过 SPI 发送命令 → 配置内部寄存器
  • 设置地址窗口 → 锁定绘图区域
  • 写入 RGB565 数据流 → 更新显存
  • 自动刷新机制 → 输出到液晶面板

当你不再依赖“别人写的库能不能用”,而是能够根据数据手册自行调整初始化参数、修复颜色偏差、优化刷新性能时,你就真正掌握了嵌入式图形开发的核心能力。

下次当你看到一块陌生的 TFT 模块,也可以自信地说:“让我看看它的驱动型号,我能搞定。”

如果你正在做一个基于 ESP32 或 Raspberry Pi Pico 的可视化项目,不妨试试亲手点亮这块小彩屏。你会发现,原来 GUI 并不远,就在你敲下的每一行write_cmd()里。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/7 18:41:01

如何快速启用Netgear路由器Telnet:面向新手的完整指南

如何快速启用Netgear路由器Telnet:面向新手的完整指南 【免费下载链接】netgear_telnet Netgear Enable Telnet (New Crypto) 项目地址: https://gitcode.com/gh_mirrors/ne/netgear_telnet Netgear路由器的隐藏Telnet功能为网络管理员提供了深度系统管理能力…

作者头像 李华
网站建设 2026/1/8 8:24:37

FanControl终极指南:5步轻松掌握Windows风扇智能控制

FanControl终极指南:5步轻松掌握Windows风扇智能控制 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHub_Trending/fa/…

作者头像 李华
网站建设 2026/1/7 2:16:15

23、多智能体环境构建与游戏调试测试

多智能体环境构建与游戏调试测试 多智能体环境构建 自定义奖励函数代码 以下代码实现了与之前自定义奖励函数相同的功能: break; case AgentSoccer.PersonRole.police:ps.agentScript.AddReward(reward);break; case AgentSoccer.PersonRole.zombie:ps.agentScript.AddRe…

作者头像 李华
网站建设 2026/1/2 10:08:15

终极ARCore Unity SDK指南:5个快速上手的实用技巧

终极ARCore Unity SDK指南:5个快速上手的实用技巧 【免费下载链接】arcore-unity-sdk ARCore SDK for Unity 项目地址: https://gitcode.com/gh_mirrors/ar/arcore-unity-sdk ARCore Unity SDK为开发者提供了在Unity环境中构建增强现实应用的全套工具集。无论…

作者头像 李华
网站建设 2026/1/11 8:38:34

Windows系统性能终极优化指南:让电池续航翻倍的完整教程

Windows系统性能终极优化指南:让电池续航翻倍的完整教程 【免费下载链接】EnergyStarX 🔋Improve your Windows 11 devices battery life. A WinUI 3 GUI for https://github.com/imbushuo/EnergyStar. 项目地址: https://gitcode.com/gh_mirrors/en/E…

作者头像 李华
网站建设 2025/12/25 8:02:55

sd-webui-controlnet快速入门:7步掌握AI绘画精准控制技巧

sd-webui-controlnet快速入门:7步掌握AI绘画精准控制技巧 【免费下载链接】sd-webui-controlnet WebUI extension for ControlNet 项目地址: https://gitcode.com/gh_mirrors/sd/sd-webui-controlnet 想要让AI绘画完全听从你的创意指挥吗?sd-webu…

作者头像 李华