用STM32F103驱动1.44寸TFT彩屏(ST7735S)显示自定义图片,手把手教你搞定Img2Lcd取模
STM32F103驱动1.44寸TFT彩屏全流程实战从图片取模到动态显示第一次在嵌入式设备上看到自己的图片显示出来那种成就感就像小时候拼好了一艘乐高战舰。本文将带你完整实现从零开始驱动ST7735S屏幕的全过程重点解决三个核心痛点SPI通信配置的常见坑点、图片取模的参数玄学、以及显示优化的小技巧。1. 硬件连接与SPI配置1.1 硬件清单与引脚映射需要准备的硬件组件STM32F103C8T6最小系统板蓝色药丸版1.44寸TFT屏ST7735S驱动128x128分辨率面包板与杜邦线若干USB转TTL模块用于串口调试关键引脚连接对照表TFT引脚STM32引脚功能说明VCC3.3V电源正极GNDGND电源地SCLPB13SPI时钟线SDAPB15SPI数据线RESPB12复位信号DCPB11数据/命令选择CSPB10片选信号BLKPB1背光控制注意部分屏幕的BLK引脚需要上拉电阻若发现背光不亮可尝试接10K电阻到3.3V1.2 SPI模式配置要点在STM32CubeMX中配置SPI2时需要特别注意以下参数/* SPI2 parameter configuration */ hspi2.Instance SPI2; hspi2.Init.Mode SPI_MODE_MASTER; hspi2.Init.Direction SPI_DIRECTION_2LINES; hspi2.Init.DataSize SPI_DATASIZE_8BIT; hspi2.Init.CLKPolarity SPI_POLARITY_LOW; // CPOL0 hspi2.Init.CLKPhase SPI_PHASE_1EDGE; // CPHA0 hspi2.Init.NSS SPI_NSS_SOFT; hspi2.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_4; // 18MHz 72MHz主频 hspi2.Init.FirstBit SPI_FIRSTBIT_MSB; hspi2.Init.TIMode SPI_TIMODE_DISABLE; hspi2.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE;常见问题排查若屏幕出现花屏检查SPI时钟相位(CPHA)设置ST7735S通常需要CPHA0若通信完全失败用逻辑分析仪捕捉SCL/SDA波形确认时序符合规格书要求若显示颜色异常检查数据格式是否为RGB56516位色2. 屏幕初始化与基础驱动2.1 初始化序列优化ST7735S的初始化命令序列较长但实际项目中只需关注几个关键配置void ST7735_Init(void) { // 硬件复位 HAL_GPIO_WritePin(RES_GPIO_Port, RES_Pin, GPIO_PIN_RESET); HAL_Delay(120); HAL_GPIO_WritePin(RES_GPIO_Port, RES_Pin, GPIO_PIN_SET); HAL_Delay(50); // 关键初始化命令 ST7735_WriteCommand(0x11); // Sleep exit HAL_Delay(120); ST7735_WriteCommand(0xB1); // FRMCTR1 ST7735_WriteData(0x01); ST7735_WriteData(0x2C); ST7735_WriteData(0x2D); ST7735_WriteCommand(0x36); // MADCTL ST7735_WriteData(0xC8); // RGB顺序行地址顺序 ST7735_WriteCommand(0x3A); // COLMOD ST7735_WriteData(0x05); // 16位像素格式 ST7735_WriteCommand(0x29); // Display on }2.2 显存操作技巧实现高效屏幕刷新的三个关键函数设置显示窗口函数void ST7735_SetWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { ST7735_WriteCommand(0x2A); // 列地址设置 ST7735_WriteData(0x00); ST7735_WriteData(x0 2); // 偏移补偿 ST7735_WriteData(0x00); ST7735_WriteData(x1 2); ST7735_WriteCommand(0x2B); // 行地址设置 ST7735_WriteData(0x00); ST7735_WriteData(y0 3); ST7735_WriteData(0x00); ST7735_WriteData(y1 3); ST7735_WriteCommand(0x2C); // 内存写入 }像素填充优化方案void ST7735_FillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) { uint8_t buff[64]; // 发送缓冲区 uint16_t cnt w * h; // 预填充缓冲区 for(uint8_t i0; i32; i) { buff[2*i] color 8; buff[2*i1] color 0xFF; } ST7735_SetWindow(x, y, xw-1, yh-1); HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); while(cnt 32) { HAL_SPI_Transmit(hspi2, buff, 64, 10); cnt - 32; } if(cnt 0) { HAL_SPI_Transmit(hspi2, buff, cnt*2, 10); } HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); }双缓冲技术实现uint16_t frameBuffer[128][128]; // 第二帧缓冲区 void ST7735_Refresh(void) { ST7735_SetWindow(0, 0, 127, 127); HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); for(int y0; y128; y) { HAL_SPI_Transmit(hspi2, (uint8_t*)frameBuffer[y], 256, 100); } HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); }3. 图片取模实战指南3.1 Img2Lcd配置详解使用Img2Lcd V2.9进行图片转换时必须注意以下参数组合关键配置项输出数据类型C语言数组(*.c)扫描模式水平扫描输出灰度16位真彩色最大宽度和高度128x128包含图像头数据取消勾选字节排列方式小端模式实测发现当图片尺寸小于128x128时勾选图像缩放会导致颜色失真建议提前用Photoshop调整尺寸3.2 取模参数对照实验我们测试了不同参数组合下的显示效果色彩模式扫描方向显示效果适用场景RGB565水平扫描最佳彩色照片RGB555垂直扫描色偏不推荐灰度水平扫描黑白单色LOGO3.3 自定义调色板技巧对于公司LOGO等简单图形可以手动定义调色板减少存储空间// 16色调色板 const uint16_t palette[16] { 0x0000, // 黑色 0xFFFF, // 白色 0xF800, // 红色 0x07E0, // 绿色 ... // 其他颜色 }; // 4位索引图像数据 const uint8_t logo[] { 0x01,0x01,0x01,0x02,0x02, 0x01,0x00,0x00,0x02,0x02, ... // 其他图像数据 }; void Draw_PaletteImage(uint8_t x, uint8_t y) { for(int i0; isizeof(logo); i) { uint16_t color palette[logo[i] 0x0F]; frameBuffer[y i/128][x i%128] color; } }4. 高级显示技巧与优化4.1 动态刷新优化通过DMASPI实现无阻塞刷新// 在CubeMX中启用SPI TX DMA通道 void ST7735_DMA_Refresh(uint16_t* buf, uint32_t len) { ST7735_SetWindow(0, 0, 127, 127); HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit_DMA(hspi2, (uint8_t*)buf, len*2); // 在传输完成中断中拉高CS }4.2 伪动画实现方案在没有硬件加速的情况下可以通过以下方式实现流畅动画使用脏矩形技术局部刷新预先计算帧间差异区域采用RLE压缩传输动画帧示例代码typedef struct { uint8_t x, y, w, h; uint8_t* compressedData; } DiffArea; void Update_Animation(DiffArea* diff) { ST7735_SetWindow(diff-x, diff-y, diff-xdiff-w-1, diff-ydiff-h-1); // 解压并发送差异数据 RLE_Decode(diff-compressedData, frameBuffer); }4.3 性能实测数据不同刷新方式的性能对比刷新方式全屏刷新时间CPU占用率普通SPI78ms98%DMA传输62ms12%差异刷新15ms30%最后分享一个实际项目中的教训当屏幕出现随机噪点时检查电源滤波电容是否足够建议在VCC和GND之间并联10μF0.1μF电容。曾经因为这个问题调试了整整两天最终发现是电源干扰导致的显示异常。