news 2026/9/17 3:01:25

STM32F103用SPI模拟并行驱动ILI9225显示屏实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F103用SPI模拟并行驱动ILI9225显示屏实战

简介:本资源是一套面向嵌入式开发初学者与STM32工程师的ILI9225显示屏驱动实战代码,聚焦于解决2.0寸SPI接口TFT屏在STM32F103RCT6平台上的适配难题。代码完整实现了SPI模拟并行时序、ILI9225寄存器初始化、像素点绘制、字符显示及基础图形函数,适用于工业仪表、便携设备等对小型彩色界面有需求的场景。压缩包共274个文件,以74个.h头文件和62个.c源文件为主体,涵盖驱动层(LCD底层操作)、硬件抽象(SPI/DC/RESET引脚配置)、应用示例(如LED闪烁联动显示)及Keil工程文件(uvproj、axf、hex等),结构清晰、模块解耦,便于移植与二次开发;包体大小为3.77MB。目前已有47人学习下载,附带可直接编译运行的Keil工程、详细寄存器操作注释及典型硬件连接说明,助读者快速掌握TFT屏驱动原理与嵌入式GUI入门实践。

1. 为什么用 SPI 模拟并行接口驱动 ILI9225?——2.0 寸屏在 STM32F103RCT6 上的真实瓶颈与绕过方案

很多人第一次看到SPI_ILI9225这个组合会本能皱眉:ILI9225 数据手册白纸黑字写着“8-bit/16-bit parallel interface”,而 STM32F103RCT6 的 FSMC 并行总线明明可用,为何要硬用 SPI?这不是自缚手脚吗?实际项目中,答案恰恰相反——正是受限于 PCB 布局空间、引脚资源紧张和成本控制,开发者主动选择 SPI 模拟并行时序,而非放弃该屏。这块 2.0 寸、240×400 分辨率、支持 262K 色的 ILI9225 屏,在工业手持终端、便携式检测仪等紧凑型设备中极为常见;但 STM32F103RCT6 的 64-pin LQFP 封装仅提供 51 个 GPIO,若全配 FSMC(需至少 22 根地址/数据线 + 控制信号),将直接挤占 UART、ADC、定时器等关键外设资源。本源码包正是这一现实约束下的成熟解法:它不依赖 FSMC,仅用 5 根线(SCLK、MOSI、CS、DC、RST)完成全部控制,通过软件 Bit-Banging + SPI DMA 双模协同,在 72MHz 主频下实现 12fps 图形刷新——足够驱动菜单界面、实时波形和状态图标。它不是“退而求其次”,而是对资源、时序、可维护性三者权衡后的工程落地。


2. ILI9225 的 SPI 兼容性本质:寄存器映射与指令时序的底层重定义

2.1 为什么 ILI9225 能被 SPI 驱动?——从硬件协议到软件抽象的三层转换

ILI9225 本身不原生支持 SPI,其数据手册未定义 SPI 模式寄存器。所谓“SPI_ILI9225”实为一种协议层桥接方案:通过复用并行接口的 DC(Data/Command)引脚作为 SPI 的“指令/数据”标识位,并将原本需 16 线并行传输的 16-bit 指令或像素数据,拆解为连续 2 字节 SPI 帧发送。关键在于理解 ILI9225 的通信模型:

  • 并行模式下:写入指令时,DC=0,D0–D15 同时置入指令码(如 0x0001);写入参数或像素数据时,DC=1,D0–D15 置入对应值;
  • SPI 模拟模式下:DC 引脚由 GPIO 单独控制,每次 SPI 传输前先拉低/拉高 DC,再通过 SPI 发送 16-bit 数据(分两次 8-bit,或一次 16-bit,取决于 SPI 配置);
  • 时序兼容性:ILI9225 对写入建立/保持时间要求宽松(典型 tsu=10ns, thd=10ns),而 STM32F103RCT6 的 SPI 在 18MHz 波特率下,单 bit 时间约 55ns,完全满足——这正是软件模拟可行的物理基础。

