STM32驱动ILI7789V彩色LCD的SPI时序与初始化详解
2026/9/11 9:35:43 网站建设 项目流程

简介:本资源是一套基于STM32F10x系列MCU的SPI接口驱动ST7789V彩色TFT液晶屏的完整工程例程,面向嵌入式初学者与中级开发者,解决LCD显示驱动开发中SPI配置、初始化时序、像素数据传输及基础图形绘制等核心问题。压缩包含185个文件,主体为33个C源码、35个头文件(.h)、34个编译中间文件(.o/.d/.crf),辅以Keil工程配置(.uvprojx/.uvoptx)、启动文件(.s)、链接脚本(.sct)及可执行镜像(.hex/.axf),总大小4.91MB,结构完整,可直接编译下载运行。已有1057人学习下载,资源包含标准外设库驱动框架、ST7789V专用LCD驱动层、屏幕初始化序列、RGB565像素写入函数及基础绘图接口,代码注释清晰,结合内容预览可见其覆盖RCC、GPIO、SPI、TIM等关键外设模块,便于理解底层硬件协同逻辑与显示驱动全流程实现。

1. STM32 驱动 ILI7789V 彩色 LCD 不是“接上线就能亮”,而是 SPI 时序、引脚复用与初始化序列的三重校准

很多刚接触 ILI7789V 屏幕的开发者会误以为:只要把 STM32 的 SPI 引脚连到屏的 SCL/SDA/CS/DC,调用 HAL_SPI_Transmit() 发几条命令,屏幕就该亮起彩色图像——结果却是全黑、花屏、闪屏或只显示固定灰阶。根本原因在于:ILI7789V 并非标准 SPI 设备,它要求严格匹配的时钟极性(CPOL)、相位(CPHA)、数据宽度(8-bit)、片选时序(CS active-low 且需保持足够低电平时间),更关键的是其初始化序列包含 20+ 条寄存器写入指令,顺序、延时、参数缺一不可。本例程聚焦 STM32F103/F407 等主流型号,使用 HAL 库 + CubeMX 配置 SPI 外设,不依赖第三方 GUI 库,从裸寄存器操作级还原驱动逻辑。适合需要嵌入式图形界面但受限于 Flash/RAM 资源的工业 HMI、便携设备或毕业设计项目,尤其适用于已掌握基础 GPIO/SPI 操作、正卡在“屏不响应”环节的 1–3 年经验工程师。


2. ILI7789V 与 STM32 的硬件连接必须满足三类电气约束:SPI 模式、DC/CS 电平逻辑与供电稳定性

2.1 ILI7789V 的 SPI 接口特性决定了 STM32 外设配置不能套用通用模板

ILI7789V 数据手册明确标注其 SPI 接口工作在Mode 0(CPOL=0, CPHA=0):空闲时 SCK 为低电平,数据在 SCK 上升沿采样,且必须在 SCK 下降沿前建立稳定。这与多数 Flash 或传感器不同,若 CubeMX 中误设为 Mode 3(CPOL=1, CPHA=1),则通信必然失败,表现为 SPI_TXE 标志永不置位或接收数据全为 0xFF。此外,该芯片仅支持8-bit 数据帧,不兼容 16-bit 传输;其最大 SCK 频率标称为 30 MHz,但实测在 STM32F103 上超过 12 MHz 易出现误码(因 GPIO 切换速度与布线容性影响),推荐初始配置为8 MHz(对应 APB2=72 MHz 时预分频值为 8)。

提示:不要直接复制其他 OLED 或 ST7735 的 SPI 配置。ILI7789V 的 DC(Data/Command)引脚功能等效于“寄存器地址/数据选择信号”,高电平时发送的是显存数据(GRAM),低电平时发送的是控制寄存器地址与参数——这一逻辑必须由软件精确控制,不能交由硬件自动管理。

2.2 典型硬件连接表与关键引脚复用冲突规避

