news 2026/9/1 10:28:09

STM32驱动ILI9341屏幕:HAL库、触摸与DMA优化实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32驱动ILI9341屏幕:HAL库、触摸与DMA优化实战

简介:本资源是一套面向嵌入式初学者与STM32进阶开发者的ILI9341 TFT LCD驱动实践方案,聚焦于降低图形显示开发门槛,解决无DMA/中断依赖下实现稳定触控与高效绘图的常见痛点。资源包共35个文件,含13个头文件(.h)定义接口与配置、11个源文件(.c)实现驱动核心逻辑(如SPI通信、图形渲染、触摸校准)、5个备份文件(.zbak)及README.md等辅助文档,整体压缩后仅12MB,轻量易集成。已有149人学习下载,适用于STM32F7系列(以Nucleo-F746ZG为参考平台)的课程实验、毕业设计或小型HMI原型开发。读者可直接获取模块化程度高、结构清晰的完整工程模板:包含HAL库初始化框架、ILI9341主控驱动、增强型GFX图形库(支持字体/几何图形绘制)及电阻式触摸屏控制模块,并附带详细调用范例与环境配置指引,便于快速移植、裁剪功能或二次扩展。 一次性项目搞定的屏,我前前后后刷过不下十块,从最早的标准库手撕寄存器,到后来全面切到HAL库,中间踩过的坑能装一箩筐。这篇就围绕STM32 + ILI9341这个经典组合,把HAL库驱动写法、触摸屏控制、以及常见的性能优化和调试方法一次说透。不管你是刚入门想点亮一块屏,还是已经在做带触摸交互的小项目,这篇都能给你一些直接能用的东西。

1. 项目定位:为什么要费劲写一个ILI9341驱动库

1.1 这块屏幕能做什么,解决了什么问题

ILI9341是当前小尺寸TFT LCD里最主流的控制芯片之一,常见的2.4寸、2.8寸、3.2寸屏,分辨率基本都是240x320,用的就是这颗IC。它支持SPI、8080并口、RGB等多达三种接口,但在STM32小项目里,最常用的就是SPI接口,因为省引脚、连线简单,一个SPI外设加几个GPIO就能把屏点亮。

这块屏能解决的问题很具体:你需要一个带色彩的交互界面时,它比OLED屏幕信息量更大,显示中文、图片、小图标都更从容;它比串口屏便宜且可控性高,想怎么画就怎么画,刷新逻辑完全掌握在你自己手里。配合触摸屏,就能做一个带按钮、滑条、状态显示的人机界面,像温度控制面板、小型仪表盘、菜单设置页这种需求,它都能扛。

1.2 HAL库与标准库之争,我的选型理由

我发现不少朋友还在纠结要不要学HAL库,尤其是看过江科大风格的标准库教程入门的,总觉得HAL库包装太多、代码啰嗦。但说实话,2024年了,ST官方已经停止维护标准库,新出的芯片基本只有HAL库和LL库可选。你迟早要切过来。

HAL库的好处在于抽象层统一。同样是SPI发送,标准库里你得操作SPI1->DR、查SPI1->SR,换一颗芯片可能寄存器就不一样了;HAL库里HAL_SPI_Transmit()基本通用,代码迁移动成本极低。坏处就是看似"慢",但实际测下来,在48MHz~72MHz主频下,SPI刷屏的瓶颈往往在液晶屏自身的像素时钟上,而不是HAL库的封装开销。后面我会用DMA把CPU从刷屏里彻底解放出来,那点封装损耗基本可以忽略。

所以这个项目我直接用STM32CubeMX生成HAL工程,再用SPI+DMA刷新。整个代码分两层:底层跟硬件相关的封装得薄一点,上层显示逻辑写得厚一点,以后就算换了屏或者换了主控,改底层就行。

2. 硬件层:SPI接线与ILI9341的初始化密码

2.1 SPI参数怎么配:时钟、极性与相位

ILI9341的SPI接口支持Mode 0(CPOL=0,CPHA=0)和Mode 3(CPOL=1,CPHA=1),我习惯用Mode 0,因为大多数STM32默认SPI配置就是Mode 0,不容易出错。

