news 2026/4/18 5:03:15

ST7735命令集解析及其在STM32中的调用方式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ST7735命令集解析及其在STM32中的调用方式

深入理解ST7735命令集:从寄存器操作到STM32实战驱动

你有没有遇到过这样的情况?明明代码烧录成功,SPI通信也看似正常,但屏幕就是一片白屏、花屏,或者显示方向完全错乱。在调试这类问题时,很多人第一反应是“换库”或“照抄例程”,结果却治标不治本。

其实,根本原因往往出在对ST7735 命令集机制的理解不足上。这款被广泛用于1.8英寸TFT屏的控制器,并不像GPIO外设那样“即写即显”。它的一切行为——从初始化、旋转、色彩格式设置,到最终像素呈现——都依赖于一套精确的命令-数据协议。只有掌握这套底层语言,才能真正掌控显示效果,而不是被“黑盒驱动”牵着鼻子走。

本文将带你彻底拆解 ST7735 的工作原理,结合 STM32 实际工程场景,手把手教你如何通过直接调用命令寄存器,构建一个高效、稳定、可移植的LCD驱动框架。


为什么必须读懂ST7735的“命令语言”?

ST7735 不是一个简单的内存映射设备。它更像是一个拥有自己“操作系统”的微型处理器,而我们主控MCU的任务,就是按照它的“语法规则”发送指令和参数。

命令与数据:D/C 引脚决定一切

关键就在于那个不起眼的D/C(Data/Command)引脚。这个引脚的状态,决定了SPI总线上接下来传输的是“命令”还是“数据”。

  • D/C = 0:告诉ST7735:“下一个字节是你要执行的操作码。”
  • D/C = 1:告诉ST7735:“接下来的是参数或像素数据。”

比如,你想设置屏幕显示方向,流程如下:
1. 拉低片选(CS)
2. 设置 D/C = 0 → 表示要发命令
3. 发送命令字节0x36(MADCTL,内存访问控制)
4. 设置 D/C = 1 → 表示要发数据
5. 发送参数字节(如0xC0表示180°旋转)
6. 拉高CS,结束

这种机制虽然增加了软件复杂度,但也带来了极高的灵活性——你可以精细控制每一个显示参数。

常见痛点:为什么“照搬例程”常翻车?

很多初学者直接使用别人封装好的驱动库,一旦换一块屏幕或MCU型号稍有不同,就出现白屏、乱码等问题。究其原因:

  • 初始化序列中某个延时不够;
  • MADCTL 配置与实际屏幕贴合方向不符;
  • SPI 模式(CPOL/CPHA)配置错误;
  • 忘记等待睡眠退出后的稳定时间(≥120ms)。

这些问题,只有当你真正理解每一条命令的作用,才能快速定位并修复。


核心命令集速览:ST7735最常用的几个“动词”

不必死记所有100多个命令,先掌握这几个最关键的,就能解决90%的日常需求。

命令 (Hex)名称功能说明是否需要参数
0x01SWRESET软件复位,重启控制器
0x11SLPOUT退出睡眠模式
0x29DISPON开启显示
0x2ACASET设置列地址范围(X轴)是(4字节)
0x2BRASET设置行地址范围(Y轴)是(4字节)
0x2CRAMWR写GRAM,开始传输像素数据是(N×2字节)
0x36MADCTL控制显示方向、镜像、RGB/BGR顺序是(1字节)
0x3ACOLMOD设置色彩格式(如16位RGB565)是(1字节)
0xB1FRMCTR1帧率控制(普通模式)是(3字节)

⚠️ 注意:部分命令后必须加延时!例如SLPOUT后需至少延时120ms才能继续发送其他命令,否则可能导致初始化失败。


在STM32上搭建驱动骨架:从硬件连接到基础函数

我们以 STM32F103 系列为例,使用 HAL 库 + SPI1 实现驱动。

硬件连接(SPI四线模式)

ST7735 引脚连接 STM32 引脚功能说明
SCKPB3 (SPI1_SCK)时钟信号
MOSIPB5 (SPI1_MOSI)主机输出
CSPC3片选,低电平有效
DCPC2数据/命令选择
RESPC1复位,低电平有效
VCC/GND3.3V/GND供电
BLKPC4 (PWM)背光控制(可选)

✅ 推荐使用硬件SPI,避免GPIO模拟带来的时序抖动。

SPI 配置要点

// SPI1 初始化(Mode 0, 10MHz 初始频率) hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL=0 hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA=0 hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; // 72MHz/8 = 9MHz

🔔 提示:初始化阶段建议使用较低频率(≤10MHz),待屏幕稳定后可通过修改预分频器提升至 20~27MHz 以提高刷新率。


封装核心传输函数:让命令调用更清晰

为了提高代码可读性和复用性,我们需要封装两个基本操作函数。