ILI7789V 引脚功能说明推荐 STM32 引脚(F103C8T6)注意事项
VCC3.3V 电源3.3V(LDO 输出)必须加 100nF 陶瓷电容紧靠屏端,避免上电瞬间电流冲击导致初始化失败
GNDGND与 MCU 共地,避免地线环路
SCLSPI 时钟PA5(SPI1_SCK)若使用 SPI2,需注意 PB13 可能被 JTAG 占用,需禁用 SWD/JTAG 或改用其他引脚
SDASPI 数据(MOSI)PA7(SPI1_MOSI)不可接 MISO(PA6),ILI7789V 为单向写入设备,无读回需求
CS片选(低有效)PA4(软件控制)必须软件片选:HAL_SPI_Transmit() 不自动拉低 CS,需在每次传输前手动置低
DC数据/命令选择PA3(GPIO 输出)初始化阶段需频繁切换电平,建议用推挽输出模式,避免开漏导致上升沿缓慢
RESET复位(低有效)PA2(GPIO 输出)上电后需保持低电平 ≥ 10ms,再拉高;部分模块已内置 RC 复位电路,可悬空
LED背光控制PB0(PWM 输出)建议通过 MOSFET 驱动,避免 MCU IO 直接带载,典型电流达 80–150mA

注意:PA4(CS)与 PA3(DC)不可复用为 ADC 或其他外设功能。若使用 STM32F407,优先选用 SPI1(APB2 总线),因其时钟频率更高,可支撑更高刷率;若用 SPI2(APB1),需确认预分频后 SCK 实际频率仍满足 8 MHz 要求。

2.3 CubeMX 中 SPI1 的关键参数配置步骤(以 F103 为例)

  1. 在 Pinout 视图中,将 PA5/PA7/PA4/PA3 分别配置为SPI1_SCK/SPI1_MOSI/GPIO_Output/GPIO_Output
  2. 进入 Configuration → Connectivity → SPI1:
    • Mode:Full-Duplex Master
    • Hardware NSS:Disabled(强制软件片选)
    • Frame Format:Motorola
    • Data Size:8 Bits
    • Clock Polarity (CPOL):Low
    • Clock Phase (CPHA):1st Edge
    • NSS Signal:Software
    • Baud Rate Prescaler:8(APB2=72MHz → SCK=9MHz,实测稳定)
    • First Bit:MSB First
  3. 生成代码后,在main.c中确认hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;已正确赋值。
// 验证 SPI 初始化是否生效(调试时添加) if (HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_RESET) { // 正常:SPI 外设已初始化 } else { // 错误:检查 CubeMX 是否生成了 MX_SPI1_Init() }

该段代码用于快速定位初始化失败点——常见原因是未勾选Generate peripheral initialization as a pair of 'xxx_MspInit/DeInit' functions导致时钟使能代码缺失。


3. ILI7789V 初始化序列必须按精确时序执行:23 条寄存器写入 + 5 类延时策略

3.1 初始化流程图谱:从复位释放到进入 RGB 模式共 7 个阶段

ILI7789V 的初始化不是简单写入几个寄存器,而是一个状态机驱动过程:
硬复位同步→ ②睡眠退出→ ③像素格式设定→ ④伽马校正加载→ ⑤显示方向配置→ ⑥TE 使能(可选)→ ⑦显示开启
其中第②、④、⑦步存在最小延时要求(单位:ms),而第③、⑤步需连续写入多寄存器,中间不能插入 CS 抬升。

3.2 关键寄存器写入函数与 DC 电平控制逻辑

所有寄存器操作均通过同一函数完成,区别仅在于is_cmd参数:

// ILI7789V 驱动核心函数(需在 lcd.h 中声明) void ILI7789_WriteCmd(uint8_t cmd) { HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_RESET); // DC=0: command mode HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET); // CS low HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY); HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET); // CS high } void ILI7789_WriteData(uint8_t data) { HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET); // DC=1: data mode HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(&hspi1, &data, 1, HAL_MAX_DELAY); HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET); } // 批量写入数据(用于GRAM填充或伽马表) void ILI7789_WriteDataBuffer(uint8_t *buf, uint16_t len) { HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(&hspi1, buf, len, HAL_MAX_DELAY); HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET); }

