news 2026/4/18 8:26:59

利用Keil实现STM32对ST7735的精准控制教程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
利用Keil实现STM32对ST7735的精准控制教程

STM32驱动ST7735:从SPI通信到精准显示的实战全解析

你有没有遇到过这样的情况——手里的1.8寸TFT屏买回来后,代码一烧录,屏幕要么全白、要么花屏乱码?明明参考了网上的例程,为什么就是出不来想要的效果?

如果你正在用STM32控制ST7735这款常见的TFT控制器芯片,那你很可能正卡在初始化序列、SPI时序或颜色格式这些“看不见”的细节上。别急,这并不是你的问题,而是因为这类显示驱动看似简单,实则处处是坑。

今天我们就以实际项目经验为基础,结合Keil开发环境,带你一步步打通STM32驱动ST7735的技术链路。不讲空话,只讲你能用得上的硬核内容:硬件连接怎么接、寄存器怎么配、初始化为何要加延时、RGB565到底是大端还是小端……全部掰开揉碎,让你真正掌握“精准控制”的能力。


为什么选ST7735?一个小而强的显示方案

在嵌入式系统中,图形界面早已不再是奢侈功能。无论是智能温控器、便携仪表,还是学生实验平台,一块能显示图标和数据的小屏幕,能让产品体验提升一个档次。

而在这类应用中,ST7735是极具性价比的选择。它不是最大最亮的,但足够小巧、便宜、省资源:

  • 分辨率:128×160(常用区域),支持最高132×162
  • 色深:16位色(RGB565),即65K色,色彩表现足够日常使用
  • 接口灵活:支持四线SPI、三线SPI、甚至并行8080模式
  • 引脚少:仅需6个IO即可完成基本控制(SCK、MOSI、CS、DC、RES、VCC/GND)
  • 内置升压电路:无需外部电荷泵即可驱动LCD偏压
  • 成本低:整块模块价格普遍低于2美元

相比动辄需要几十KB RAM的ILI9341(240×320),ST7735对MCU资源更友好,特别适合像STM32F103C8T6这种仅有20KB SRAM的入门级芯片。


硬件怎么连?一张图说清楚

先解决最基础的问题:接线。

我们采用最常见的四线SPI + 控制引脚方式,与STM32进行通信。假设使用的是STM32F1系列MCU,以下是推荐连接方式:

ST7735 引脚功能说明连接到 MCU
SCL / SCKSPI时钟PA5 (SPI1_SCK)
SDA / MOSI数据输出PA7 (SPI1_MOSI)
CS片选(低有效)PB2
DC数据/命令选择PB1
RES复位信号PB0
VCC电源3.3V
GNDGND

⚠️ 注意:部分模块标称为“SDA”,但在SPI模式下其实就是MOSI;不要误以为是I2C!

虽然ST7735也支持硬件SPI NSS,但我们通常使用软件片选(NSS=SOFT),以便更好地控制CS拉低时机。

此外,强烈建议在电源端加入去耦电容组合(10μF钽电容 + 0.1μF陶瓷电容),避免因电源波动导致显示抖动或复位失败。


SPI通信配置:Mode 0才是关键

ST7735要求SPI工作在Mode 0:即空闲时SCLK为低电平(CPOL=0),数据在第一个上升沿采样(CPHA=0)。这是很多初学者忽略的核心点。

如果配置成Mode 3或其他模式,可能也能点亮屏幕,但容易出现数据错位、花屏等问题。

下面是基于HAL库的标准SPI1初始化代码(适用于STM32F1系列):

