1. 项目概述
在嵌入式开发中,LCD显示是最基础也最常用的功能之一。今天我要分享的是如何在STM32H743平台上驱动ST7735芯片的160×80分辨率LCD屏幕显示图片。这个项目不仅涉及硬件接口配置,还包括了从SD卡读取BMP图片文件、图像数据转换以及最终显示的全流程实现。
作为一名长期从事嵌入式开发的工程师,我发现很多初学者在实现LCD图片显示时会遇到各种问题:从硬件接口配置错误、SPI通信速率设置不当,到图像数据格式转换错误等。本文将详细记录整个开发过程,并分享我在实际项目中积累的经验技巧。
2. 硬件准备与工程创建
2.1 硬件选型与连接
本项目使用的核心硬件包括:
- 主控芯片:STM32H743VIT6(高性能Cortex-M7内核)
- LCD模块:ST7735驱动芯片,160×80分辨率,SPI接口
- 存储介质:MicroSD卡(FAT32格式)
硬件连接要点:
- SPI4接口用于LCD数据传输(PE12-SCK,PE14-MOSI)
- GPIO控制引脚:PE11(CS),PE13(DC)
- PWM背光控制:TIM1_CH2N(PE10)
- SD卡接口:SDMMC1 4位模式
注意:不同开发板的引脚定义可能不同,务必根据实际硬件原理图进行连接。我曾遇到过因为引脚定义错误导致屏幕无法点亮的情况,排查了整整一天才发现是原理图标注有误。
2.2 STM32CubeMX工程配置
2.2.1 新建工程
启动STM32CubeMX后,选择"New Project",在芯片选择界面输入"STM32H743VIT6"。这里有个细节需要注意:CubeMX会提示是否启动高级模式,对于这个项目我们选择"No",因为不需要复杂的内核配置。
2.2.2 时钟配置
时钟配置是STM32开发中最容易出错的部分之一。我们的配置要点:
- HSE时钟源:8MHz(匹配外部晶振频率)
- 主PLL配置:
- PLLM分频:4
- PLLN倍频:480
- PLLP分频:2
- PLLQ分频:20
- PLLR分频:2
- 系统时钟:480MHz
- SPI4时钟:120MHz(15MHz通信速率)
时钟树配置完成后,务必检查所有时钟域是否都显示绿色(表示在允许范围内)。红色标记表示配置错误,需要调整分频/倍频系数。
3. 外设接口配置详解
3.1 LCD接口配置
3.1.1 SPI接口设置
ST7735通过SPI接口通信,关键配置参数:
- 模式:Half-Duplex Master
- 数据大小:8位
- 预分频器:8(得到15MHz SCK)
- 时钟极性:Low
- 时钟相位:1 Edge
特别注意引脚重映射:
- 默认SPI4引脚:PE2(SCK),PE6(MOSI)
- 实际使用引脚:PE12(SCK),PE14(MOSI)
在CubeMX中重映射引脚的方法:
- 按住Ctrl键
- 点击原引脚(PE2)并拖动到目标引脚(PE12)
- 对MOSI引脚重复相同操作
3.1.2 GPIO控制引脚
除了SPI接口,还需要配置两个GPIO控制引脚:
- PE11:LCD片选(CS),推挽输出,无上拉下拉
- PE13:数据/命令选择(DC),推挽输出,无上拉下拉
3.1.3 背光控制
使用TIM1的通道2N产生PWM信号控制背光:
- 定时器时钟:240MHz
- 预分频器:239
- 计数周期:999
- 初始占空比:50%(500/1000)
3.2 SD卡接口配置
SD卡通过SDMMC1接口连接,4位总线模式:
- 时钟分频:4(得到48MHz时钟)
- 总线宽度:4位
- 电源控制:禁用
- 中断优先级:5(适中优先级)
经验分享:SD卡通信对时序要求严格,如果遇到初始化失败,可以尝试降低时钟频率。我在项目中曾将分频系数从4调整为8(24MHz)解决了某些品牌SD卡的兼容性问题。
4. 软件实现与代码解析
4.1 工程结构设计
生成的MDK-ARM工程包含以下关键文件:
code复制├── Core
│ ├── Inc
│ │ ├── main.h
│ │ └── myGlobal.h # 新增的全局变量头文件
│ ├── Src
│ │ ├── main.c
│ │ └── lcd.c # LCD驱动实现
├── Drivers
│ ├── BSP
│ │ └── ST7735 # ST7735底层驱动
│ └── FATFS # 文件系统支持
4.2 显存管理设计
在myGlobal.h中定义显存结构:
c复制#define IMGBUFWIDTH 120
#define IMGBUFHEIGHT 80
#define IMGBUFNUM 2
#define TXTBUFWIDTH 20
extern uint8_t IMGBUF[IMGBUFNUM][IMGBUFHEIGHT*IMGBUFWIDTH*2];
extern uint8_t IMGIDX;
extern uint8_t TXTBUF[TXTBUFWIDTH];
这种双缓冲设计(IMGBUF[2])可以避免屏幕刷新时的闪烁现象。一个缓冲区用于显示,另一个用于准备下一帧数据。
4.3 LCD驱动实现
4.3.1 初始化函数
c复制void LCD_Disp_Init(void) {
ST7735Ctx.Orientation = ST7735_ORIENTATION_LANDSCAPE_ROT180;
ST7735Ctx.Panel = HannStar_Panel;
ST7735Ctx.Type = ST7735_0_9_inch_screen;
ST7735_RegisterBusIO(&st7735_pObj,&st7735_pIO);
ST7735_LCD_Driver.Init(&st7735_pObj,ST7735_FORMAT_RBG565,&ST7735Ctx);
ST7735_LCD_Driver.FillRect(&st7735_pObj, 0, 0,
ST7735Ctx.Width, ST7735Ctx.Height, BLACK);
LCD_SetBrightness(500);
}
初始化时需要注意:
- 屏幕方向(Orientation)要与物理安装方式一致
- 颜色格式(RBG565)要与后续图像数据处理匹配
- 初始背光亮度不宜设置过高,避免电流冲击
4.3.2 显示刷新函数
c复制void LCD_Disp(void) {
ST7735_FillRGBRect(&st7735_pObj, 0, 0,
IMGBUF[IMGIDX], IMGBUFWIDTH, IMGBUFHEIGHT);
LCD_ShowString(130, 30, ST7735Ctx.Width, 16, 16, TXTBUF);
}
这个函数实现了:
- 图像区域刷新(左侧120×80像素)
- 文本信息显示(右侧40像素宽区域)
4.4 图片读取与处理
4.4.1 BMP文件读取
c复制int ReadSD(void) {
FATFS fs; FIL file; FRESULT res; UINT bytes_read;
uint8_t image_buf[IMGBUFWIDTH*3];
char *filename = "cat120.bmp";
// 挂载文件系统
res = f_mount(&fs, "0:", 1);
if(res != FR_OK) return -1;
// 打开文件
res = f_open(&file, filename, FA_READ);
if(res != FR_OK) return -1;
// 读取BMP头(54字节)
res = f_read(&file, image_buf, 54, &bytes_read);
if(!(res == FR_OK && bytes_read == 54 &&
image_buf[0] == 'B' && image_buf[1] == 'M')) {
f_close(&file);
return -1;
}
// 读取图像数据并转换
for(int row=IMGBUFHEIGHT-1; row>=0; row--) {
for(int num=0; num<2; num++) {
f_read(&file, image_buf, width*3/2, &bytes_read);
uint8_t *p = image_buf;
for(int col=0; col<IMGBUFWIDTH*2; col+=2) {
// RGB888转RGB565
uint8_t b = *(p++);
uint8_t g = *(p++);
uint8_t r = *(p++);
uint16_t color565 = ((r >> 3) << 11) |
((g >> 2) << 5) |
(b >> 3);
IMGBUF[num][row*IMGBUFWIDTH*2+col] = color565 >> 8;
IMGBUF[num][row*IMGBUFWIDTH*2+col+1] = color565 & 0xFF;
}
}
}
sprintf((char *)TXTBUF, "Cat");
f_close(&file);
return 0;
}
这段代码实现了:
- FATFS文件系统挂载与文件打开
- BMP文件头验证(检查"BM"标志)
- 图像数据读取(注意BMP文件是倒序存储)
- RGB888到RGB565的颜色格式转换
关键点:BMP文件的像素数据是从下到上存储的,所以读取时需要从最后一行开始。我曾因为忽略这一点导致图像上下颠倒,调试了很久才发现问题。
5. 主程序设计与系统集成
5.1 主函数实现
c复制int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SDMMC1_SD_Init();
MX_SPI4_Init();
MX_TIM1_Init();
MX_FATFS_Init();
LCD_Disp_Init();
if(ReadSD() != 0) {
sprintf((char *)TXTBUF, "Error");
}
while(1) {
LCD_Disp();
IMGIDX = 1 - IMGIDX; // 切换显存
HAL_Delay(500);
}
}
5.2 编译与下载
- 在MDK-ARM中设置正确的编译器版本(建议V5.27及以上)
- 配置下载器选项(ST-Link/V2等)
- 设置正确的Flash算法(STM32H743xx)
- 编译并下载程序
常见编译问题解决:
- 如果出现"未定义符号"错误,检查是否添加了所有必要的源文件
- 如果链接时提示内存不足,调整堆栈大小(本项目设置为Heap=2000,Stack=2000)
- 如果下载失败,检查下载器连接和驱动安装
6. 实际效果与性能优化
6.1 显示效果评估
成功运行后,LCD屏幕应该:
- 左侧显示从SD卡读取的图片(120×80像素)
- 右侧显示"Cat"文字标识
- 图片每500ms切换一次显存(演示双缓冲效果)
6.2 性能优化建议
-
SPI速率优化:
- 初始设置为15MHz(预分频=8)
- 可尝试提高至30MHz(预分频=4),观察屏幕稳定性
- 如果出现雪花噪点,说明速率过高,需要降低
-
图像处理优化:
- 使用DMA传输图像数据,减少CPU占用
- 将RGB转换算法改用查表法加速
- 对于静态图片,可以预转换并存储为RGB565格式
-
内存优化:
- 当前设计使用了双缓冲,占用较大内存
- 如果内存紧张,可改为单缓冲+直接刷新方式
7. 常见问题与解决方案
7.1 屏幕无显示
排查步骤:
- 检查背光是否点亮(测量PE10引脚PWM信号)
- 检查SPI信号(PE12应有15MHz时钟)
- 用逻辑分析仪抓取SPI通信数据
- 确认复位引脚是否正确处理
7.2 图片显示异常
可能原因及解决:
- 颜色错乱:检查RGB565转换代码是否正确
- 图像错位:确认屏幕初始化参数(尤其是宽度高度设置)
- 花屏现象:降低SPI速率或检查接线是否接触不良
7.3 SD卡读取失败
处理方法:
- 确认SD卡格式为FAT32
- 尝试不同的SD卡品牌(兼容性差异较大)
- 检查SDMMC接口的上拉电阻(通常需要50kΩ上拉)
- 降低SDMMC时钟频率(从48MHz降至24MHz)
8. 项目扩展思路
-
多图片幻灯片播放:
- 扩展文件读取功能,支持多文件遍历
- 添加定时自动切换机制
-
图片动态效果:
- 实现图片缩放、旋转算法
- 添加简单的动画过渡效果
-
触摸交互功能:
- 添加触摸屏支持
- 实现图片滑动浏览
-
网络图片加载:
- 通过以太网或WiFi模块获取网络图片
- 实现JPEG解码功能
这个项目虽然基础,但涵盖了嵌入式开发的多个重要方面:外设驱动开发、文件系统操作、内存管理、图像处理等。通过这个案例,我们不仅实现了图片显示功能,更建立了一个可扩展的显示框架,为后续更复杂的图形应用打下了基础。