逻辑说明:HAL_SPI_Transmit()是阻塞式发送,HAL_MAX_DELAY表示无限等待 TXE 标志。若实际项目需非阻塞,应改用HAL_SPI_Transmit_IT()并配合回调函数,但初始化阶段无需考虑实时性,阻塞更可靠。LCD_DC_PinLCD_CS_Pin需在main.h中定义为对应 GPIO 引脚宏。

3.3 完整初始化序列(含精确延时与寄存器含义注释)

void ILI7789_Init(void) { // 1. 硬件复位(若 RESET 引脚已接 MCU) HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_RESET); HAL_Delay(20); // ≥10ms HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET); HAL_Delay(150); // 等待内部 PLL 锁定 // 2. 退出睡眠模式 ILI7789_WriteCmd(0x11); // Sleep Out HAL_Delay(120); // 必须 ≥120ms // 3. 设置像素格式:16-bit RGB565 ILI7789_WriteCmd(0x3A); ILI7789_WriteData(0x05); // 16-bit // 4. 设置列地址(X 轴范围:0–239) ILI7789_WriteCmd(0x2A); ILI7789_WriteData(0x00); ILI7789_WriteData(0x00); ILI7789_WriteData(0x00); ILI7789_WriteData(0xEF); // 239 // 5. 设置行地址(Y 轴范围:0–319) ILI7789_WriteCmd(0x2B); ILI7789_WriteData(0x00); ILI7789_WriteData(0x00); ILI7789_WriteData(0x01); ILI7789_WriteData(0x3F); // 319 // 6. 伽马校正(简化版,仅写关键点) ILI7789_WriteCmd(0xE0); // Positive Gamma uint8_t gamma_p[] = {0x0F,0x1F,0x1C,0x0C,0x0F,0x08,0x48,0x90, 0x8F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F}; ILI7789_WriteDataBuffer(gamma_p, 16); ILI7789_WriteCmd(0xE1); // Negative Gamma uint8_t gamma_n[] = {0x0F,0x21,0x1F,0x0F,0x0F,0x0F,0x48,0x90, 0x8F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F}; ILI7789_WriteDataBuffer(gamma_n, 16); // 7. 设置显示方向:竖屏(0x60),横屏(0xA0) ILI7789_WriteCmd(0x36); ILI7789_WriteData(0x60); // MADCTL: MY=0, MX=1, MV=0, ML=0, RGB=1 // 8. 开启显示 ILI7789_WriteCmd(0x29); // Display On }

参数说明:0x36寄存器(MADCTL)控制屏幕旋转与 RGB/BGR 顺序。0x60表示 X/Y 轴翻转组合为竖屏(240×320),若需横屏显示(320×240),应改为0xA00x050x3A寄存器中指定 16-bit 数据格式,这是 ILI7789V 最常用模式,兼容大多数嵌入式 GUI 库。

3.4 四类延时策略对比与选型建议

延时类型适用场景优点缺点本例程采用
HAL_Delay()初始化阶段(ms 级)简单、精准、不占中断资源依赖 SysTick,需配置好时基
us_delay()寄存器间微秒级间隔(如 1μs)精度高(基于 DWT 或 NOP)占用 CPU,不可在中断中调用❌(未用)
HAL_TIM_Base_Start_IT()动态刷新时帧同步可触发 DMA 传输配置复杂,增加中断负载⚠️(进阶)
FreeRTOS vTaskDelay()RTOS 环境下任务调度延时不阻塞其他任务需移植 FreeRTOS❌(裸机)

本例程全程使用HAL_Delay(),因其在初始化阶段唯一且可靠。若后续需实现动画,应迁移到定时器中断 + DMA 方式。


4. 实现 16-bit RGB565 图像填充与矩形绘制:从裸数据到可验证的视觉反馈

4.1 GRAM 写入协议与内存布局映射关系

ILI7789V 的显存(GRAM)是线性排列的 16-bit 像素数组,总容量为240 × 320 × 2 = 153,600 bytes。写入流程为:
① 发送0x2C(Memory Write)命令 → ② 连续发送像素数据(每个像素 2 字节,高位在前)。
关键约束:不能跨行写入。例如设置区域为(0,0)(239,319)后,必须一次性写满 320 行 × 240 像素,否则屏幕会错位。因此,批量填充函数需严格按行组织数据。

4.2 全屏纯色填充函数(含错误检测与性能提示)

// 填充指定区域为单一颜色(RGB565 格式:R5:G6:B5) void ILI7789_FillRect(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { uint32_t width = x2 - x1 + 1; uint32_t height = y2 - y1 + 1; uint32_t total_pixels = width * height; // 1. 设置地址窗口 ILI7789_WriteCmd(0x2A); // Column Address Set ILI7789_WriteData(x1 >> 8); ILI7789_WriteData(x1 & 0xFF); ILI7789_WriteData(x2 >> 8); ILI7789_WriteData(x2 & 0xFF); ILI7789_WriteCmd(0x2B); // Page Address Set ILI7789_WriteData(y1 >> 8); ILI7789_WriteData(y1 & 0xFF); ILI7789_WriteData(y2 >> 8); ILI7789_WriteData(y2 & 0xFF); ILI7789_WriteCmd(0x2C); // Memory Write // 2. 准备颜色数据缓冲区(避免栈溢出) #define MAX_FILL_SIZE 2048 // 一次最多填 1024 像素(2KB) static uint8_t fill_buf[MAX_FILL_SIZE]; uint16_t *p = (uint16_t*)fill_buf; for (uint32_t i = 0; i < total_pixels; i++) { p[i] = color; if ((i + 1) % (MAX_FILL_SIZE/2) == 0 || i == total_pixels - 1) { uint16_t send_len = (i + 1) % (MAX_FILL_SIZE/2) == 0 ? MAX_FILL_SIZE/2 : (i + 1) % (MAX_FILL_SIZE/2); HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(&hspi1, fill_buf, send_len * 2, HAL_MAX_DELAY); HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET); } } }

逻辑说明:该函数将color(如0xF800表示纯红)重复写入指定矩形区域。为防止大区域填充时栈溢出,采用分块发送策略:每 1024 像素(2048 字节)为一批,调用HAL_SPI_Transmit()发送。0x2A/0x2B命令设定地址窗口后,0x2C命令启动连续写入,此时 CS 必须保持低电平直至整批数据发完。

4.3 验证驱动是否成功的三步测试法

  1. 复位后观察背光:上电后 LED 引脚应输出 PWM 信号,屏背光亮起(若背光不亮,检查 PB0 是否配置为 AF_PP 或 TIM 输出模式);
  2. 执行ILI7789_FillRect(0,0,239,319,0x001F):填充纯蓝(B5),若屏幕显示均匀蓝色,证明初始化与 GRAM 写入通路正常;
  3. 叠加红色矩形ILI7789_FillRect(50,50,150,150,0xF800):若出现清晰红色方块,说明坐标映射与区域设置无误。

注意:若第二步显示灰色或绿色,大概率是0x3A寄存器未设为0x05(16-bit),或0x36寄存器方向配置错误导致 RGB 位域错位;若第三步方块位置偏移,需检查0x2A/0x2B命令中高低字节顺序是否颠倒。


5. 排查 SPI 通信失败的五大硬核线索:从示波器波形到寄存器快照

5.1 使用逻辑分析仪捕获 SPI 波形的三个必检点

当屏幕无反应时,最高效的方式是用 Saleae 或 PulseView 抓取 SCK/SDA/CS/DC 四路信号:

信号正常特征异常表现与根因
SCK方波,频率 ≈ 8 MHz,占空比 50%,空闲为低电平频率远低于配置值 → APB2 时钟未使能;无波形 → SPI 外设未初始化或引脚复用冲突
SDA在 SCK 上升沿变化,数据为 8-bit,首字节应为0x11(Sleep Out)数据全为 0x00 → CS 未拉低或 MOSI 引脚虚焊;数据乱码 → CPOL/CPHA 配置错误或线路干扰
CS每次命令/数据传输前拉低,传输结束后拉高,低电平持续时间 ≥ 100ns始终高电平 →HAL_GPIO_WritePin()调用被注释或引脚宏定义错误;脉冲过窄 → 未加HAL_Delay(1)
DC发送命令时为低,发送数据时为高,切换时刻严格对齐 CS 下降沿DC 电平不变 →LCD_DC_Pin宏指向错误引脚;切换延迟 → GPIO 输出速度未设为GPIO_SPEED_FREQ_HIGH

提示:在 CubeMX 的 GPIO 配置中,PA4/PA3 必须设为GPIO_MODE_OUTPUT_PP+GPIO_SPEED_FREQ_HIGH,否则 DC 切换延迟可达数微秒,破坏时序。

5.2 STM32 调试寄存器快照法:绕过 HAL 直查底层状态

若无示波器,可通过调试器读取 SPI 相关寄存器判断硬件状态:

寄存器(F103)偏移读取值含义故障指示
SPI1->CR10x00bit0:SPI_ENABLE,bit1:SPI_SPE,bit2:SPI_SSI,bit3:SPI_SSMSPI_ENABLE=0→ 外设未使能;SPI_SPE=0→ 主模式未激活
SPI1->SR0x0Cbit1:TXE(发送缓冲空),bit2:RXNE(接收非空),bit4:BSY(忙)TXE=0→ 数据未发出;BSY=1TXE=0→ 通信卡死,可能 CS 未拉低或从机未响应
SPI1->DR0x0C发送/接收数据寄存器写入后TXE仍为 0 → 时钟未启动或 MOSI 引脚断开

在 Keil 或 STM32CubeIDE 调试模式下,打开Peripherals → SPI1 → CR1/SR/DR视图,执行ILI7789_WriteCmd(0x11)后立即暂停,观察SR寄存器中TXE是否由 1 变 0 再变 1——若始终为 1,说明数据未真正发出。

5.3 常见“伪成功”现象与真实原因对照表

表象真实原因解决方案
屏幕偶尔闪一下白光,随后黑屏初始化序列中0x29(Display On)前缺少HAL_Delay(120),导致显示控制器未就绪0x29前添加HAL_Delay(120),参考数据手册 Table 12 “Display ON Timing”
花屏(彩色噪点)SDA 线过长未加 100Ω 串联电阻,或与 SCK 平行走线 > 5cm 引起串扰PCB 布线时 SDA/SCK 间距 ≥ 3 倍线宽,SCK 线加 33Ω 电阻,SDA 线加 100Ω 电阻
颜色严重偏色(如红变黄)0x36(MADCTL)寄存器设置错误,或0x3A(Interface Pixel Format)未设为0x05(16-bit)用逻辑分析仪确认0x36命令后紧跟0x600x3A后紧跟0x05;检查ILI7789_WriteData()是否传入正确字节
初始化耗时超 5 秒HAL_Delay()依赖的 SysTick 未配置,或SystemCoreClock未正确更新main()开头添加HAL_Init(); SystemClock_Config();,确保HAL_GetTick()返回值递增
使用不同批次屏幕效果不一ILI7789V 存在 Rev.A/Rev.B 差异,B 版本需额外写入0xB1(Frame Rate Control)寄存器查阅屏厂规格书,若为 B 版本,在0x11后插入:
ILI7789_WriteCmd(0xB1); ILI7789_WriteData(0x00);

最后一行技术内容:若需在 STM32F407 上启用 DMA 加速 GRAM 填充,应配置hdma_spi1_tx通道,将SPI1->DR地址设为外设地址,显存缓冲区地址设为内存地址,并在HAL_SPI_Transmit_DMA()调用前确保hspi1.StateHAL_SPI_STATE_READY

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询