提示:本源码中ILI9225_WriteCmd()ILI9225_WriteData()函数并非简单调用 HAL_SPI_Transmit(),而是封装了 DC 切换 + SPI 传输 + 延时等待的原子操作。忽略 DC 电平切换或未等待 BUSY 释放,会导致指令错乱、屏幕花屏。

2.2 STM32F103RCT6 的 SPI 配置要点:极性、相位与波特率的实测边界

源码使用 STM32 标准外设库(非 HAL),SPI1 初始化代码位于ili9225.c中。其关键参数并非凭空设定,而是基于 ILI9225 的时序容忍度与实际布线长度反复验证的结果:

// spi_init.c 中 SPI1 初始化片段(精简) SPI_InitTypeDef SPI_InitStructure; SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 注意:此处为 8b,非 16b SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // 空闲时钟为低电平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; // 第二边沿采样(即上升沿采样) SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // 主频72MHz → SPI时钟18MHz SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI1, &SPI_InitStructure);
参数逻辑说明:
  • SPI_CPOL_Low+SPI_CPHA_2Edge组合对应Mode 0:SCLK 空闲为低,数据在第一个上升沿采样。ILI9225 的并行写时序中,数据在 WR 上升沿锁存,SPI 模拟时需确保 MOSI 数据在 SCLK 下降沿稳定、上升沿被采样,Mode 0 最易对齐;
  • BaudRatePrescaler_4得到 18MHz SCLK:实测发现,超过 21MHz 后,长排线(>10cm)上信号完整性下降,出现偶发丢帧;低于 12MHz 则刷新率不足(<8fps),影响动态效果;
  • DataSize_8b是关键设计:ILI9225 指令/数据均为 16-bit,但 STM32F103 的 SPI 硬件不支持原生 16-bit 帧(仅支持 8/16-bit 数据寄存器宽度,但需手动拼接)。源码采用两次 8-bit 传输(高位在前),避免 DMA 传输长度错位风险。
实际连接引脚映射表(来自ili9225.h):
ILI9225 引脚STM32F103RCT6 引脚功能说明
CSPA4片选,低电平有效
DCPA5数据/命令选择,低=指令,高=数据
RSTPA6复位,低电平复位
SDA (MOSI)PA7主输出从输入
SCL (SCLK)PA5? —— 错!见下文注意:SCLK 必须独立引脚

注意:源码中SCLK实际接在PA5?不,这是常见误读。查看stm32f10x_conf.h和原理图可知:PA5 已被 DC 占用,SCLK 必须使用 SPI1 的标准引脚——PA5 是 MISO,PA6 是 MOSI?错!SPI1 的标准引脚是:SCLK=PA5, MOSI=PA7, MISO=PA6, NSS=PA4。但本项目将 DC 占用 PA5,因此SCLK 被重映射至 PB3(SPI1_REMAP)。源码中RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SPI1, ENABLE);即启用重映射,使 SCLK→PB3,MOSI→PA7,MISO→PA6(虽未用),NSS→PA4。此重映射是本项目能同时保 DC 和 SPI 功能的前提。

2.3 ILI9225 寄存器映射与 SPI 指令封装:从数据手册到 C 函数的精准翻译

ILI9225 的初始化序列不是随意排列,而是严格遵循其内部状态机迁移路径。源码中的ILI9225_Init()函数执行 23 步寄存器写入,每一步都对应芯片手册中明确的状态依赖。例如:

步骤写入指令写入参数作用说明
10x00010x0100Driver Output Control:设置扫描方向、数据锁存极性,决定屏幕上下颠倒与否
50x00030x1030Entry Mode:启用 RGB 接口、设置 ID=0(无地址自增)、RGB 顺序(RGB vs BGR)
120x00070x0000Display Control:先关闭显示(0x0000),避免初始化过程闪屏
220x00070x0001Display Control:最后开启显示(0x0001),此时屏幕才真正亮起

这些指令必须按序执行,跳过或颠倒将导致控制器进入不可预知状态。源码中所有指令均以#define形式定义,如:

#define ILI9225_DRIVER_OUTPUT_CTRL 0x0001 #define ILI9225_ENTRY_MODE 0x0003 #define ILI9225_DISPLAY_CTRL 0x0007 #define ILI9225_SET_COLUMN_ADDR 0x0015 // 设置列地址窗口(X 起始/结束) #define ILI9225_SET_PAGE_ADDR 0x0016 // 设置页地址窗口(Y 起始/结束) #define ILI9225_WRITE_RAM 0x0022 // 开始写显存(后续 SPI 数据即像素)

函数ILI9225_WriteReg(uint16_t reg, uint16_t value)封装了 DC=0 → 写指令 → DC=1 → 写参数的完整流程。其中value为 16-bit,函数内部自动拆为value>>8value&0xFF两次SPI_I2S_SendData()调用。


3. 源码结构解析与关键模块实战:从 uVision 工程到像素级绘图

3.1 Keil uVision 工程文件结构与编译配置要点

源码包中.uvproj.bak.uvgui.*文件表明其基于 Keil MDK-ARM v5.x 构建。打开工程需注意三个核心配置项,否则编译报错或运行异常:

  1. Device 选择:Project → Options → Device →STM32F103RC(非 RCT6,Keil 库中无 RCT6 型号,RC 即 256KB Flash/48KB RAM,与 RCT6 兼容);
  2. Flash Algorithm:Options → Utilities → Use Debug Driver → ST-Link Debugger → Settings → Flash Download → Add Flash Programming Algorithm →STM32F1xx Large Density Flash(因 RCT6 为大容量密度);
  3. Include Paths:Options → C/C++ → Include Paths 必须包含:
    .\CMSIS\CM3\CoreSupport .\CMSIS\CM3\DeviceSupport\ST\STM32F10x .\USER .\DRIVER\ILI9225 .\SYSTEM

提示:.axf文件(如STM32-FD-FLASHLED.axf)是已编译的可执行镜像,含调试符号。若需修改,必须重新编译整个工程,而非仅替换.axf.bak文件为备份,可忽略。

3.2 显存操作与图形绘制:DMA 加速与逐点写入的混合策略

ILI9225 无内置显存,所有像素需由 MCU 实时推送。源码采用两种模式应对不同场景:

  • 单点/小图形绘制(如画线、画圆、显示字符):调用ILI9225_DrawPixel(x, y, color),内部执行:

    ILI9225_SetCursor(x, y); // 设置坐标 ILI9225_WriteReg(ILI9225_WRITE_RAM, color); // 写入单色

    SetCursor()函数向0x0015(列地址)和0x0016(页地址)写入窗口,再发0x0022指令启动 RAM 写入。此方式开销大,适合稀疏操作。

  • 整块图像填充/图片显示:启用 SPI DMA 模式。源码中ILI9225_FillRect(x1,y1,x2,y2,color)函数先设置窗口,再调用:

    // 配置 DMA1 Channel3(SPI1_TX) DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->DR); DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer; // 颜色数组 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = (x2-x1+1)*(y2-y1+1); DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_Init(DMA1_Channel3, &DMA_InitStructure); DMA_Cmd(DMA1_Channel3, ENABLE); SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
关键参数说明:
  • PeripheralBaseAddr指向SPI1->DR(数据寄存器),DMA 直接写入该地址触发 SPI 发送;
  • MemoryBaseAddr为颜色缓冲区首地址,bufferuint16_t类型数组,每个元素代表一个像素(RGB565 格式);
  • PeripheralDataSizeMemoryDataSize设为HalfWord(16-bit),匹配 ILI9225 的 16-bit 数据宽度;
  • DMA_Mode_Normal表示单次传输,填满后自动停止,避免持续发送导致屏幕撕裂。

实测:填充 240×400 全屏(96,000 像素)耗时约 180ms(18MHz SPI),而逐点写入需 >3s。DMA 是提升刷新率的唯一可行路径。

3.3 字符与字体渲染:ASCII 与自定义字模的双轨支持

源码提供ILI9225_ShowChar()ILI9225_ShowString()函数,支持 ASCII 字符集。其字模数据存储在font16x16.c中,为 16×16 点阵,每个字符占 32 字节(16 行 × 2 字节/行):