static void MX_SPI1_Init(void) { hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_1LINE; 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; // 软件管理CS hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; // APB2=72MHz → SCLK≈18MHz hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = DISABLE; hspi1.Init.CRCCalculation = DISABLE; HAL_SPI_Init(&hspi1); }

📌经验提示:尽管ST7735官方文档标明最高支持15MHz时钟,但初期调试建议降至10MHz以下(如预分频设为8),可显著提高通信稳定性。等一切正常后再逐步提速。


关键控制信号抽象:宏定义让代码更清晰

为了提升可读性和移植性,建议将DC、CS、RES三个控制信号封装为宏操作:

#define TFT_CS_PIN GPIO_PIN_2 #define TFT_CS_PORT GPIOB #define TFT_DC_PIN GPIO_PIN_1 #define TFT_DC_PORT GPIOB #define TFT_RES_PIN GPIO_PIN_0 #define TFT_RES_PORT GPIOB // 快速操作宏 #define TFT_CS_LOW() HAL_GPIO_WritePin(TFT_CS_PORT, TFT_CS_PIN, GPIO_PIN_RESET) #define TFT_CS_HIGH() HAL_GPIO_WritePin(TFT_CS_PORT, TFT_CS_PIN, GPIO_PIN_SET) #define TFT_DC_CMD() HAL_GPIO_WritePin(TFT_DC_PORT, TFT_DC_PIN, GPIO_PIN_RESET) #define TFT_DC_DATA() HAL_GPIO_WritePin(TFT_DC_PORT, TFT_DC_PIN, GPIO_PIN_SET) #define TFT_RES_LOW() HAL_GPIO_WritePin(TFT_RES_PORT, TFT_RES_PIN, GPIO_PIN_RESET) #define TFT_RES_HIGH() HAL_GPIO_WritePin(TFT_RES_PORT, TFT_RES_PIN, GPIO_PIN_SET)

这样做的好处是,在后续发送命令或数据时逻辑非常清晰:

TFT_CS_LOW(); // 开始传输 TFT_DC_CMD(); // 下一条是命令 HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY); TFT_CS_HIGH(); // 结束传输

命令与数据分离:理解DC引脚的作用

这是许多新手最容易混淆的地方:DC引脚决定了当前传输的是命令还是数据

  • DC = 0:表示接下来发送的是命令字节(比如0x2A设置列地址)
  • DC = 1:表示接下来发送的是数据内容(比如像素值、参数)

例如:

ST7735_WriteCommand(0x2A); // 发送命令:设置列地址 ST7735_WriteData(0x00); // 参数:起始列高字节 ST7735_WriteData(0x00); // 参数:起始列低字节 ST7735_WriteData(0x00); ST7735_WriteData(0x7F); // 结束列 = 127

对应的底层函数实现如下:

void ST7735_WriteCommand(uint8_t cmd) { TFT_CS_LOW(); TFT_DC_CMD(); HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY); TFT_CS_HIGH(); } void ST7735_WriteData(uint8_t data) { TFT_CS_LOW(); TFT_DC_DATA(); HAL_SPI_Transmit(&hspi1, &data, 1, HAL_MAX_DELAY); TFT_CS_HIGH(); } void ST7735_WriteBuffer(uint8_t *buffer, size_t len) { TFT_CS_LOW(); TFT_DC_DATA(); HAL_SPI_Transmit(&hspi1, buffer, len, HAL_MAX_DELAY); TFT_CS_HIGH(); }

注意:批量写入时尽量使用WriteBuffer减少CS频繁切换,否则会影响刷新效率。


初始化序列详解:别再盲目复制粘贴

网上很多ST7735驱动代码都直接复制某份GitHub项目的初始化函数,结果换了块屏就出问题。根本原因在于:不同厂商的模组初始化序列存在差异!

尤其是ST7735R和ST7735S,虽然型号接近,但某些Gamma设置和电压参数完全不同。

下面是一个经过验证的ST7735R 初始化流程(含必要延时):

