4. 基本像素写入:写像素命令、行列地址设置、写GRAM、单像素刷新实现

在驱动 ST7789 显示器的过程中,最基础也是最核心的操作就是向指定坐标写入一个像素的颜色值。本章将深入剖析单像素刷新的完整链路,从命令协议到寄存器操作,再到代码实现,帮助您建立对像素级控制的精确理解。

4.1 像素写入的完整流程

向 ST7789 写入一个像素,并非直接发送颜色数据那么简单。控制器内部维护了一个自增的“写指针”,指向当前要写入的 GRAM 地址。因此,写入像素前必须完成以下三个步骤:

  1. 设置行列地址范围:通过命令 CASET (2Ah) 和 RASET (2Bh) 定义目标像素的矩形区域。
  2. 启动写 GRAM 命令:发送 RAMWR (2Ch) 命令,通知控制器后续数据将写入 GRAM。
  3. 传输像素颜色数据:按照 RGB565 格式,连续发送 2 字节(或 3 字节,取决于接口模式)的颜色数据。
关键概念: ST7789 的 GRAM 写指针在每次写入后自动递增。即使只写一个像素,也必须先通过 CASET/RASET 将地址范围限定为单个点,否则写指针会继续向后填充,导致非预期区域的像素被修改。

4.2 核心命令详解

下表列出了单像素写入所涉及的三个核心命令及其参数格式:

命令名称 命令码 参数长度 参数说明
CASET (Column Address Set) 0x2A 4 字节 起始列 (SC) [15:0],结束列 (EC) [15:0]
RASET (Row Address Set) 0x2B 4 字节 起始行 (SR) [15:0],结束行 (ER) [15:0]
RAMWR (Memory Write) 0x2C N 字节 RGB565 颜色数据,每个像素 2 字节

4.3 行列地址设置策略

对于单像素写入,行列地址范围必须设置为一个 1x1 的窗口。假设目标坐标为 (x, y),分辨率为 240x240:

  • CASET 参数:起始列 = x,结束列 = x
  • RASET 参数:起始行 = y,结束行 = y

这种设置方式将 GRAM 写指针锁定在唯一的地址上,确保后续写入的颜色数据只影响该像素。

常见错误: 许多初学者在连续写入多个像素时,忘记重新设置 CASET/RASET,导致写指针溢出到下一行或超出显示区域。务必在每次像素写入前重新定义窗口范围。

4.4 单像素刷新实现代码

以下代码展示了在 4-wire SPI 接口下,使用 MicroPython 实现单像素写入的完整函数。代码中包含了必要的延时和命令序列:

import machine
import time

# 假设已初始化 SPI、DC、CS、RST 引脚
# spi: machine.SPI 对象
# dc: 数据/命令控制引脚
# cs: 片选引脚

def write_cmd(cmd):
    """发送命令字节"""
    cs.value(0)
    dc.value(0)  # 命令模式
    spi.write(bytes([cmd]))
    cs.value(1)

def write_data(data):
    """发送数据字节"""
    cs.value(0)
    dc.value(1)  # 数据模式
    spi.write(bytes([data]))
    cs.value(1)

def set_window(x, y):
    """设置单像素窗口"""
    # CASET: 列地址范围
    write_cmd(0x2A)
    write_data(x >> 8)   # 起始列高字节
    write_data(x & 0xFF)  # 起始列低字节
    write_data(x >> 8)   # 结束列高字节
    write_data(x & 0xFF)  # 结束列低字节

    # RASET: 行地址范围
    write_cmd(0x2B)
    write_data(y >> 8)   # 起始行高字节
    write_data(y & 0xFF)  # 起始行低字节
    write_data(y >> 8)   # 结束行高字节
    write_data(y & 0xFF)  # 结束行低字节

def draw_pixel(x, y, color):
    """在 (x, y) 处绘制一个 RGB565 颜色像素"""
    # 1. 设置窗口
    set_window(x, y)

    # 2. 启动 RAMWR 命令
    write_cmd(0x2C)

    # 3. 发送颜色数据 (RGB565)
    # color 为 16 位整数,例如 0xF800 表示红色
    write_data((color >> 8) & 0xFF)  # 高字节
    write_data(color & 0xFF)          # 低字节

# 使用示例:在 (100, 100) 处画一个红色像素
draw_pixel(100, 100, 0xF800)

4.5 像素写入流程可视化

下图展示了从设置地址到数据写入的完整时序与数据流关系:

单像素写入流程与数据流 主机 (MCU) CASET (0x2A) RASET (0x2B) RAMWR (0x2C) GRAM 写入 像素刷新显示 流程说明: ① 主机依次发送 CASET 和 RASET 命令及参数,锁定 1x1 像素窗口。 ② 发送 RAMWR 命令,随后传输 2 字节 RGB565 颜色数据,GRAM 写指针自动递增。

4.6 性能考量与优化方向

单像素写入虽然简单,但效率极低。每次调用 draw_pixel() 都需要发送 3 条命令和至少 6 字节参数,再加上 2 字节颜色数据。对于 240x240 的屏幕,全屏刷新需要 240×240 = 57,600 次像素写入,总数据传输量巨大。

  • 瓶颈分析:命令开销远大于数据开销。每次像素写入的协议开销(命令+参数)约 8 字节,而有效颜色数据仅 2 字节,效率仅为 20%。
  • 优化思路:通过设置大窗口(如整行或整块区域),利用 GRAM 写指针自动递增特性,批量发送颜色数据,大幅降低命令发送次数。