news 2026/9/16 12:25:37

STM32F103驱动ILI9225显示屏的SPI时序与显存映射实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F103驱动ILI9225显示屏的SPI时序与显存映射实战

简介:本资源是一套基于STM32F103RCT6单片机驱动2.0寸SPI接口ILI9225 TFT显示屏的完整嵌入式源码工程,面向嵌入式初学者、单片机开发工程师及高校电子类课程实践者,解决SPI模拟并行时序驱动非原生SPI屏的核心难点。压缩包共274个文件,以74个.h头文件和62个.c源文件为主体,涵盖SPI底层驱动、ILI9225寄存器配置、图形绘制函数(点/线/矩形/字符)、屏幕初始化与方向控制等模块;另有26个.o目标文件、24个.crf编译中间文件及多个Keil工程文件(.uvproj、.uvopt)和可执行镜像(.hex、.axf),总大小3.77MB,结构完整,支持开箱即编译调试。目前已有47人学习下载。读者可直接复用该工程的硬件抽象层设计、SPI时序适配逻辑及寄存器操作范式,快速实现TFT显示功能,并参考其模块化组织方式优化自身项目架构。

1. 2.0寸SPI_ILI9225显示屏在STM32F103RCT6上跑通,不是调个库就完事——它卡在SPI时序、GPIO复用和显存映射三道坎上

你拿到一个标着“2.0寸SPI_ILI9225显示屏_ART20F140 Code for STM32 (STM32F103RCT6)”的压缩包,解压后发现是标准HAL库结构:Core、Drivers、Inc、Src,还有几个.c文件里写着ILI9225_Init()、ILI9225_SetCursor()、ILI9225_WriteData()。但烧录进去屏幕全黑,或者只闪一下白光就熄灭——这不是代码没编译成功,而是SPI物理层没对齐、LCD控制器寄存器没按手册逐字节配置、显存地址映射没匹配ILI9225的GRAM起始偏移。这个标题指向的是一类典型嵌入式显示落地场景:资源受限(STM32F103RCT6仅72MHz主频、64KB Flash、20KB RAM)、接口简陋(仅支持8位并口或3线/4线SPI)、驱动芯片老旧(ILI9225已停产多年,但因成本低仍在工控小屏中大量使用)。它不适用于LVGL或emWin这类GUI框架,而必须手撕底层时序、重写写命令/写数据函数、手动管理176×220像素的显存搬运。适合需要快速验证硬件连接、做状态指示或简易参数界面的嵌入式工程师,尤其当你手头只有ST-Link V2和一块最小系统板时。


2. SPI通信必须绕开HAL_SPI_Transmit的默认行为:ILI9225要求DCX引脚电平切换与SPI帧严格同步

ILI9225不是标准SPI外设,它没有MISO引脚,也不支持自动片选;它的通信协议本质是“伪SPI”:通过DCX(Data/Command)引脚电平区分发送的是命令还是数据,且每次发送前必须拉低CS(片选),发送后拉高,而DCX必须在CS拉低后、第一个SCK边沿前稳定。HAL库默认的HAL_SPI_Transmit()会直接发完一整段数据,中间无法插入DCX翻转——这会导致ILI9225把命令当数据、把数据当命令,初始化失败或显示错乱。

2.1 手动控制SPI+GPIO组合,实现DCX与SCK边沿对齐

不能依赖HAL_SPI_Transmit,必须拆解为单字节发送,并在每次发送前精确控制DCX。以STM32F103RCT6为例,假设SPI1用于通信,PA4为CS,PA5为DCX,PA7为SCK,PA6为MOSI:

// 在stm32f103rct6_gpio.h中定义 #define LCD_CS_HIGH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET) #define LCD_CS_LOW() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET) #define LCD_DCX_CMD() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET) // 0 = command #define LCD_DCX_DATA() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET) // 1 = data // 单字节SPI发送(阻塞式,无DMA) static void ILI9225_SPI_WriteByte(uint8_t byte) { uint8_t tx_buf = byte; uint8_t rx_buf; HAL_SPI_TransmitReceive(&hspi1, &tx_buf, &rx_buf, 1, HAL_MAX_DELAY); }

提示:这里必须用HAL_SPI_TransmitReceive()而非HAL_SPI_Transmit(),因为ILI9225虽无MISO,但SPI硬件要求MISO引脚悬空或接上拉,否则SCK可能失锁。TransmitReceive能确保SPI状态机完整运行,避免后续帧错位。

2.2 初始化序列必须严格按Datasheet第12页执行,漏掉任一寄存器将导致黑屏