时钟频率方面,ILI9341的数据手册标称SPI写时钟可以达到10MHz左右,实际用起来:

  • 普通驱动板,建议先从1MHz起步,点亮之后再往上调
  • 排除干扰之后,F103平台跑到9MHz没有任何问题
  • F407以上平台,把SPI时钟写到18MHz,快速刷新场景下也稳定

我实测过直接拉到最高时钟会导致花屏,因为杜邦线太长、或者模块上拉电阻匹配不好时,信号沿不干净。建议PCB走线或者短线连接时满速跑,飞线连接时适当降速。

CubeMX里的配置大概是这样的思路:SPI1全双工主机、8位数据、MSB先行、软件NSS(因为ILI9341的CS是普通GPIO控制更灵活),分频系数按你的主频去算。

2.2 初始化序列:那些看着像魔法的寄存器命令

ILI9341的初始化序列是大家最容易抄错的地方。原因是市面上流传的初始化代码版本太多,有标准寄存器版本,有厂家精简版,不同屏幕模组对颜色格式、扫描方向、Gamma校正的要求还不一样。

我说一下我的处理方式:直接拿Adafruit或ST官方给的标准初始化序列打底,把关键的寄存器抽出来解释一下:

  • 0x01软复位,开场必发
  • 0x11退出睡眠模式,发完要延时120ms以上,不然屏幕不亮
  • 0x3A像素格式,设置RGB565或RGB666,我固定用0x55,即16位色RGB565
  • 0x36MADCTL,控制扫描方向和颜色顺序,这个直接决定画面是不是倒的、颜色是不是反的
  • 0x29打开显示

常见新手操作是三四十行初始化代码一股脑全发,发完发现画面歪了或者颜色不对,然后开始怀疑屏坏了。其实只要抓住上面这几个关键寄存器,大部分问题都能定位。扫描方向错了就改0x36参数,颜色不对基本就是RGB还是BGR的开关没拨对。

2.3 供电与背光:新手最容易忽略的坑

我见过太多人配置好了所有GPIO和SPI,屏幕就是不亮,仔细一看背光引脚悬空了。背光控制引脚(LEDA/BLK)需要接一个GPIO拉高,或者直接接3.3V。

供电电流这块也得注意。2.8寸屏背光全开时电流能到50mA左右,如果用STM32芯片的3.3V LDO直接带,电压会掉,影响SPI信号电平。我一般建议单独用一颗LDO给屏幕供电,或者至少保证3.3V供电余量充足。

还有一个坑是逻辑电平问题。很多便宜的ILI9341模块板上自带了电平转换电路,但有的没有,如果STM32是5V供电、IO输出5V电平,直接把SPI信号灌到ILI9341里,可能烧坏模块。虽然大部分模块用3.3V供电还带保护,但稳妥起见,在接线前看一下模块背面有没有TXB0104或者类似的电平转换芯片。

3. 驱动库实现:从HAL底层封装到API设计

3.1 最底层的两个函数:写命令与写数据

驱动库的核心其实就两个函数:写命令和写数据。ILI9341的数据/命令选择由DC引脚的电平决定——DC低电平发的是命令,高电平发的是数据。配合CS片选、WR上升沿锁存,一个完整的SPI写周期就是拉低CS、设置DC、SPI发送字节、拉高CS。

HAL库写起来大概是这个感觉:

void LCD_WriteCmd(uint8_t cmd) { LCD_CS_LOW(); LCD_DC_LOW(); HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY); LCD_CS_HIGH(); } void LCD_WriteData(uint8_t data) { LCD_CS_LOW(); LCD_DC_HIGH(); HAL_SPI_Transmit(&hspi1, &data, 1, HAL_MAX_DELAY); LCD_CS_HIGH(); }

这个实现没问题,但如果你后面要刷整屏,一字节一字节地发太慢了。所以通常还会加一个批量写数据函数,直接传缓冲区指针和长度:

