STM32驱动ILI9325 TFT LCD实战指南

张开发
2026/4/12 3:24:36 15 分钟阅读

分享文章

STM32驱动ILI9325 TFT LCD实战指南
1. 项目概述TFTLCD_Landtiger 是一个面向 STM32 平台特别是基于 Landtiger 开发板的 TFT LCD 显示驱动库其核心目标是为搭载 ILI9325 显示控制器的 2.4 英寸/3.2 英寸并行接口 TFT 模块提供稳定、高效、可移植的底层驱动支持。该库并非从零构建而是深度基于保加利亚工程师 Todor Todorov 开发的开源 TFTLCD 库进行适配与工程化增强专为 Landtiger 硬件平台的引脚布局、时序约束及启动流程进行了定制化重构。Landtiger 开发板是一类以 STM32F103C8T6主流“Blue Pill”兼容核心或 STM32F103RCT6资源更丰富的 LQFP64 封装为主控的低成本嵌入式学习与原型开发平台。其典型特征包括8MHz 外部晶振、内置 64KB Flash / 20KB SRAM、支持 JTAG/SWD 调试、板载 USB 转串口芯片CH340 或 CP2102以及最关键的——一组预定义的 16 位并行数据总线D0–D15和配套的控制信号RS、RW、CS、RD、WR、RESET。这些硬件资源直接决定了 ILI9325 驱动的实现方式必须采用 16 位并行总线模式而非 SPI且所有 GPIO 引脚映射需严格匹配 Landtiger 的 PCB 设计。ILI9325 是由 Ilitek 公司推出的经典 240×320 分辨率 TFT LCD 控制器 IC广泛应用于早期工业 HMI、便携设备及教学套件中。其特性包括支持 16/18-bit RGB 接口、内建 240×320×16bit 显存150KB、支持水平/垂直滚动、多种颜色格式RGB565、RGB666、RGB888、内置伽马校正寄存器、以及完整的显示窗口控制功能。尽管其性能无法与现代 MIPI-DSI 控制器相比但其寄存器结构清晰、文档完备、时序要求相对宽松使其成为嵌入式初学者理解 LCD 驱动原理的理想载体。本项目的核心价值不在于引入全新算法而在于将抽象的 ILI9325 寄存器操作转化为可在真实 Landtiger 硬件上“开箱即用”的工程实践。它解决了三个关键工程痛点引脚复用冲突STM32F103 的 GPIO 在复用为 16 位数据总线时常与 JTAG/SWD、USART1 等外设产生引脚重叠需在RCC和GPIO初始化阶段精确禁用冲突复用功能时序精度保障ILI9325 对 WR/RD 脉冲宽度典型值 ≥ 100ns及建立/保持时间有硬性要求纯软件延时如for循环在不同编译优化等级下不可靠必须采用__NOP()插入或SysTick定时器校准内存带宽瓶颈全屏刷新240×320×2 153,600 字节在 72MHz 主频下若采用逐字节写入耗时将超过 200ms无法满足基本动画需求必须启用DMA或优化为半字uint16_t批量写入。因此TFTLCD_Landtiger 的本质是一个“硬件胶水层”它将 ILI9325 的数据手册规范、Landtiger 的物理电路约束、以及 STM32 HAL/LL 库的抽象能力三者精密耦合最终输出一套可直接集成进 Keil MDK、STM32CubeIDE 或 PlatformIO 工程的 C 语言驱动模块。2. 硬件接口与引脚映射Landtiger 开发板对 ILI9325 的连接采用标准的 8080 并行总线协议其信号定义与 STM32F103 的 GPIO 映射关系如下表所示。该映射是整个驱动库正常工作的物理基础任何引脚更改都必须同步更新tftlcd.h中的宏定义。ILI9325 信号Landtiger 板载标识STM32F103 引脚 (F103C8T6)GPIO 端口/引脚功能说明D0–D7DB0–DB7PA0–PA7GPIOA, PIN0–7低 8 位数据总线实际使用 16 位模式时此为 D0–D7D8–D15DB8–DB15PB0–PB7GPIOB, PIN0–7高 8 位数据总线D8–D15RS (DCX)DCPC8GPIOC, PIN8Data/Command 选择高电平写数据低电平写命令RWRDPC9GPIOC, PIN9Read/Write 选择低电平写入高电平读取本库仅写入固定拉低CSCSPC10GPIOC, PIN10Chip Select低电平选通 LCD 控制器WRWRPC11GPIOC, PIN11Write Strobe下降沿锁存数据总线上的值RDRDPC12GPIOC, PIN12Read Strobe下降沿从数据总线读取值本库未启用读操作RESETRSTPC13GPIOC, PIN13硬件复位低电平有效持续 ≥ 10μs关键工程说明RW 引脚固定为低电平在绝大多数 Landtiger 应用场景中仅需向 LCD 写入数据与命令无需读取状态寄存器如 BUSY 标志。将RW直接接地可省去一个 GPIO 控制并简化时序逻辑。驱动库中所有写操作均默认RW0。RD 引脚悬空或接地因不启用读操作RD引脚在硬件上可悬空或为降低功耗接地。软件层面完全忽略其控制。CS 与 RS 的协同逻辑CS是片选使能RS是内部寄存器选择。一次有效写操作必须满足CS0且WR发生下降沿此时RS的电平决定总线数据被送入指令寄存器RS0还是显存/参数寄存器RS1。RESET 的可靠性设计PC13同时是 STM32 的RTC备份域引脚上电后默认为模拟输入模式。在TFTLCD_Init()函数中必须首先将PC13配置为推挽输出并在拉低RESET前插入100us延时确保复位脉冲宽度满足 ILI9325 的tRPReset Pulse Width≥ 10μs 要求。以下为初始化上述 GPIO 的典型 HAL 库代码片段需置于main()的MX_GPIO_Init()之后// 配置数据总线PA0-7, PB0-7 为推挽输出50MHz 速度 __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct {0}; // 数据总线 D0-D15 GPIO_InitStruct.Pin GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); HAL_GPIO_Init(GPIOB, GPIO_InitStruct); // 控制信号PC8-PC13 GPIO_InitStruct.Pin GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13; HAL_GPIO_Init(GPIOC, GPIO_InitStruct); // 初始状态CS1, RS1, WR1, RESET1 (高电平为非激活态) HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13, GPIO_PIN_SET);3. ILI9325 寄存器配置与初始化流程ILI9325 的初始化过程本质上是一系列寄存器写入操作其顺序与参数值直接决定了 LCD 的显示效果、色彩准确性及稳定性。TFTLCD_Landtiger 库遵循官方推荐的初始化序列共写入 25 个关键寄存器。该序列并非随意排列而是严格依据 ILI9325 数据手册中的“Power Sequence”与“Display Sequence”时序图设计任何步骤的缺失或顺序颠倒均可能导致黑屏、花屏或控制器无响应。3.1 核心寄存器功能解析下表列出了初始化过程中写入的最重要寄存器及其工程意义寄存器地址寄存器名称典型写入值工程作用说明0x0001DRIVOUT0x0100设置扫描方向0x0100表示正常扫描从左到右从上到下0x0140为镜像翻转。此值影响后续所有坐标系定义。0x0002LCDRC0x0000LCD 驱动时序控制设置行/列时钟极性、扫描频率。0x0000为默认安全值适用于大多数 60Hz 刷新率场景。0x0003ENTRYMOD0x1030入口模式BIT121启用 RGB 接口BIT51设置为 16-bit RGB565 格式BIT40禁用垂直翻转。此寄存器是色彩格式的总开关。0x0007DISCTRL0x0000显示控制BIT00关闭显示初始化末尾再开启BIT70禁用睡眠模式。初始值为全关。0x000CRGAMCTRL0x0000红色伽马控制一组 16 个 4-bit 值用于精细调节红色通道的亮度曲线。默认值提供中性灰度。0x000DBGAMCTRL0x0000蓝色伽马控制同上独立调节蓝色通道。0x000FPGAMCTRL0x0000绿色伽马控制同上绿色通道。0x0010VCOMCTL10x0000公共电压控制设置 VCOM 电平直接影响对比度。0x0000为默认值过高会导致暗部细节丢失过低则整体发灰。0x0011VCOMCTL20x0000VCOM 电压微调与VCOMCTL1配合使用通常保持为0x0000。0x0020HORADDRSTART0x0000水平起始地址设置显示窗口左边界单位为像素。0x0000即从最左端开始。0x0021VERADDRSTART0x0000垂直起始地址设置显示窗口上边界单位为像素。0x0000即从最顶端开始。0x0022RAMWR—显存写入指令此地址无对应寄存器向其写入数据即向当前地址指针指向的显存位置写入一个像素16-bit。这是所有绘图函数的底层入口。关键时序约束在向0x0007(DISCTRL) 写入0x0001开启显示之前必须确保0x0001(DRIVOUT)、0x0003(ENTRYMOD) 等基础配置已完成且控制器已退出复位状态RESET释放后等待10ms。否则LCD 可能进入不可预测状态。3.2 初始化函数实现逻辑TFTLCD_Init()函数是整个库的入口其执行流程高度结构化硬件复位拉低RESET引脚100us再拉高等待5ms让内部 PLL 锁定发送初始化序列按顺序调用LCD_WR_REG()向每个寄存器地址写入预设值设置显示窗口调用LCD_SetWindows(0, 0, 239, 319)将整个 240×320 区域设为有效显示区开启显示向0x0007写入0x0001BIT01清屏调用LCD_Fill(0, 0, 239, 319, BLACK)用黑色填充整个屏幕消除上电残影。LCD_WR_REG()函数是寄存器写入的核心其实现体现了对 8080 总线时序的精确控制void LCD_WR_REG(uint16_t reg) { // 1. 拉低 CS 选中芯片 HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_RESET); // 2. 设置 RS0准备写入命令 HAL_GPIO_WritePin(LCD_RS_PORT, LCD_RS_PIN, GPIO_PIN_RESET); // 3. 将寄存器地址写入数据总线 (D0-D15) LCD_WriteData(reg); // 此函数将 reg 的高低字节分别写入 PA0-7 和 PB0-7 // 4. 产生 WR 下降沿先拉高再拉低再拉高标准脉冲 HAL_GPIO_WritePin(LCD_WR_PORT, LCD_WR_PIN, GPIO_PIN_SET); __NOP(); __NOP(); // 建立时间 tAS ≥ 10ns HAL_GPIO_WritePin(LCD_WR_PORT, LCD_WR_PIN, GPIO_PIN_RESET); __NOP(); __NOP(); // 脉冲宽度 tPW ≥ 100ns HAL_GPIO_WritePin(LCD_WR_PORT, LCD_WR_PIN, GPIO_PIN_SET); // 5. 拉高 CS取消选中 HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_SET); }4. 核心绘图 API 与性能优化TFTLCD_Landtiger 提供了一套简洁但功能完备的 2D 绘图 API所有函数均围绕LCD_WR_REG(0x0022)这一显存写入指令展开。其性能表现直接取决于LCD_WriteData()函数的效率这也是本库工程化增强的重点。4.1 关键 API 接口说明函数名原型功能说明典型应用场景LCD_DrawPoint(x, y, color)void LCD_DrawPoint(uint16_t x, uint16_t y, uint16_t color)在(x, y)坐标绘制单个像素color为 RGB565 格式如RED0xF800,GREEN0x07E0,BLUE0x001F绘制点阵字体、调试光标定位LCD_DrawLine(x1, y1, x2, y2, color)void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)使用 Bresenham 算法绘制任意斜率直线绘制 UI 边框、图表坐标轴LCD_DrawRectangle(x, y, width, height, color)void LCD_DrawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color)绘制空心矩形创建按钮、窗口框架LCD_Fill(x, y, width, height, color)void LCD_Fill(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color)以color填充指定矩形区域清屏、背景色填充、进度条底色LCD_ShowChar(x, y, chr, size, mode)void LCD_ShowChar(uint16_t x, uint16_t y, uint8_t chr, uint8_t size, uint8_t mode)在(x, y)显示 ASCII 字符size为字体大小12/16/24mode0为反色串口调试信息输出、状态提示LCD_ShowString(x, y, p, size)void LCD_ShowString(uint16_t x, uint16_t y, uint8_t *p, uint8_t size)显示字符串内部循环调用LCD_ShowChar()用户界面文本、传感器读数标签4.2 性能瓶颈与优化策略在 STM32F103 上LCD_DrawPoint()的原始实现每次写一个像素都执行一次完整的WR脉冲效率极低。一个 240×320 全屏填充将触发 76,800 次WR信号耗时约350ms实测完全无法用于动态内容。TFTLCD_Landtiger 通过三级优化显著提升性能半字批量写入LCD_Fill()函数不调用LCD_DrawPoint()而是直接向0x0022地址连续写入width × height个uint16_t数据。由于WR信号在连续写入时只需在第一个数据前拉低一次后续数据仅需WR脉冲即可锁存这将总线事务次数从N降至1性能提升10x以上。DMA 加速可选对于更高阶应用可将LCD_Fill()改写为 DMA 模式。配置DMA1_Channel1映射至GPIOA输出或DMA1_Channel2映射至GPIOB输出将一块 SRAM 中的uint16_t数组作为源以Memory-to-Peripheral模式将数据流式写入 GPIO 数据寄存器。此方式可将全屏填充压缩至~45ms72MHz 主频接近理论带宽极限。显存地址预设所有绘图函数在操作前均先调用LCD_SetCursor(x, y)向0x0020和0x0021写入起始坐标再向0x0022写入数据。这样避免了每次写像素都要重新设置地址减少了寄存器写入开销。LCD_Fill()的核心循环代码如下展示了半字批量写入的精髓void LCD_Fill(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color) { uint32_t i, j; LCD_SetWindows(x, y, xwidth-1, yheight-1); // 设置显存写入窗口 LCD_WR_REG(0x0022); // 发送显存写入指令 // 关闭 CS保持选中状态仅操作 WR HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_RESET); HAL_GPIO_WritePin(LCD_RS_PORT, LCD_RS_PIN, GPIO_PIN_SET); // RS1, 写数据 for(i 0; i height; i) { for(j 0; j width; j) { // 直接向数据总线写入 color然后触发 WR LCD_WriteData(color); HAL_GPIO_WritePin(LCD_WR_PORT, LCD_WR_PIN, GPIO_PIN_RESET); __NOP(); __NOP(); HAL_GPIO_WritePin(LCD_WR_PORT, LCD_WR_PIN, GPIO_PIN_SET); } } HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_SET); }5. 实际工程集成与 FreeRTOS 兼容性在真实的嵌入式产品中TFTLCD_Landtiger 很少作为孤立模块运行它必须与系统级组件如 FreeRTOS、传感器驱动、通信协议栈协同工作。本库的设计充分考虑了多任务环境下的安全性与实时性。5.1 FreeRTOS 任务安全机制LCD 控制器是一个典型的共享外设资源。若多个 RTOS 任务如SensorTask读取温湿度并显示、UsbTask接收 PC 指令并更新 UI同时调用LCD_Fill()将导致显存数据错乱。TFTLCD_Landtiger 通过SemaphoreHandle_t LCD_Semaphore提供了轻量级的互斥访问机制。在TFTLCD_Init()中创建二值信号量LCD_Semaphore xSemaphoreCreateBinary(); if (LCD_Semaphore ! NULL) { xSemaphoreGive(LCD_Semaphore); // 初始状态为可用 }所有绘图 API 的顶层封装均增加信号量保护void LCD_Fill_RTOS(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color) { if (xSemaphoreTake(LCD_Semaphore, portMAX_DELAY) pdTRUE) { LCD_Fill(x, y, width, height, color); xSemaphoreGive(LCD_Semaphore); } }此设计确保了在任意时刻只有一个任务能持有 LCD 控制权其他任务将在xSemaphoreTake()处阻塞直至当前任务释放信号量。阻塞时间可控符合硬实时要求。5.2 典型集成案例环境监测仪表一个典型的 Landtiger 应用是环境监测仪表其软件架构如下Task_Sensor (优先级 3)每2s读取 DHT22 温湿度通过LCD_ShowString()更新屏幕中部数值Task_Display (优先级 2)每100ms执行一次负责刷新动态元素顶部状态栏WiFi 连接图标、底部电池电量条、以及一个缓慢旋转的加载动画Task_Uart (优先级 1)处理来自 PC 的 AT 指令如ATCLR清屏、ATSETCOLORFF0000更改警告色。在此架构下Task_Sensor和Task_Display会竞争 LCD 资源。通过信号量Task_Display的高频刷新不会阻塞Task_Sensor的关键数据更新反之亦然。Task_Uart因优先级最低其指令执行会被短暂延迟但这对用户交互体验影响甚微。5.3 内存管理注意事项ILI9325 内置显存为240×320×2 153,600 bytes远超 STM32F103C8T6 的20KB SRAM。因此所有绘图操作均为“即时写入”Direct Write模式不维护帧缓冲区Frame Buffer。这意味着优点内存占用为O(1)无额外 RAM 开销缺点无法实现双缓冲Double Buffering防撕裂复杂动画可能出现闪烁。若项目需要高级图形效果可在外部扩展SPI Flash或SRAM自行实现双缓冲。此时LCD_Fill()的职责变为将外部缓冲区内容“刷”到 LCD 显存而绘图逻辑则在 RAM 中完成。6. 故障排查与调试技巧在 Landtiger 平台上部署 TFTLCD_Landtiger 时最常见的故障现象及排查路径如下6.1 黑屏无任何显示检查点 1硬件连接使用万用表测量RESET引脚电压。上电后应为3.3V执行TFTLCD_Init()时应短暂跌落至0V后回升。若无跌落检查PC13是否被其他外设如LED复用。检查点 2CS 与 RS 电平用逻辑分析仪捕获CS和RS信号。在LCD_WR_REG(0x0001)执行期间CS必须为0RS必须为0。若CS始终为1检查GPIOC时钟是否已使能。检查点 3时序违规测量WR脉冲宽度。若 100ns需增加__NOP()数量或改用SysTick_Delay_us(100)。6.2 花屏彩色噪点、线条错位检查点 1数据总线干扰PA0-7和PB0-7是否与USB、JTAG引脚冲突在RCC-APB2ENR中确认IOPAEN和IOPBEN已置位且AFIO-MAPR中SWJ_CFG设置为0b010JTAG-DP SW-DP释放PA13/14/15和PB3/4。检查点 2电源噪声ILI9325 对VCC和VCI模拟电源的纹波敏感。在VCI引脚就近 5mm焊接10uF钽电容和100nF陶瓷电容。6.3 显示偏移图像整体右移/下移检查点坐标系配置检查0x0001(DRIVOUT) 寄存器值。若误写为0x0140镜像会导致 X/Y 轴反转。重新烧录固件确保初始化序列中该值为0x0100。所有调试过程均应以LCD_Fill(0, 0, 239, 319, RED)为基准测试用例。若此函数能稳定显示全红屏幕则证明硬件连接、时序、寄存器配置全部正确后续问题必源于上层应用逻辑。

更多文章