ILI9225上电后需按固定顺序写入14个关键寄存器,顺序错、值错、延时不达标均会失败。常见错误是把0x0022(GRAM写使能)写在0x0007(Display Control)之前,或忽略0x0001(Driver Output Control)中VS(Vertical Scroll)位的设置。以下是精简但必填的初始化片段(含毫秒级延时):

void ILI9225_Init(void) { LCD_CS_LOW(); LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0001); // Driver Output Control HAL_Delay(5); LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0002); // LCD Driving AC Control LCD_DCX_DATA(); ILI9225_SPI_WriteByte(0x0000); LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0003); // Entry Mode LCD_DCX_DATA(); ILI9225_SPI_WriteByte(0x1030); // AM=1, ID=10, DL=0, DFM=0 LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0007); // Display Control LCD_DCX_DATA(); ILI9225_SPI_WriteByte(0x0133); // BT=1, SC=1, DT=1, GE=1, PW=3 LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0010); // Power Control 1 LCD_DCX_DATA(); ILI9225_SPI_WriteByte(0x0000); LCD_DCX_DATA(); ILI9225_SPI_WriteByte(0x0000); LCD_DCX_DATA(); ILI9225_SPI_WriteByte(0x0000); LCD_DCX_DATA(); ILI9225_SPI_WriteByte(0x0000); LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0011); // Power Control 2 LCD_DCX_DATA(); ILI9225_SPI_WriteByte(0x0000); LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0012); // Power Control 3 LCD_DCX_DATA(); ILI9225_SPI_WriteByte(0x0000); LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0013); // Power Control 4 LCD_DCX_DATA(); ILI9225_SPI_WriteByte(0x0000); LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0020); // GRAM Horizontal Address Set LCD_DCX_DATA(); ILI9225_SPI_WriteByte(0x0000); LCD_DCX_DATA(); ILI9225_SPI_WriteByte(0x0000); LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0021); // GRAM Vertical Address Set LCD_DCX_DATA(); ILI9225_SPI_WriteByte(0x0000); LCD_DCX_DATA(); ILI9225_SPI_WriteByte(0x0000); LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0022); // GRAM Write LCD_CS_HIGH(); HAL_Delay(50); }
2.2.1 关键寄存器参数说明表
寄存器地址名称推荐值作用说明
0x0001Driver Output Control0x0000设置扫描方向:GS=0(Gate Scan from G1 to G320),VLE=0(No Vertical Line Enable)
0x0003Entry Mode0x1030AM=1(Address Counter Mode: horizontal then vertical),ID=10(Increment direction: column→row)
0x0007Display Control0x0133控制背光、休眠、显示开/关;0x0133表示正常显示(D=1)、非休眠(S=1)、BT=1(Boost Voltage)
0x0020/0x0021GRAM Address Set(0,0)(175,219)ILI9225分辨率为176×220,GRAM起始地址为(0,0),结束地址为(175,219),注意索引从0开始
0x0022GRAM Write此命令后所有后续字节均写入GRAM,无需再发命令

注意:ILI9225的GRAM地址空间为176×220×2 = 77440字节(16位色),但实际可用显存需按0x0020/0x0021设定的窗口裁剪。若未正确设置地址窗口,0x0022写入的数据会覆盖到屏幕外区域,表现为局部花屏或偏移。


3. 显存管理不能靠malloc:用静态数组+坐标映射实现176×220像素的高效刷屏

STM32F103RCT6的20KB RAM中,仅剩约12KB可用(扣除栈、堆、全局变量),而ILI9225全屏显存需77440字节(≈75.6KB),远超RAM容量。因此不能开辟全屏缓冲区,必须采用“窗口刷屏”策略:只维护一个16×16像素的小缓存(512字节),配合SetCursor(x,y,w,h)函数动态定位写入区域,再用WriteGRAM()逐行发送。

3.1 定义GRAM写入窗口与坐标转换逻辑

ILI9225的GRAM地址不是线性排列,而是按列优先(Column-major)存储:先填满一列(220行),再下一列。因此(x,y)像素对应GRAM地址为y * 176 + x(注意不是x * 220 + y)。该转换必须硬编码进写函数,否则图像会90°旋转。