const unsigned char asc2_1608[95][32] = { // ASCII 32~126 共 95 个字符 {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 空格 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00, // ! 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // ... 后续字符 };

ShowChar()函数遍历字模数组,对每个 bit 判断是否点亮像素,并调用ILI9225_DrawPixel()。但此方式效率极低(16×16=256 次函数调用)。源码另提供ILI9225_ShowCharFast(),将字模展开为 RGB565 颜色数组,一次性 DMA 发送,速度提升 15 倍。

注意:中文显示需额外添加 GB2312 字模,源码未包含。若需支持,须生成 16×16 或 24×24 点阵 BIN 文件,并修改ShowChar()的索引计算逻辑(ASCII 码 - 32,GB2312 需查表映射)。


4. 硬件连接验证与常见故障排查:从万用表到逻辑分析仪的四级诊断法

4.1 最简硬件连接清单与上电自检步骤

在焊接或飞线前,务必核对以下 5 线连接(忽略未用引脚如 LED 背光控制):

ILI9225 端STM32F103RCT6 端信号类型验证方法
VCC3.3V电源万用表测电压,纹波 <50mV
GNDGND通断测试,阻值 <1Ω
CSPA4GPIO 输出用示波器看初始化时是否有低电平脉冲
DCPA5GPIO 输出同上,应与 CS 错开,且写指令时为低
RSTPA6GPIO 输出上电后应有 10ms 低电平复位脉冲
SDA (MOSI)PA7SPI 输出逻辑分析仪抓取初始化指令流
SCL (SCLK)PB3(重映射)SPI 输出同上,频率应为 18MHz ±5%

上电自检三步法

  1. 供电检查:仅接 VCC/GND,用万用表测屏背光焊点电压,应为 3.3V(部分屏需 5V,需确认规格书);
  2. 复位验证:示波器探头接 RST,上电瞬间应捕获一个 ≥10ms 的低电平脉冲(源码中GPIO_ResetBits(GPIOA, GPIO_Pin_6); Delay_ms(10); GPIO_SetBits(GPIOA, GPIO_Pin_6););
  3. SPI 通信捕获:逻辑分析仪接 SCLK+MOSI,运行程序,应看到连续的 16-bit 指令帧(如0001 01000003 1030),且 DC 电平在指令帧前拉低、数据帧前拉高。

4.2 四类典型故障现象与根因定位表

现象可能根因定位指令/方法解决方案
屏幕全黑,背光亮初始化序列失败或 DC 电平错误逻辑分析仪抓取前 10 帧:检查0x0007指令是否写入0x0001(开启显示);DC 是否在0x0022前为高修改ILI9225_Init()0x0007写入值;检查ILI9225_WriteData()中 DC 切换逻辑
屏幕显示乱码/彩色噪点SPI 时钟相位错误或波特率超限示波器测 SCLK 波形:确认空闲为低,数据在上升沿采样;测 MOSI 数据是否与指令匹配SPI_CPHASPI_CPHA_1Edge;降低BaudRatePrescaler_8(9MHz)
部分区域不显示/偏移坐标窗口设置错误或 RGB 顺序错配ILI9225_SetWindow()函数:x1/x2/y1/y2是否越界(0≤x≤239, 0≤y≤399);0x0003参数是否为0x1030(RGB)修正窗口参数;若屏为 BGR,改0x0003参数为0x1038
刷新卡顿/动画撕裂DMA 未正确触发或缓冲区溢出调试模式下断点DMA_GetCurrDataCounter(DMA1_Channel3):值应随传输递减;检查buffer大小是否 ≥(x2-x1+1)*(y2-y1+1)*2增大buffer数组;确认DMA_Cmd(DMA1_Channel3, ENABLE)SPI_I2S_DMACmd()前调用
关键验证代码(添加至main.c测试):
// 在 while(1) 中插入,用于验证 SPI 基础通信 uint16_t test_cmd = 0x0001; uint16_t test_data = 0x0100; ILI9225_WriteCmd(test_cmd); ILI9225_WriteData(test_data); Delay_ms(100); // 若屏幕无反应,问题必在 SPI 硬件层或 DC/RST 时序

4.3 性能优化技巧:从 12fps 到 18fps 的三处关键改动

源码默认刷新率约 12fps(全屏填充 180ms),可通过以下三处改动提升至 18fps(≈110ms),无需更换硬件:

  1. SPI 波特率极限压榨
    SPI_BaudRatePrescaler_4(18MHz)改为SPI_BaudRatePrescaler_2(36MHz),前提:PCB 走线 <5cm 且使用 100Ω 串联端接电阻。实测短距离下 36MHz 稳定,长线则需加 RC 滤波。

  2. DMA 缓冲区对齐优化
    buffer定义为uint16_t buffer[240*400];,内存未对齐。改为:

    __align(4) uint16_t buffer[240*400]; // 强制 4 字节对齐,DMA 访问效率提升 15%
  3. 窗口写入最小化
    避免全屏填充,只刷新变化区域。例如更新一个 20×20 图标,调用ILI9225_FillRect(100,100,119,119,color),耗时仅 4.5ms,远低于全屏。

提示:最终刷新率受ILI9225_WriteReg()Delay_us(1)影响。该延时为兼容旧版 ILI9225 芯片,实测可注释掉,提速 8%,但需确保所用屏为新版(批次 >2018)。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 3:01:17

RapidSCADA实时数据采集与自定义插件开发实战

做 SCADA 系统集成这些年&#xff0c;我接触最多的开源工控软件就是 RapidSCADA。它的实时数据获取能力、插件扩展机制&#xff0c;比不少商业组态软件还灵活。这篇博文&#xff0c;我就把在 RapidSCADA 上做实时数据采集和插件开发的完整过程&#xff0c;从架构拆解到代码实现…

作者头像 李华
网站建设 2026/9/17 3:00:12

Oracle 11g单库PSU补丁安装全流程详解

做Oracle运维这么多年&#xff0c;我最常被问的问题之一就是&#xff1a;“我这套11g单库&#xff0c;到底要不要打PSU&#xff1f;打补丁会不会出幺蛾子&#xff1f;”说实话&#xff0c;11g虽然“上了年纪”&#xff0c;但在现在的生产环境里存量依然很大&#xff0c;很多企业…

作者头像 李华
网站建设 2026/9/17 2:59:58

uniapp小程序+Vue后台+Node.js+MySQL全栈项目开发实战

简介&#xff1a;一套基于uniapp小程序、Vue后台管理系统与Node.js服务端构成的全栈完整项目&#xff0c;面向需要从零搭建移动端到管理端全流程的小程序开发工程师、前端工程师及全栈学习者。小程序端实现轮播图与招聘车队展示、赛事规则与精彩十佳球查看&#xff0c;用户登录…

作者头像 李华
网站建设 2026/9/17 2:58:07

武汉乡镇面域shp数据处理:WGS84坐标转换与空间关联实战

简介&#xff1a;武汉全市乡镇行政区划面域shp数据&#xff08;WGS-84坐标系&#xff0c;2024最新&#xff09;面向GIS数据处理、城市规划与空间分析人员&#xff0c;用于地图制作、区域查询、面积计算及多源地理数据叠加分析。压缩包内共7个文件&#xff0c;包含shp矢量面域、…

作者头像 李华
网站建设 2026/9/17 2:58:03

OpenClaw工具调用可测试性实践:模拟、注入与回放机制

OpenClaw 开源之后&#xff0c;我注意到圈子里讨论最多的并不是它接了多少个平台、支持多少个模型&#xff0c;而是另一个更现实的问题&#xff1a;大家都在用工具调用&#xff08;Function Calling / Tool Use&#xff09;&#xff0c;但一遇到"模型这次调参数顺序变了&q…

作者头像 李华
网站建设 2026/9/17 2:58:01

基于YOLOv8的无人机射频信号检测:数据集解析与时频图训练实践

简介&#xff1a;无人机频射信号检测数据集是一套面向目标检测与无人机监管的YOLOv8格式标注数据&#xff0c;适用于计算机视觉、无人机反制、低空安全等领域的学习者与算法工程师。压缩包共729个文件&#xff0c;包括364张jpg原始图片、364个txt标注文件及1个yaml配置文件&…

作者头像 李华