2026/1/16 7:36:44
网站建设
项目流程
王健林亏60亿做不成一个网站,企业网站建设调查问卷,wordpress 运行慢,连接wordpress从点不亮到流畅显示#xff1a;我在STM32上用HAL库驱动ST7735的实战全记录最近在做一个基于STM32F103C8T6的小型人机界面项目#xff0c;需要接入一块1.8英寸的彩色TFT屏。市面上最常见的就是ST7735控制器的模组了——便宜、小巧、接口简单。理想很丰满#xff0c;但真正动手…从点不亮到流畅显示我在STM32上用HAL库驱动ST7735的实战全记录最近在做一个基于STM32F103C8T6的小型人机界面项目需要接入一块1.8英寸的彩色TFT屏。市面上最常见的就是ST7735控制器的模组了——便宜、小巧、接口简单。理想很丰满但真正动手才发现“接上线就能亮”只是幻想。我花了整整三天时间才把这块看似简单的屏幕从黑屏、花屏、乱码一步步调到正常显示。期间踩遍了SPI时序不对、初始化序列错配、DC引脚控制混乱等几乎所有坑。今天这篇记录不是什么高大上的技术论文而是一个普通嵌入式工程师的真实调试手记希望能帮你少走弯路。为什么选ST7735又为什么这么难搞先说结论ST7735本身是一款非常优秀的TFT驱动IC尤其适合资源有限的MCU平台。它支持RGB565格式、128×160分辨率仅需4根SPI线SCK、MOSI、CS、DC即可工作连RST都可以省掉。那问题出在哪答案是“不同厂家、不同批次、不同标签颜色”的模组内部初始化流程可能完全不同。你没看错。同样是写着“ST7735”的屏幕有“Green Tab”、“Black Tab”、“Red Tab”甚至“White Tab”版本它们的初始化命令序列差异巨大。网上随便搜一份代码很可能只能点亮某一类模组换一块就罢工。更坑的是这些信息往往藏在数据手册的角落里或者根本就没写清楚。于是我们只能靠试、靠猜、靠逻辑分析仪抓波形。硬件连接别小看这几根线我的开发环境如下MCUSTM32F103C8T6蓝 pill显示屏1.8” SPI TFT背面贴着绿色胶布即所谓“Green Tab”版接口方式四线SPI DC RST CS供电3.3V来自板载LDO连接关系如下STM32ST7735PB13 (SCK)SCKPB15 (MOSI)MOSIPB12CSPB14DCPB11RSTGNDGND3.3VVCC, LED⚠️ 注意虽然ST7735标称兼容5V信号输入但我建议全部使用3.3V系统避免电平冲突风险。如果你的主控是5V如Arduino务必加电平转换SPI配置在CubeMX中设置为Mode: MasterClock Polarity: Low (CPOL0)Clock Phase: 1 Edge (CPHA0) → 即SPI Mode 0Baud Rate Prescaler: fpclk/16 初始调试设慢点后面再提速First Bit: MSBNSS: Software (由GPIO手动控制CS)这个Mode 0很重要有些模组要求Mode 3但绝大多数Green/Black Tab版本都用Mode 0。错了直接通信失败。驱动框架设计命令和数据必须分家ST7735最核心的设计之一就是通过DC引脚区分命令和数据DC 0接下来传输的是命令字节比如0x2C表示开始写显存DC 1接下来传输的是数据内容比如像素颜色值这看起来很简单但在代码实现时很容易出错。很多人图省事在SPI传输过程中动态切换DC电平结果导致第一个字节就被误判。所以我的做法是封装两个独立函数确保每次传输前状态明确。// st7735.h #ifndef __ST7735_H #define __ST7735_H #include stm32f1xx_hal.h // 引脚定义根据实际PCB修改 #define ST7735_CS_PORT GPIOB #define ST7735_CS_PIN GPIO_PIN_12 #define ST7735_DC_PORT GPIOB #define ST7735_DC_PIN GPIO_PIN_13 #define ST7735_RST_PORT GPIOB #define ST7735_RST_PIN GPIO_PIN_11 // 模式宏 #define CMD_MODE() HAL_GPIO_WritePin(ST7735_DC_PORT, ST7735_DC_PIN, GPIO_PIN_RESET) #define DAT_MODE() HAL_GPIO_WritePin(ST7735_DC_PORT, ST7735_DC_PIN, GPIO_PIN_SET) // 片选操作 #define CS_LOW() HAL_GPIO_WritePin(ST7735_CS_PORT, ST7735_CS_PIN, GPIO_PIN_RESET) #define CS_HIGH() HAL_GPIO_WritePin(ST7735_CS_PORT, ST7735_CS_PIN, GPIO_PIN_SET) // 函数声明 void ST7735_Init(void); void ST7735_WriteCmd(uint8_t cmd); void ST7735_WriteData(uint8_t data); void ST7735_WriteBuffer(uint8_t *buffer, uint16_t len); void ST7735_SetAddressWindow(uint8_t x, uint8_t y, uint8_t w, uint8_t h); void ST7735_FillRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t color); #endif对应的底层传输函数// st7735.c #include st7735.h #include string.h extern SPI_HandleTypeDef hspi1; // 假设使用SPI1 void ST7735_WriteCmd(uint8_t cmd) { CS_LOW(); CMD_MODE(); // 先设为命令模式 HAL_SPI_Transmit(hspi1, cmd, 1, HAL_MAX_DELAY); CS_HIGH(); } void ST7735_WriteData(uint8_t data) { CS_LOW(); DAT_MODE(); // 设为数据模式 HAL_SPI_Transmit(hspi1, data, 1, HAL_MAX_DELAY); CS_HIGH(); } void ST7735_WriteBuffer(uint8_t *buffer, uint16_t len) { CS_LOW(); DAT_MODE(); HAL_SPI_Transmit(hspi1, buffer, len, HAL_MAX_DELAY); CS_HIGH(); }这里的关键点在于每次传输都完整经历“拉低CS → 设置DC → 发送 → 拉高CS”全过程。虽然效率不高但稳定性极佳特别适合调试阶段。初始化序列成败在此一举这是整个驱动中最关键的部分。ST7735上电后处于睡眠状态必须按特定顺序发送几十条寄存器配置命令才能唤醒。而不同厂商的模组所需的初始化流程天差地别。以下是我实测可用的“Green Tab”版本初始化代码适用于大多数淘宝常见绿屏void ST7735_Reset(void) { HAL_GPIO_WritePin(ST7735_RST_PORT, ST7735_RST_PIN, GPIO_PIN_RESET); HAL_Delay(10); HAL_GPIO_WritePin(ST7735_RST_PORT, ST7735_RST_PIN, GPIO_PIN_SET); HAL_Delay(120); // 必须等待足够长时间 } void ST7735_Init(void) { ST7735_Reset(); // --- 开始初始化序列 --- ST7735_WriteCmd(0x11); // Sleep Out HAL_Delay(120); ST7735_WriteCmd(0xB1); ST7735_WriteData(0x01); ST7735_WriteData(0x2C); ST7735_WriteData(0x2D); ST7735_WriteCmd(0xB2); ST7735_WriteData(0x01); ST7735_WriteData(0x2C); ST7735_WriteData(0x2D); ST7735_WriteCmd(0xB3); ST7735_WriteData(0x01); ST7735_WriteData(0x2C); ST7735_WriteData(0x2D); ST7735_WriteData(0x01); ST7735_WriteData(0x2C); ST7735_WriteData(0x2D); ST7735_WriteCmd(0xB4); // Inversion Off ST7735_WriteData(0x07); ST7735_WriteCmd(0xC0); ST7735_WriteData(0xA2); ST7735_WriteData(0x02); ST7735_WriteData(0x84); ST7735_WriteData(0xC5); // VL63 ST7735_WriteCmd(0xC1); ST7735_WriteData(0xC5); ST7735_WriteCmd(0xC2); ST7735_WriteData(0x0A); ST7735_WriteData(0x00); ST7735_WriteCmd(0xC3); ST7735_WriteData(0x8A); ST7735_WriteData(0x2A); ST7735_WriteData(0x8A); ST7735_WriteCmd(0xC4); ST7735_WriteData(0x8A); ST7735_WriteData(0xEE); ST7735_WriteCmd(0xC5); // VCOM ST7735_WriteData(0x0E); ST7735_WriteCmd(0x36); // MADCTL: 内存访问控制 ST7735_WriteData(0xC0); // 旋转方向 BGR顺序 ST7735_WriteCmd(0x3A); // COLMOD: 接口像素格式 ST7735_WriteData(0x05); // 16位色 (RGB565) ST7735_WriteCmd(0x21); // Display Inversion On HAL_Delay(10); ST7735_WriteCmd(0x13); // Normal Display Mode On ST7735_WriteCmd(0x29); // Display On HAL_Delay(10); }重点说明几个关键命令0x36 (MADCTL)决定屏幕旋转方向和颜色排列。常用值0x00: 0°, RGB0x60: 90°, RGB0xA0: 180°, RGB0xC0: 270°, BGR ← 我这块屏需要这个0x3A (COLMOD)必须设为0x05启用16位色模式否则颜色会异常。0x11和0x29分别是“退出睡眠”和“开启显示”缺一不可。所有延时都不能删特别是复位后的120ms是芯片启动所需时间。如果换成了“Black Tab”模组你可能需要换成另一套初始化序列例如先发0x0C,0x0F等。建议准备多套init函数备用。实际测试画个红色方块验证为了快速验证是否成功我写了个最简单的填充函数void ST7735_FillRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t color) { uint8_t x1 x w - 1; uint8_t y1 y h - 1; ST7735_SetAddressWindow(x, y, x1, y1); uint32_t total_pixels w * h; uint8_t *buffer malloc(total_pixels * 2); // 每像素2字节 if (!buffer) return; for (int i 0; i total_pixels; i) { buffer[2*i] color 8; // 高字节 buffer[2*i 1] color 0xFF; // 低字节 } ST7735_WriteCmd(0x2C); // 开始写GRAM ST7735_WriteBuffer(buffer, total_pixels * 2); free(buffer); }配合地址窗口设置函数void ST7735_SetAddressWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { ST7735_WriteCmd(0x2A); // Column Address Set ST7735_WriteData(0x00); ST7735_WriteData(x0 2); // XSTART偏移修正 ST7735_WriteData(0x00); ST7735_WriteData(x1 2); // XEND ST7735_WriteCmd(0x2B); // Row Address Set ST7735_WriteData(0x00); ST7735_WriteData(y0 1); ST7735_WriteData(0x00); ST7735_WriteData(y1 1); } 注意很多ST7735模组的实际可视区域是128×160但它物理RAM是132×162所以通常要加偏移1或2才能对齐。这也是常被忽略的细节。主函数中调用int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_SPI1_Init(); ST7735_Init(); ST7735_FillRect(10, 10, 50, 50, 0xF800); // 红色矩形RGB565 while (1) {} }当看到屏幕上真的出现一个鲜红的方块时那一刻的成就感懂的人都懂。踩过的坑与避坑指南❌ 问题1屏幕全黑什么也不显示排查思路- 是否执行了0x11和0x29- RST有没有正确释放有没有加足够延时- SPI Mode是否正确用逻辑分析仪看一下SCK空闲电平是不是低✅ 解决方案加入完整的复位流程 使用示波器确认SPI波形。❌ 问题2显示花屏、条纹、雪花典型表现颜色错乱、图像撕裂、部分区域乱码。原因- SPI速率太快8MHz在F1上容易出错- DC引脚控制时机错误- 地址窗口未正确设置写到了无效区域✅ 解决方案- 将SPI降频至4MHz以下测试- 检查ST7735_WriteCmd/Data中CS和DC的顺序- 绘图前务必调用SetAddressWindow。❌ 问题3刷新卡顿CPU占用100%原因每发一个像素都要调一次HAL_SPI_Transmit加上CS反复切换开销极大。✅ 优化建议- 改用DMA传输配合SPI双缓冲更佳- 对静态内容做缓存只刷新变动区域- 使用局部更新而非全屏重绘。未来可以引入LVGL这类轻量GUI库进一步提升交互体验。总结稳定比炫技更重要回顾这次调试过程最大的体会是嵌入式开发没有银弹细节决定成败。一块小小的TFT屏背后藏着SPI时序、电源管理、初始化流程、内存映射等多个技术点的协同。任何一个环节出问题都会表现为“点不亮”。但我相信只要你掌握了以下几点就能应对绝大多数ST7735模组硬件连接准确无误尤其是DC、CS、RSTSPI配置为Mode 0初始速率不宜过高初始化序列必须匹配你的模组类型Green/Black Tab每个传输步骤都要有明确的状态控制善用延时和逻辑分析仪定位问题。现在我已经把这套驱动打包成一个可复用模块后续还会加入字体渲染、图片解码、触摸响应等功能。如果你也在做类似项目欢迎留言交流经验。毕竟每一个能点亮屏幕的夜晚都是值得庆祝的胜利。创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考