// 设置GRAM写入窗口:x0~x1列,y0~y1行(含端点) void ILI9225_SetWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { LCD_CS_LOW(); LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0020); // Column Address Set LCD_DCX_DATA(); ILI9225_SPI_WriteByte(x0 >> 8); // X start MSB LCD_DCX_DATA(); ILI9225_SPI_WriteByte(x0 & 0xFF); // X start LSB LCD_DCX_DATA(); ILI9225_SPI_WriteByte(x1 >> 8); // X end MSB LCD_DCX_DATA(); ILI9225_SPI_WriteByte(x1 & 0xFF); // X end LSB LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0021); // Row Address Set LCD_DCX_DATA(); ILI9225_SPI_WriteByte(y0 >> 8); // Y start MSB LCD_DCX_DATA(); ILI9225_SPI_WriteByte(y0 & 0xFF); // Y start LSB LCD_DCX_DATA(); ILI9225_SPI_WriteByte(y1 >> 8); // Y end MSB LCD_DCX_DATA(); ILI9225_SPI_WriteByte(y1 & 0xFF); // Y end LSB LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0022); // GRAM write } // 向当前窗口写入一行像素(16位RGB565格式) void ILI9225_WriteLine(uint16_t *line, uint16_t len) { LCD_CS_LOW(); for (uint16_t i = 0; i < len; i++) { LCD_DCX_DATA(); ILI9225_SPI_WriteByte(line[i] >> 8); // R5G6B5高位字节 ILI9225_SPI_WriteByte(line[i] & 0xFF); // 低位字节 } LCD_CS_HIGH(); }

3.2 实现字符渲染:用8×16点阵字模+逐像素写入

不依赖外部字体文件,直接在代码中定义ASCII字符集(0x20–0x7E),每个字符占16字节(8列×16行),每字节表示一列的8个像素(MSB在上)。渲染时按列扫描,将点阵映射到GRAM坐标:

const uint8_t font8x16[96][16] = { // 空格 0x20 {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // '0' 0x30 {0x00,0x00,0x00,0xF8,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xF8,0x00,0x00,0x00,0x00}, // ... 其他字符省略 }; void ILI9225_PutChar(uint16_t x, uint16_t y, char c, uint16_t color) { if (c < 0x20 || c > 0x7E) return; const uint8_t *p = font8x16[c - 0x20]; uint16_t line_buf[8]; // 一行8像素,每像素16位 for (uint8_t row = 0; row < 16; row++) { for (uint8_t col = 0; col < 8; col++) { uint8_t bit = (p[row] & (0x80 >> col)) ? color : 0x0000; line_buf[col] = bit; } ILI9225_SetWindow(x, y + row, x + 7, y + row); ILI9225_WriteLine(line_buf, 8); } }
3.2.1 字模数据生成方法说明

网络搜索“ILI9225 字模生成工具”可得在线生成器,输入字体(如Courier New)、大小(8×16)、输出格式(C数组),勾选“MSB first”和“Column major”。生成后需手动校验:第一行应为顶部像素,0x80表示最顶点亮,0x01表示最底点亮。错误的字模会导致文字倒置或镜像。


4. STM32F103RCT6硬件适配要点:时钟配置、引脚复用与Boot引脚实测验证

STM32F103RCT6的SPI1位于APB2总线,最高支持36MHz,但ILI9225最大SPI时钟为10MHz(Datasheet Section 6.3),因此必须降频。同时,PA4/PA5/PA6/PA7需配置为推挽输出(非复用功能),因为ILI9225不响应SPI硬件片选,CS和DCX必须由GPIO软件控制。

4.1 SPI1时钟与GPIO模式配置(CubeMX不可信,必须手改)

CubeMX生成的代码常将PA6/PA7设为AF_PP(复用推挽),但ILI9225不需要MISO,且PA6若设为复用会强制启用内部上拉,导致SPI空闲时MOSI电平异常。正确做法是:

// 在MX_GPIO_Init()中修改 __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = 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); // SPI1初始化(在MX_SPI1_Init中) hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; // 实际只用MOSI,但硬件要求2线模式 hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL=0 hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA=0,采样在第一个边沿 hspi1.Init.NSS = SPI_NSS_SOFT; // 必须软NSS,禁用硬件CS hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; // APB2=72MHz → SCK=9MHz hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

提示:SPI_BAUDRATEPRESCALER_8对应72MHz/8=9MHz,在ILI9225允许的10MHz内;若设为_4(18MHz)则屏幕闪烁或乱码。实测中,超过9.5MHz即不稳定。

4.2 Boot引脚电平确认:避免程序不启动的隐形故障

STM32F103RCT6的启动模式由BOOT0和BOOT1引脚决定。常见错误是BOOT0悬空(未接10kΩ下拉),导致上电时随机进入系统存储器启动模式(USB DFU),Keil下载后无法运行。必须实测:

BOOT1BOOT0启动模式推荐接法
00主闪存(Flash)BOOT0接地,BOOT1接地(默认)
01系统存储器(System Memory)BOOT0接3.3V(调试用)
10内置SRAM不常用

用万用表测量PA14(BOOT0)对地电压,应为0V;若为1.8V以上,检查原理图是否漏接下拉电阻。此问题与ILI9225无关,但会导致“代码烧录成功却无任何反应”,浪费数小时排查SPI。


5. 验证与排错:用逻辑分析仪抓SPI波形,定位DCX/CSPIN时序偏差

当屏幕全黑或显示残影时,最有效手段是用Saleae Logic或类似设备抓取CS、DCX、SCK、MOSI四路信号,验证三个关键时间点:
① CS下降沿到DCX稳定的时间 ≥ 10ns(Datasheet tCSX);
② DCX稳定到第一个SCK上升沿的时间 ≥ 20ns(tDCX);
③ 每次发送命令/数据前CS必须重新拉低(ILI9225无自动片选保持)。

5.1 典型故障波形与修复方案

故障现象波形特征根本原因修复代码位置
屏幕全黑,无任何反应CS始终高电平,或仅在初始化时拉低一次LCD_CS_LOW()未被调用,或HAL_GPIO_WritePin()参数错误检查ILI9225_Init()开头是否漏写LCD_CS_LOW()
显示内容上下颠倒DCX在CS拉高后才翻转,或GRAM地址窗口Y轴设反ILI9225_SetWindow()中y0/y1传参颠倒,或字模扫描方向错修改ILI9225_SetWindow(y0,y1)ILI9225_SetWindow(y0,y1),确保y0<y1
文字有重影、颜色发虚SPI时钟过快(>10MHz)或SCK空闲电平错误hspi1.Init.CLKPolarity设为SPI_POLARITY_HIGH,导致采样相位错改为SPI_POLARITY_LOW,并确认CLKPhase=SPI_PHASE_1EDGE

5.2 最小可验证程序:只点亮一个像素

不要一上来就跑完整UI,先验证硬件链路:

int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_SPI1_Init(); ILI9225_Init(); // 点亮坐标(0,0)处一个红色像素(RGB565: 0xF800) ILI9225_SetWindow(0, 0, 0, 0); uint16_t red = 0xF800; LCD_CS_LOW(); LCD_DCX_CMD(); ILI9225_SPI_WriteByte(0x0022); LCD_DCX_DATA(); ILI9225_SPI_WriteByte(red >> 8); LCD_DCX_DATA(); ILI9225_SPI_WriteByte(red & 0xFF); LCD_CS_HIGH(); while (1) { } }

烧录后若左上角出现一个红点,证明SPI通信、DCX控制、GRAM写入全部正确;若无反应,则问题必在硬件连接(检查VCC/GND/LCD_VCC是否3.3V)或Boot引脚配置。

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

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

AI芯片设计实战:NPU调用、内存墙与异构协同硬核指南

1. 项目概述&#xff1a;这不是劝退指南&#xff0c;而是一份芯片设计现场的“生存手记”“AI芯片设计从入门到放弃”——看到这个标题&#xff0c;我笑了。不是笑它夸张&#xff0c;而是笑它太真实。我在某头部AI芯片初创公司干了七年&#xff0c;从RTL工程师干到架构组技术负…

作者头像 李华
网站建设 2026/9/16 12:20:01

AI学术写作助手:提升科研效率的智能解决方案

1. 项目概述&#xff1a;当AI遇上学术写作作为一名在科研领域摸爬滚打多年的"老油条"&#xff0c;我深知学术写作中的痛点——从文献综述的枯燥整理到数据可视化的反复调整&#xff0c;从格式规范的琐碎校对到术语表达的精准拿捏。去年团队里新来的研究生小王&#x…

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

2026年绵阳装修公司排行榜 - 专业装修服务大全

绵阳装修公司排行榜 - 专业装修服务大全打算在绵阳做新房装修、旧房改造的朋友&#xff0c;大多会搜索绵阳装修公司排名&#xff0c;参考榜单筛选本地靠谱的装修团队。但只看网上流传的绵阳装修公司排名并不够&#xff0c;还需要学会甄别榜单是否真实客观。一份靠谱的绵阳装修公…

作者头像 李华
网站建设 2026/9/16 12:15:33

Python轻量级工业数据采集系统设计与优化

1. 项目背景与核心价值在制造业数字化转型浪潮中&#xff0c;数据采集系统如同工厂的神经系统。我去年为某汽车零部件企业实施智能改造时&#xff0c;发现传统SCADA系统存在三大痛点&#xff1a;部署成本高&#xff08;单台工控机投入超2万元&#xff09;、协议兼容性差&#x…

作者头像 李华