#include "stm32f1xx_hal.h" // 引脚宏定义(根据实际电路调整) #define TFT_CS_PORT GPIOC #define TFT_CS_PIN GPIO_PIN_3 #define TFT_DC_PORT GPIOC #define TFT_DC_PIN GPIO_PIN_2 #define TFT_RES_PORT GPIOC #define TFT_RES_PIN GPIO_PIN_1 // 快捷宏 #define SELECT() HAL_GPIO_WritePin(TFT_CS_PORT, TFT_CS_PIN, GPIO_PIN_RESET) #define DESELECT() HAL_GPIO_WritePin(TFT_CS_PORT, TFT_CS_PIN, GPIO_PIN_SET) #define CMD() HAL_GPIO_WritePin(TFT_DC_PORT, TFT_DC_PIN, GPIO_PIN_RESET) #define DATA() HAL_GPIO_WritePin(TFT_DC_PORT, TFT_DC_PIN, GPIO_PIN_SET) extern SPI_HandleTypeDef hspi1; /** * @brief 发送单个命令字节 */ void lcd_write_cmd(uint8_t cmd) { SELECT(); CMD(); HAL_SPI_Transmit(&hspi1, &cmd, 1, 100); DESELECT(); } /** * @brief 发送多个数据字节 */ void lcd_write_data(uint8_t *buf, size_t len) { SELECT(); DATA(); HAL_SPI_Transmit(&hspi1, buf, len, HAL_MAX_DELAY); DESELECT(); } /** * @brief 发送带参数的命令(常用组合) */ void lcd_send_command(uint8_t cmd, uint8_t *data, size_t len) { lcd_write_cmd(cmd); if (len > 0) { lcd_write_data(data, len); } }

这些函数构成了整个驱动的基础。后续所有高级功能都将基于它们实现。


初始化流程详解:别再盲目复制粘贴了!

很多开发者直接把网上找来的初始化序列扔进项目,却不明白每一行的意义。下面是一段经过验证的ST7735S 初始化代码,并附带详细注释。