void LCD_WriteDataBuffer(uint8_t *buf, uint32_t len) { LCD_CS_LOW(); LCD_DC_HIGH(); HAL_SPI_Transmit(&hspi1, buf, len, HAL_MAX_DELAY); LCD_CS_HIGH(); }

有了这三个函数,后面所有画点画线都是在这之上叠加。

需要注意的一点是,HAL_SPI_TransmitTimeout参数,我一般填HAL_MAX_DELAY,因为SPI主机模式发送是同步阻塞的,正常情况下不会超时。如果你在中断里调用并且填了有限超时,一旦系统繁忙就会返回HAL_TIMEOUT,然后后续状态机混乱。这个坑我在早期调试串口空闲中断时也踩过类似的。

3.2 显示驱动框架的层级划分

一个能长期维护的显示驱动,不能把所有函数堆在一个文件里。我习惯分成三层:

  • 硬件抽象层(HAL底层):管SPI、GPIO、DMA,只暴露LCD_WriteCmdLCD_WriteDataBuffer这几个函数
  • 驱动核心层:管ILI9341的寄存器操作,坐标窗口设置、颜色填充、屏幕旋转、清屏等
  • 应用层:画点、画线、画矩形、显示字符、显示图片、UI组件

分层的好处是:你调试触摸屏时不需要动显示底层,你换主控平台时只需要改硬件抽象层。很多人的代码写成一坨,后期加个按钮都要在几百行里找函数,就是因为没分层。

坐标窗口这个机制是ILI9341刷屏的核心。它的原理是:先告诉屏幕一个矩形区域的范围(CASET行地址、PASET列地址),之后所有写入的像素数据会自动填充到这个矩形中。我封装了这样一个函数:

void LCD_SetWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { LCD_WriteCmd(0x2A); // CASET LCD_WriteData(x0 >> 8); LCD_WriteData(x0 & 0xFF); LCD_WriteData(x1 >> 8); LCD_WriteData(x1 & 0xFF); LCD_WriteCmd(0x2B); // PASET LCD_WriteData(y0 >> 8); LCD_WriteData(y0 & 0xFF); LCD_WriteData(y1 >> 8); LCD_WriteData(y1 & 0xFF); LCD_WriteCmd(0x2C); // RAMWR }

设置完窗口之后,紧接着推送的像素数据就会按从左到右、从上到下的顺序,填满这个窗口。这个机制是后面所有加速手段的基础。

3.3 画点、画线、填充的加速技巧

画点最简单,设置窗口为单像素点,然后写一个RGB565颜色值。但刷整屏如果逐点画,要发240x320=76800次设置窗口命令,光是命令开销就够喝一壶了。所以实际写驱动时,画大面积色块都是先设置一个窗口,然后连续写数据。

画一条水平线,只要设置一个宽为len、高为1的窗口,然后连续发len个颜色值。画垂直线类似。这样比逐点画快几十倍。

填充整个屏幕或者某个矩形,也是同样的道理,只不过窗口更大。我把填充函数封装成:

void LCD_FillRect(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color) { uint32_t pixel_count = (x1 - x0 + 1) * (y1 - y0 + 1); uint32_t i; LCD_SetWindow(x0, y0, x1, y1); LCD_DC_HIGH(); LCD_CS_LOW(); for (i = 0; i < pixel_count; i++) { HAL_SPI_Transmit(&hspi1, (uint8_t *)&color, 2, HAL_MAX_DELAY); } LCD_CS_HIGH(); }

但注意,这里(uint8_t *)&color是平台依赖的,STM32小端模式下颜色低字节在前,而ILI9341期望高字节在前,所以直接发&color会导致红蓝互换。我一般定义一个联合体或者手动拆字节:

uint8_t color_buf[2] = { color >> 8, color & 0xFF };

这种细节最后都会体现为"为什么颜色不对"的玄学问题,后面排查章节再细说。

如果想要更高的填充效率,可以开一个局部缓冲区,把颜色字节拼好,再一次性HAL_SPI_Transmit发出。比如缓冲区256字节,一次发128个像素,减少HAL调用次数,速度能再涨一截。

4. 触摸屏控制:坐标读取、滤波与两点校准

4.1 XPT2046是怎么把按压变成数字的

这块屏配套的触摸方案通常是一颗XPT2046或ADS7846芯片,驱动的是四线电阻式触摸屏。原理说白了就是:触摸屏上下左右各有一条导电层,按下时上下层接触,通过分压电阻网络,X轴和Y轴各产生一个随触摸位置变化的模拟电压,触摸芯片通过内部ADC把电压转换成12位数字值,再通过SPI读出来。

所以触摸读数读到的不是像素坐标,而是原始ADC值。这个原始值跟屏幕分辨率没有直接关系,它取决于触摸屏的导电层均匀性和ADC参考电压。这也是为什么必须做校准——原始值到像素坐标的映射,每块屏都不一样。

XPT2046的SPI读取时序有一点特殊:它是一个典型的双向SPI器件,主机发命令字节,它边收边发,把上次转换结果在下一个字节的时钟里送出来。所以一次完整的触摸读取,通常要连续收发三个字节,第一个字节是命令,后两个字节里含12位有效数据。

我的读取函数大概长这样:

static uint16_t TP_ReadADC(uint8_t cmd) { uint8_t buf[3] = {0}; uint8_t recv[3] = {0}; buf[0] = cmd; HAL_GPIO_WritePin(TP_CS_GPIO_Port, TP_CS_Pin, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(&hspi2, buf, recv, 3, HAL_MAX_DELAY); HAL_GPIO_WritePin(TP_CS_GPIO_Port, TP_CS_Pin, GPIO_PIN_SET); return ((uint16_t)recv[1] << 8 | recv[2]) >> 3; }

我习惯把触摸芯片挂独立的SPI外设,因为触摸读取频繁且时序要求高,跟显示SPI混在一起容易出现互相干扰。如果硬件上已经共用了SPI,那需要加锁机制,麻烦很多。

4.2 触摸坐标读取与滤波

直接用TP_ReadADC读出来的值噪声比较大,手指按压时轻微抖动会导致ADC值波动几十甚至上百,直接映射成坐标就会在界面上乱跳。所以滤波是必须的。

我用的方案是简单的中值滤波加滑动平均:连续读5次,排序取中间值,再把中间值跟上次的值做一次加权平均。这个方法算下来延迟小、效果也不错:

uint16_t TP_ReadFiltered(uint8_t cmd) { uint16_t samples[5]; for (int i = 0; i < 5; i++) { samples[i] = TP_ReadADC(cmd); } // 排序取中值 for (int i = 0; i < 4; i++) { for (int j = i + 1; j < 5; j++) { if (samples[j] < samples[i]) { uint16_t tmp = samples[i]; samples[i] = samples[j]; samples[j] = tmp; } } } return samples[2]; }

排序5个元素的开销在72MHz主频下可以忽略。如果你需要更高采样率,减少到3次采样也够用,但边缘位置(点靠屏幕边界时)噪声会明显增大,我建议保持5次采样不降。

按压检测也不只是读取坐标这么简单。XPT2046有一个PENIRQ引脚,触摸按下时拉低。硬件上把它接到STM32的外部中断引脚,就能实现"按下才读坐标、松开不读"的机制。没有这个引脚也问题不大,可以用读取Y轴时的压力值判断,但延迟会高一点。

4.3 两点校准法的原理与代码

校准这件事,很多教程直接忽略,默认原始ADC值和屏幕坐标是线性关系,然后通过测量屏幕两个角的原始值做缩放。这个粗糙做法在屏幕尺寸小、精度要求低时能用,但一旦屏幕贴了保护膜或者触摸层老化,映射就会偏移。

我的做法是经典的两点校准加线性插值。校准原理很简单:屏幕上有两个已知的像素位置,比如(60, 60)和(180, 220),用户触摸这两个点,记录下对应的原始ADC值。然后根据"ADC值变化是线性的"这一假设,计算每个像素坐标对应X方向和Y方向上的比例系数:

scale_x = (raw_x2 - raw_x1) / (pixel_x2 - pixel_x1) scale_y = (raw_y2 - raw_y1) / (pixel_y2 - pixel_y1)

之后任何触摸原始值换算成像素坐标,就用:

pixel_x = (raw_x - raw_x1) / scale_x + pixel_x1 pixel_y = (raw_y - raw_y1) / scale_y + pixel_y1

注意,X轴的原始值通常跟屏幕Y方向有关(因为触摸屏X电极和Y电极的摆放位置跟LCD行列不一定是正对应的),所以校准参数矩阵实际可能包含交叉项。很多现成UI库用的是五点校准甚至九点校准来解决畸变问题。但对于简单的按钮交互,两点加方向判断就够了。

我实际项目中是这么处理的:上电后如果检测到Flash里没有有效的校准参数,就在屏幕上显示两个校准点,让用户依次点击,然后把参数存到Flash里的固定扇区。以后每次上电直接读取,不用重复校准。

4.4 做一个小Demo:触摸屏控制温度的软件

说一个真实做过的小项目,可以算是"触摸屏控制温度的软件"的落地版:用STM32F103C8T6 + ILI9341触摸屏 + DS18B20温度传感器 + 一个继电器控制加热棒,做成一个恒温控制面板。

界面分三个区域:顶部显示实时温度,中间显示目标温度,底部是加号和减号两个触摸按钮。触摸屏负责交互,用户按加号,目标温度增加0.5度,按减号减少0.5度;主循环里读DS18B20温度,跟目标温度比较,决定继电器通断。

核心代码其实很直接:

while (1) { TP_Scan(); // 扫描触摸,更新触摸坐标 if (touch_pressed) { if (pt_in_rect(touch_x, touch_y, btn_plus)) { target_temp += 0.5f; if (target_temp > 40.0f) target_temp = 40.0f; } if (pt_in_rect(touch_x, touch_y, btn_minus)) { target_temp -= 0.5f; if (target_temp < 10.0f) target_temp = 10.0f; } } current_temp = DS18B20_GetTemp(); if (current_temp < target_temp) { HAL_GPIO_WritePin(RELAY_PIN, GPIO_PIN_RESET); // 接通加热 } else { HAL_GPIO_WritePin(RELAY_PIN, GPIO_PIN_SET); // 断开加热 } UI_Update(current_temp, target_temp); HAL_Delay(50); }

这个项目里的关键点其实不在温度控制逻辑,而在UI状态的刷新策略。如果每50ms重绘一次整个界面,屏幕会闪烁得很厉害。我的做法是:只在数据变化时才刷新对应区域。比如温度从25.0变成25.2,只需要把那一个小区域重绘,而不是整屏刷新。

触摸坐标的"命中检测"也不难,判断触点是否落在按钮矩形内就行,但要留一点容错范围,因为用户手指点按的位置跟视觉中心会有偏移。我给按钮矩形扩展了5个像素的命中区域,实测误触率明显降低。

5. 性能优化:DMA刷新与显示策略

5.1 为什么刷屏会卡,瓶颈在哪

一块240x320的屏幕,RGB565格式一帧数据是240x320x2 = 153600字节。如果用8MHz SPI刷,理论最少耗时19.2毫秒,但这是纯数据搬运时间,还没算命令开销和循环开销。如果不小心用了阻塞方式一字节一字节发,实际刷新一帧可能要100毫秒以上,也就是10帧不到,肉眼可见地卡顿。

刷屏最忌讳的就是在for循环里反复调用HAL_SPI_Transmit。每调用一次HAL函数,都有参数检查、状态判断、循环,这些CPU周期积累起来非常可观。

5.2 用DMA把CPU从刷屏中解放出来

正确做法是SPI + DMA传输。CubeMX里把SPI的发送DMA请求打开,生成代码后,发送大块数据直接调用:

HAL_SPI_Transmit_DMA(&hspi1, buf, len);

DMA搬运数据不占CPU,发送期间CPU可以继续跑逻辑代码。但有个地方要特别注意:DMA传输完成后,需要检查传输完成标志,或者注册传输完成回调,不能立刻修改buf或者再次启动DMA,否则会数据错乱。

我的建议是维护一个简单的DMA完成状态标志:

volatile uint8_t spi_dma_busy = 0; void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi == &hspi1) { spi_dma_busy = 0; } }

发送前检查spi_dma_busy,如果还在忙就等待,否则直接启动新的DMA传输。

在刷整幅图片或者全屏填充时,DMA效果最为明显。CPU可以在DMA搬运数据的同时执行其他任务,比如读取触摸、更新温度、处理按键,整个系统的响应性会好很多。

5.3 局部刷新和缓冲区的取舍

即使有DMA,整屏刷新一帧20毫秒的代价还是不能随便付。界面交互场景里,真正高效的做法是局部刷新,哪块变了刷哪块。

我一般的UI刷新策略是:

  • 静态背景只画一次,不重复刷
  • 动态数据区域,比如温度数值、进度条,单独划定矩形区域,数据变化时只刷新这个区域
  • 触摸按钮,只在按下和释放时各刷一次,避免持续刷新

缓冲区方面,小内存单片机放不下全屏帧缓冲区(153KB),但开一个很小的行缓冲区或者矩形缓冲区是可行的。比如在STM32F103上开一个1KB的缓冲区,填充矩形时就先填满缓冲区,再DMA发出去,而不是逐像素循环。这样既发挥DMA优势,又不占用太多RAM。

如果你用的是F407或者F429这类带LTDC的芯片,可以考虑SDRAM + 全屏帧缓冲,但F103级别就不要想这件事了,局部刷新是最现实的方案。

6. 常见问题与排查实录

6.1 上电黑屏/白屏的排查顺序

这是最多人遇到的问题。我排查顺序一般是这样的:

第一,确认背光有没有亮。背光亮但屏幕黑,说明电源和背光正常,问题在初始化或SPI通信;背光不亮,先查供电和背光引脚。

第二,确认复位引脚是否拉高。有些模块的RESET引脚必须由MCU控制,上电拉低再拉高完成复位。如果模块是RC复位电路,需要把初始化里的软复位延时拉长。

第三,确认SPI时钟和引脚配置。用逻辑分析仪或者示波器看SCLK是否有波形,MOSI上是否有数据,CS/DC电平是否正常。没有示波器的话,可以在初始化代码里加一个GPIO翻转的调试点,看程序有没有跑进去。

第四,确认初始化序列是否完整。有些精简版初始化代码省略了延时,导致屏幕在睡眠模式里没出来就直接写数据,屏幕自然不亮。我习惯于在退出睡眠(0x11)后固定延时120ms以上,打开显示(0x29)后再延时20ms。

6.2 颜色不对、花屏怎么解决

颜色不对十有八九是RGB字节序问题。ILI9341默认接收的RGB565格式,高字节在前:RRRRRGGGGGGBBBBB。但是STM32在内存里是小端,16位数的高字节存在高地址,如果你直接把uint16_t数组通过DMA发送,发送顺序是低字节在前,红蓝就会互换。解决办法是发送前把颜色值转换一次,即color_byte0 = color >> 8; color_byte1 = color & 0xFF,或者反过来发。

花屏一般是SPI时钟太快、或者时序不对导致的。可以先降低SPI分频系数,看看花屏是否消失。如果降速正常,说明是信号完整性问题,优先检查杜邦线长度和供电。另外,花屏也可能是DMA传输未完成就切换了DC/CS引脚,破坏了一帧数据的完整性,需要检查DMA完成回调是否真的触发了。

6.3 触摸漂移、触点乱跳怎么办

触摸漂移最常见的原因是校准参数不对,或者压根没校准。先做一个原始值测试程序,把TP_ReadADC读到的X/Y原始值实时打印到串口,用手指点屏幕四个角和中心,观察数值是否呈单调变化、范围是否合理。

如果某个方向的原始值完全不变化,检查触摸芯片的对应通道配置,可能是命令字节写错了。XPT2046选择X通道和Y通道的命令不一样,而且最后两位控制PD(Power Down)模式,我一般用0x90读Y坐标、0xD0读X坐标这样的经典配置。

如果单点采样时坐标乱跳,多半是滤波没做好,把采样次数加上去就行。还有一种情况是触摸屏的VCC纹波大,特别是背光PWM调光频率设置不当的时候,ADC参考电压被干扰,导致读数漂移。解决办法是触摸芯片VCC加一个10uF电容,并且把背光PWM频率提高到1kHz以上。

6.4 一张速查表

现象可能原因排查办法
背光不亮供电不足或背光引脚配置错误测电压、检查BL引脚GPIO状态
背光亮但黑屏初始化未完成、SPI不通信逻辑分析仪看SCLK/MOSI波形
画面花屏SPI时钟过快、DMA时序错乱降速测试、检查DMA完成标志
颜色红蓝互换RGB字节序错误发送前交换高低字节
中文显示乱码字库取模方式错误确认取模为逐行式还是逐列式
触摸无响应触摸IC供电异常、SPI配置错误读原始值打印串口
触摸坐标往一边偏未校准或校准参数错误重新执行两点校准并保存校准参数
触摸偶尔乱跳滤波不足、电源纹波大增加采样次数、加强滤波、加电容

7. 写在最后:一点个人体会

驱动ILI9341这个活儿,说难不难,说简单也不简单。难的地方不在点亮那一瞬间的兴奋,而在后续的工程化细节:怎么把驱动写得简洁易维护,怎么把刷新效率提上去,怎么让触摸交互不飘、不卡、不误触。这些靠看芯片手册和抄代码都学不来,得一块板子一块板子地试,一个坑一个坑地踩。

我个人的习惯是,每做完一个项目就把驱动代码重新整理一遍,把硬件相关的部分和算法逻辑彻底分开。这套ILI9341的驱动库,我在F103C8、F103ZET、F407上分别跑过,只需要改SPI句柄和几个引脚宏,其余代码几乎原封不动。把底层接口抽象好了,后面换屏只是换驱动,换主控也只是改底层,整个项目的生命周期会舒服很多。

最后再分享一个小技巧:调试触摸屏界面时,不要直接在真实触摸事件上加逻辑,先在屏幕固定位置打几个调试坐标点,用串口把原始坐标、校准后坐标、按钮命中结果都打出来,逐个环节排查。这个习惯能让你在半小时内解决别人折腾一整天的触摸问题。

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

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

C语言系统学习指南:从环境搭建到项目实战,避开速成陷阱

“七天速成C语言大神”、“零基础到大神”、“少走99%的弯路”……在B站、知乎和各种学习平台上&#xff0c;这样的标题你一定不陌生。作为一个在编程领域摸爬滚打多年的开发者&#xff0c;我深知这类承诺背后&#xff0c;往往隐藏着巨大的认知陷阱。C语言&#xff0c;作为计算…

作者头像 李华
网站建设 2026/9/1 10:24:23

技术讨论总变“二极管”?用上下文对齐与模板化协作破局

你不是一个人。很多开发者都有过这种感受&#xff1a;明明是在做技术讨论&#xff0c;结果没说两句&#xff0c;对方就甩过来一句“这方案明显不行”“用 XX 就完了”“你那样做就是错的”。乍一听&#xff0c;好像全世界的同事都变成了只会输出 0 和 1 的二极管&#xff0c;非…

作者头像 李华
网站建设 2026/9/1 10:24:14

Agent记忆系统设计:Context、Session、State与Memory分层架构

Agent 类应用真正难的地方&#xff0c;不是怎么让大模型把话说对&#xff0c;而是怎么让它记住“上一次发生了什么”。在电商客服、企业助手、工单系统这类真实业务场景里&#xff0c;用户不会每次重复一遍自己的订单号、收货地址和售后诉求&#xff0c;更不会接受 AI 频繁反问…

作者头像 李华
网站建设 2026/9/1 10:21:57

Next AI Draw.io 使用指南:如何用自然语言快速生成架构图

Next AI Draw.io 使用指南&#xff1a;如何用自然语言快速生成架构图 【免费下载链接】next-ai-draw-io A next.js web application that integrates AI capabilities with draw.io diagrams. This app allows you to create, modify, and enhance diagrams through natural la…

作者头像 李华