最近在调试一块2.8寸的ILI9341驱动TFTLCD屏时,遇到了花屏、颜色异常、显示方向错误等一系列问题。经过两周的反复试验和查阅资料,终于梳理出一套完整的解决方案。本文将分享从硬件连接到FSMC配置的全流程避坑经验,特别适合使用正点原子开发板的STM32F4开发者参考。
拿到LCD模块后,第一步不是急着写代码,而是确认硬件连接是否正确。常见的2.8寸TFTLCD模块通常采用16位8080并行接口,需要检查以下关键信号线:
特别注意:某些开发板的LCD接口可能复用其他功能,需检查原理图确认是否已正确配置为FSMC模式。
8080时序是驱动这类屏幕的核心,其基本操作流程如下:
常见问题排查表:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 屏幕全白 | 背光未开启 | 检查背光控制引脚电压 |
| 花屏 | 数据线接触不良 | 重新插接排线,检查焊接 |
| 无反应 | 电源异常 | 测量3.3V和5V供电 |
STM32的FSMC外设可以完美模拟8080时序,关键在于正确配置存储区域和时序参数。以Bank1为例,典型配置步骤如下:
c复制// FSMC初始化结构体配置
FSMC_NORSRAMInitTypeDef FSMC_InitStruct;
FSMC_NORSRAMTimingInitTypeDef FSMC_TimingStruct;
// 时序参数配置(单位:HCLK周期)
FSMC_TimingStruct.FSMC_AddressSetupTime = 1; // 地址建立时间
FSMC_TimingStruct.FSMC_AddressHoldTime = 0; // 地址保持时间
FSMC_TimingStruct.FSMC_DataSetupTime = 5; // 数据建立时间
FSMC_TimingStruct.FSMC_BusTurnAroundDuration = 0;
FSMC_TimingStruct.FSMC_CLKDivision = 0;
FSMC_TimingStruct.FSMC_DataLatency = 0;
FSMC_TimingStruct.FSMC_AccessMode = FSMC_AccessMode_A;
// NOR/SRAM控制配置
FSMC_InitStruct.FSMC_Bank = FSMC_Bank1_NORSRAM1;
FSMC_InitStruct.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
FSMC_InitStruct.FSMC_MemoryType = FSMC_MemoryType_SRAM;
FSMC_InitStruct.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
FSMC_InitStruct.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
FSMC_InitStruct.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
FSMC_InitStruct.FSMC_WrapMode = FSMC_WrapMode_Disable;
FSMC_InitStruct.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
FSMC_InitStruct.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
FSMC_InitStruct.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
FSMC_InitStruct.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
FSMC_InitStruct.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
FSMC_InitStruct.FSMC_ReadWriteTimingStruct = &FSMC_TimingStruct;
FSMC_InitStruct.FSMC_WriteTimingStruct = &FSMC_TimingStruct;
// 初始化FSMC
FSMC_NORSRAMInit(&FSMC_InitStruct);
FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE);
关键参数调整建议:
ILI9341有数十条控制指令,但实际开发中最常用的有以下几条:
读取LCD控制器ID是验证通信是否正常的第一步:
c复制uint16_t ILI9341_ReadID(void) {
uint16_t id = 0;
LCD_WriteReg(0xD3); // 发送读ID命令
id = LCD_ReadData(); // 丢弃第一个字节
id = LCD_ReadData(); // 应为0x00
id = LCD_ReadData(); // 0x93
id <<= 8;
id |= LCD_ReadData(); // 0x41
return id; // 正确应返回0x9341
}
通过0x36指令可以灵活控制显示方向,其参数格式如下:
| 位 | 名称 | 功能 |
|---|---|---|
| D7 | MY | 行地址顺序 |
| D6 | MX | 列地址顺序 |
| D5 | MV | 行列交换 |
| D4 | ML | 垂直刷新顺序 |
| D3 | BGR | 颜色顺序 |
| D2 | MH | 水平刷新顺序 |
常用方向配置:
c复制// 默认方向(从左到右,从上到下)
LCD_WriteReg(0x36, 0x08);
// 旋转180度
LCD_WriteReg(0x36, 0xC8);
// 竖屏模式
LCD_WriteReg(0x36, 0x68);
显存操作是LCD驱动的核心功能,主要涉及以下指令:
典型显存写入流程:
c复制void ILI9341_SetWindow(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
LCD_WriteReg(0x2A, x1>>8, x1&0xFF, x2>>8, x2&0xFF); // 设置X范围
LCD_WriteReg(0x2B, y1>>8, y1&0xFF, y2>>8, y2&0xFF); // 设置Y范围
LCD_WriteReg(0x2C); // 准备写入GRAM
}
void ILI9341_FillColor(uint16_t color, uint32_t count) {
while(count--) {
LCD_WriteData(color);
}
}
对于大块区域填充或图像显示,使用DMA可以显著提高性能:
c复制void ILI9341_DMAFill(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) {
uint32_t count = (x2-x1+1)*(y2-y1+1);
ILI9341_SetWindow(x1, y1, x2, y2);
// 配置DMA
DMA_InitTypeDef DMA_InitStruct;
DMA_DeInit(DMA2_Stream0);
DMA_InitStruct.DMA_Channel = DMA_Channel_0;
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(LCD->RAM);
DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)&color;
DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStruct.DMA_BufferSize = count;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream0, &DMA_InitStruct);
DMA_Cmd(DMA2_Stream0, ENABLE);
while(DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_TCIF0) == RESET);
DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_TCIF0);
}
对于动态显示内容,可以采用双缓冲技术避免闪烁:
ILI9341支持RGB565格式,常用颜色转换函数:
c复制// RGB888转RGB565
uint16_t RGB888_to_RGB565(uint8_t r, uint8_t g, uint8_t b) {
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
// RGB565分解
void RGB565_to_RGB888(uint16_t rgb565, uint8_t *r, uint8_t *g, uint8_t *b) {
*r = (rgb565 >> 8) & 0xF8;
*g = (rgb565 >> 3) & 0xFC;
*b = (rgb565 << 3) & 0xF8;
}
在实际项目中,我们可能会遇到各种显示异常情况。以下是几个典型问题的解决方法:
花屏问题排查流程:
颜色异常处理:
触摸屏校准技巧:
经过多次项目实践,发现最稳定的配置方案是:FSMC时钟84MHz,DataSetupTime=5,使用DMA传输,显示方向设置为0x68(竖屏模式)。这种配置在各种环境温度下都能稳定工作,刷新率可达到45fps以上,完全满足大多数嵌入式GUI应用的需求。