void st7735_init(void) { // === 步骤1:硬件复位 === HAL_GPIO_WritePin(TFT_RES_PORT, TFT_RES_PIN, GPIO_PIN_RESET); HAL_Delay(10); // 至少保持10ms低电平 HAL_GPIO_WritePin(TFT_RES_PORT, TFT_RES_PIN, GPIO_PIN_SET); HAL_Delay(120); // 等待内部电路稳定 // === 步骤2:退出睡眠模式 === lcd_write_cmd(0x11); // SLPOUT HAL_Delay(120); // 关键!必须等待 ≥120ms // === 步骤3:帧率设置(正常模式)=== uint8_t frmctr1[] = {0x01, 0x2C, 0x2D}; lcd_send_command(0xB1, frmctr1, 3); // === 步骤4:显示反转控制 === uint8_t invctrl[] = {0x07}; lcd_send_command(0xB4, invctrl, 1); // 改变部分行反转 // === 步骤5:电源控制设置 === uint8_t pwctrl[] = {0xA2, 0x02, 0x84}; // AVDD=4.6V, AVEE=-4.6V, VDS=2.6V lcd_send_command(0xC0, pwctrl, 3); // === 步骤6:VCOM控制 === uint8_t vmctrl[] = {0x0A, 0x00}; lcd_send_command(0xC5, vmctrl, 2); // === 步骤7:设置MADCTL(关键!控制显示方向)=== lcd_write_cmd(0x36); uint8_t madctl = 0xC0; // MY=0, MX=1, MV=1, ML=0 → 180°旋转 lcd_write_data(&madctl, 1); // === 步骤8:设置色彩格式为16位(RGB565)=== lcd_write_cmd(0x3A); uint8_t colmod = 0x05; lcd_write_data(&colmod, 1); // === 步骤9:使能内部升压电路 === lcd_write_cmd(0xF2); uint8_t enable_tf = {0x02}; lcd_write_data(&enable_tf, 1); // === 步骤10:伽马校正设置(典型值)=== uint8_t gamma_p[] = {0x05, 0x3C, 0x30, 0x3C, 0x0F, 0x0F, 0x33, 0x33}; uint8_t gamma_n[] = {0x00, 0x3C, 0x30, 0x3C, 0x0F, 0x0F, 0x33, 0x33}; lcd_send_command(0xE0, gamma_p, 8); // 正极性 lcd_send_command(0xE1, gamma_n, 8); // 负极性 // === 步骤11:开启显示 === lcd_write_cmd(0x29); // DISPON }

📌重点说明
-0x36(MADCTL)是控制显示方向的核心。常见取值如下:
-0x00:0°(默认)
-0x60:90°
-0xC0:180°
-0xA0:270°
- 若你的屏幕显示倒置或左右翻转,优先检查此项。
- 伽马设置影响色彩饱和度和对比度,可根据视觉效果微调。


实战:绘制一个像素点

有了初始化和基础函数,就可以开始绘图了。以下是绘制单个像素的核心逻辑。

void st7735_draw_pixel(uint8_t x, uint8_t y, uint16_t color) { // 设置列地址(CASET) lcd_write_cmd(0x2A); uint8_t col_start[4] = {0x00, x + 2, 0x00, x + 2}; // 补偿偏移 lcd_write_data(col_start, 4); // 设置行地址(RASET) lcd_write_cmd(0x2B); uint8_t row_start[4] = {0x00, y + 1, 0x00, y + 1}; lcd_write_data(row_start, 4); // 写入像素数据(RAMWR) lcd_write_cmd(0x2C); uint8_t pixel[2] = {color >> 8, color & 0xFF}; // RGB565 分高低字节 lcd_write_data(pixel, 2); }

⚠️ 注意:很多ST7735模块存在坐标偏移(如原点在(2,1)处),因此需要在x+2,y+1进行补偿。具体数值请参考模块规格书。


常见问题与调试秘籍

❌ 白屏/花屏怎么办?

  1. 查复位:确认RES引脚是否有足够宽度的低脉冲(≥10ms);
  2. 查时序:SPI是否为 Mode 0(CPOL=0, CPHA=0);
  3. 查延时0x11后是否延时 ≥120ms;
  4. 查电压:VCC是否稳定在3.3V?建议并联0.1μF陶瓷电容;
  5. 查接线:MOSI/SCK是否接反?CS是否接地导致一直选中?

🔄 显示方向不对?

修改MADCTL参数即可。推荐封装一个函数:

void st7735_set_rotation(uint8_t rotation) { static const uint8_t values[] = {0x00, 0x60, 0xC0, 0xA0}; lcd_write_cmd(0x36); lcd_write_data(&values[rotation % 4], 1); }

🐢 刷新太慢怎么优化?

  • 启用DMA:对于大块区域填充,使用DMA传输像素数据,释放CPU;
  • 提高SPI频率:在保证信号质量前提下,将SPI时钟提升至20MHz以上;
  • 局部刷新:只更新变化区域,避免全屏重绘;
  • 关闭不必要的动画效果:如滚动、渐变等。

高级技巧:迈向高性能显示系统

掌握了基础之后,可以进一步优化:

使用DMA批量传输像素

HAL_SPI_Transmit_DMA(&hspi1, (uint8_t*)pixel_buffer, length);

配合双缓冲机制,可实现流畅动画。

封装命令序列数组

将初始化命令封装为结构体数组,便于管理和调试输出:

typedef struct { uint8_t cmd; const uint8_t *data; uint8_t len; uint8_t delay_ms; } lcd_init_cmd_t; static const lcd_init_cmd_t init_cmds[] = { {0x11, NULL, 0, 120}, {0xB1, (uint8_t[]){0x01,0x2C,0x2D}, 3, 0}, {0x36, (uint8_t[]){0xC0}, 1, 0}, ... };

这样可以在不同屏幕间轻松切换配置。


如果你正在开发一款基于STM32的小型HMI设备,无论是智能仪表、传感器终端还是DIY电子玩具,掌握ST7735的命令级操作能力,将让你摆脱对第三方库的依赖,真正做到“知其然,更知其所以然”。

当你下次面对一块新买的TFT屏时,不再需要到处搜索“适配代码”,而是能够根据数据手册,独立写出可靠的初始化流程——这才是嵌入式开发的魅力所在。

欢迎在评论区分享你在驱动LCD过程中踩过的坑,我们一起讨论解决方案!

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

提示词工程进阶:prompt中LoRA强度(0~1)调节技巧

提示词工程进阶:LoRA强度(0~1)调节的艺术与科学 在AI生成内容的实践中,我们常遇到这样的困境:明明训练了一个风格鲜明的LoRA模型,可一到推理阶段,要么“毫无反应”,要么“彻底失控”…

作者头像 李华
网站建设 2026/4/18 2:25:55

GitHub镜像站推荐:快速获取lora-scripts源码与依赖库

GitHub镜像站推荐:快速获取lora-scripts源码与依赖库 在生成式AI迅猛发展的今天,越来越多开发者希望基于Stable Diffusion或大语言模型(LLM)训练自己的定制化模型。然而,面对复杂的训练流程和国内访问GitHub不稳定的问…

作者头像 李华
网站建设 2026/4/18 3:58:40

negative_prompt应用实例:规避低质量、模糊图像生成

negative_prompt应用实例:规避低质量、模糊图像生成 在如今的AI图像生成实践中,哪怕是最先进的模型也时常“翻车”——刚跑出一张赛博朋克城市夜景,结果放大一看:建筑边缘糊成一团,路灯周围泛着诡异噪点,人…

作者头像 李华
网站建设 2026/4/17 20:18:14

任务类型切换配置:text-generation在LLM微调中的设定

任务类型切换配置:text-generation在LLM微调中的设定 在大模型落地的浪潮中,一个现实问题摆在许多团队面前:如何用有限的算力资源,把通用语言模型变成懂行的“专家”?全参数微调动辄需要数张A100,对大多数开…

作者头像 李华
网站建设 2026/4/18 3:59:17

理想L系列交互界面:lora-scripts设计动态元素

理想L系列交互界面中的动态元素生成:基于 lora-scripts 与 LoRA 的轻量化AI实践 在智能汽车的竞争逐渐从“硬件堆料”转向“体验定义”的今天,理想L系列所追求的已不仅是续航或算力,而是如何让每一次语音唤醒、每一块界面动效都传递出品牌的温…

作者头像 李华