void ST7735_Init(void) { // 硬件复位 TFT_RES_LOW(); HAL_Delay(10); TFT_RES_HIGH(); HAL_Delay(120); // 退出睡眠模式 ST7735_WriteCommand(0x11); HAL_Delay(120); // 颜色格式设置为RGB565 ST7735_WriteCommand(0x3A); ST7735_WriteData(0x05); // 16-bit/pixel // 液晶驱动设置 ST7735_WriteCommand(0xB1); ST7735_WriteData(0x01); ST7735_WriteData(0x2C); ST7735_WriteData(0x2D); ST7735_WriteCommand(0xB2); ST7735_WriteData(0x01); ST7735_WriteData(0x2C); ST7735_WriteData(0x2D); ST7735_WriteCommand(0xB3); ST7735_WriteData(0x01); ST7735_WriteData(0x2C); ST7735_WriteData(0x2D); ST7735_WriteData(0x01); ST7735_WriteData(0x2C); ST7735_WriteData(0x2D); // VOP设置(模拟偏压) ST7735_WriteCommand(0xB4); ST7735_WriteData(0x07); // 正常显示模式 ST7735_WriteCommand(0xC0); ST7735_WriteData(0xA2); ST7735_WriteData(0x02); ST7735_WriteData(0x84); ST7735_WriteData(0xC0); ST7735_WriteData(0x0D); ST7735_WriteData(0x00); ST7735_WriteCommand(0xC1); ST7735_WriteData(0x0A); ST7735_WriteData(0x00); ST7735_WriteCommand(0xC2); ST7735_WriteData(0x02); ST7735_WriteData(0x02); ST7735_WriteCommand(0xC3); ST7735_WriteData(0x11); ST7735_WriteData(0x11); ST7735_WriteCommand(0xC4); ST7735_WriteData(0x0F); // 设置GVDD(模拟供电) ST7735_WriteCommand(0xC5); ST7735_WriteData(0x41); // 设置旋转方向(默认0度) ST7735_WriteCommand(0x36); ST7735_WriteData(0x08); // MY=0, MX=0, MV=0, ML=0 → 0° // 默认伽马校正 ST7735_WriteCommand(0xE0); uint8_t gammaP[] = {0x0F,0x1A,0x0F,0x18,0x2F,0x28,0x20,0x22,0x1D,0x1B,0x23,0x37,0x00,0x07,0x02,0x10}; ST7735_WriteBuffer(gammaP, sizeof(gammaP)); ST7735_WriteCommand(0xE1); uint8_t gammaN[] = {0x0F,0x1B,0x0F,0x17,0x33,0x2C,0x29,0x2E,0x30,0x30,0x39,0x3F,0x00,0x07,0x03,0x10}; ST7735_WriteBuffer(gammaN, sizeof(gammaN)); // 开启显示 ST7735_WriteCommand(0x29); HAL_Delay(10); }

🔍重点提醒
- 所有延时不可随意删除,尤其在复位和退出睡眠后必须等待足够时间;
-0x36寄存器用于设置显示方向,可通过修改其值实现横屏/竖屏切换;
- Gamma表(E0/E1)直接影响色彩还原效果,不同批次屏幕可能需要微调。


如何画图?从填充矩形开始

有了初始化,下一步就是输出内容。最基础的操作是区域填充

设置地址窗口

要向指定区域写像素,必须先设定GRAM的访问范围:

void ST7735_SetWindow(uint8_t xSt, uint8_t ySt, uint8_t xEd, uint8_t yEd) { ST7735_WriteCommand(0x2A); // Column Address Set ST7735_WriteData(0x00); ST7735_WriteData(xSt + 2); // 偏移补偿(部分模块需+2) ST7735_WriteData(0x00); ST7735_WriteData(xEd + 2); ST7735_WriteCommand(0x2B); // Page Address Set ST7735_WriteData(0x00); ST7735_WriteData(ySt + 1); ST7735_WriteData(0x00); ST7735_WriteData(yEd + 1); }

📌 注意:有些ST7735模块存在2列偏移问题,因此X坐标需整体加2才能对齐可视区域。

写入像素流

接着发送0x2C命令进入显存写入模式,并连续发送RGB565数据:

void ST7735_FillScreen(uint16_t color) { uint32_t i; uint8_t hi = color >> 8, lo = color; ST7735_SetWindow(0, 0, 127, 159); ST7735_WriteCommand(0x2C); TFT_CS_LOW(); TFT_DC_DATA(); uint8_t colorBuf[2] = {hi, lo}; for (i = 0; i < 128 * 160; i++) { HAL_SPI_Transmit(&hspi1, colorBuf, 2, HAL_MAX_DELAY); } TFT_CS_HIGH(); }

优化建议:若启用DMA,可用HAL_SPI_Transmit_DMA实现零CPU占用刷屏。


常见问题排查指南

别慌,这些问题我们都经历过:

现象可能原因解决方法
屏幕全白/黑初始化未完成或顺序错误对照手册逐条检查命令,确保延时到位
显示偏移或裁剪地址窗口设置错误检查是否添加了+2偏移,确认CASET/PASET参数
颜色发红或倒置RGB/BGR混淆查看模块类型,部分为BGR565,需交换R/B通道
刷新慢使用轮询SPI且无DMA启用DMA传输,或改用FSMC/FlexBus驱动更大屏
花屏闪烁SPI速率过高或电源噪声大降低波特率至10MHz,增加滤波电容

🛠️调试技巧
- 使用逻辑分析仪抓取SPI波形,确认SCK、MOSI、CS、DC时序正确;
- 在Keil中设置断点,逐步执行初始化流程,观察哪一步之后屏幕有反应;
- 编写最小测试程序,仅做清屏操作,排除其他干扰。


Keil工程中的实践优势

虽然现在很多人转向STM32CubeIDE,但Keil MDK在工业项目中依然广受青睐,尤其在稳定性和编译优化方面表现突出。

在Keil环境下开发ST7735驱动的优势包括:

  • 编译效率高:Arm Compiler生成的代码体积小、运行快;
  • 调试体验流畅:支持实时变量监视、内存查看、函数栈追踪;
  • 兼容性强:完美支持J-Link、ST-Link等主流调试器;
  • 启动文件完善:无需手动编写汇编启动代码;
  • 中断响应快:对ISR优化良好,适合实时性要求高的场景。

主函数典型结构如下:

int main(void) { HAL_Init(); SystemClock_Config(); // 72MHz MX_GPIO_Init(); MX_SPI1_Init(); ST7735_Init(); while (1) { ST7735_FillScreen(0xF800); // 红色 HAL_Delay(1000); ST7735_FillScreen(0x001F); // 蓝色 HAL_Delay(1000); } }

通过Keil的“Build”窗口可以清楚看到Flash和RAM占用情况,便于评估资源是否超限。


更进一步:帧缓冲与DMA加速

如果你要做动画或图形界面,仅靠CPU轮询刷屏显然不够高效。

两个进阶方向值得尝试:

1. 帧缓冲区(Framebuffer)

开辟一块40KB左右的内存作为离屏缓存:

uint16_t frame_buffer[128][160]; // 占用约40KB RAM

所有绘图操作先在此缓冲区完成,最后统一刷到屏幕。优点是可以避免画面撕裂,支持双缓冲机制。

⚠️ 注意:STM32F103RCT6及以上才具备足够SRAM,C8T6等小容量型号慎用。

2. SPI DMA传输

利用DMA将像素数据自动搬移到SPI外设,释放CPU资源:

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

配合DMA完成回调函数,可实现流水线式刷新。


写在最后:掌握底层,才能自由发挥

驱动一块小小的TFT屏,背后涉及的知识远不止“调用API”那么简单。只有真正理解了SPI通信机制、命令时序、GRAM管理、颜色格式等底层原理,你才能做到:

  • 快速定位问题,而不是靠“换例程”碰运气;
  • 自主适配不同品牌模组,不再被“兼容性”束缚;
  • 优化性能,榨干每一滴MCU资源;
  • 为未来接入LVGL、TouchGFX等GUI框架打下坚实基础。

当你能在Keil里亲手点亮第一块屏幕,并让红蓝交替闪烁时,那种成就感,正是嵌入式开发的魅力所在。

如果你也在用STM32驱动ST7735,欢迎在评论区分享你的踩坑经历或优化技巧。一起交流,少走弯路。

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

如何用vgmstream轻松解码游戏音频:从零开始完整指南

如何用vgmstream轻松解码游戏音频&#xff1a;从零开始完整指南 【免费下载链接】vgmstream vgmstream - A library for playback of various streamed audio formats used in video games. 项目地址: https://gitcode.com/gh_mirrors/vg/vgmstream vgmstream是一个强大…

作者头像 李华
网站建设 2026/4/16 15:38:56

Obsidian模板终极实战手册:7步打造你的第二大脑系统

Obsidian模板终极实战手册&#xff1a;7步打造你的第二大脑系统 【免费下载链接】Obsidian-Templates A repository containing templates and scripts for #Obsidian to support the #Zettelkasten method for note-taking. 项目地址: https://gitcode.com/gh_mirrors/ob/Ob…

作者头像 李华
网站建设 2026/4/18 1:28:57

Prometheus监控告警不及时?教你5步实现容器健康状态实时感知

第一章&#xff1a;Prometheus监控告警不及时&#xff1f;重新理解容器健康状态感知的挑战在现代云原生架构中&#xff0c;Prometheus 作为主流监控系统&#xff0c;广泛用于采集容器化应用的指标数据。然而&#xff0c;许多团队发现其告警响应存在延迟&#xff0c;根本原因在于…

作者头像 李华
网站建设 2026/4/17 21:51:35

AnimeGANv2实战:如何用AI打造专属二次元头像的完整教程

AnimeGANv2实战&#xff1a;如何用AI打造专属二次元头像的完整教程 1. 学习目标与前置知识 本教程将带你从零开始&#xff0c;使用 AnimeGANv2 模型实现真实照片到二次元动漫风格的转换。完成本教程后&#xff0c;你将能够&#xff1a; 理解 AnimeGANv2 的基本工作原理部署并…

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

AppleRa1n完整教程:3步搞定iOS激活锁绕过难题

AppleRa1n完整教程&#xff1a;3步搞定iOS激活锁绕过难题 【免费下载链接】applera1n icloud bypass for ios 15-16 项目地址: https://gitcode.com/gh_mirrors/ap/applera1n 你是否曾因二手iPhone的激活锁而束手无策&#xff1f;或者忘记Apple ID密码让设备变成昂贵的&…

作者头像 李华
网站建设 2026/4/18 4:26:57

智能视频格式转换器:解锁B站缓存视频的高效解决方案

智能视频格式转换器&#xff1a;解锁B站缓存视频的高效解决方案 【免费下载链接】m4s-converter 将bilibili缓存的m4s转成mp4(读PC端缓存目录) 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 还在为B站缓存视频无法播放而烦恼吗&#xff1f;那些精心收藏的…

作者头像 李华