嵌入式小屏电容触摸驱动实战:ST7789与CST816D的SPI/I2C协同设计
1. 项目概述2英寸电容触摸LCD的定位与价值最近在捣鼓一个需要紧凑人机交互界面的小项目选型时一眼看中了2英寸电容触摸LCD这个组合。你可能觉得现在满大街都是大屏为啥还要折腾这么小的屏幕其实在嵌入式、便携设备、智能穿戴或者一些对空间和功耗极度敏感的工控场景里这种小尺寸高集成度的屏幕方案才是真正的“刚需”。它不像SPI TFT裸屏那样需要外挂电阻触摸电容触摸带来的直接、流畅的滑动和点击体验能让你的产品交互感提升一个档次。这次我用的核心是ST7789驱动的LCD和CST816D触摸芯片通过SPI总线与主控比如STM32通信。整个折腾下来从点亮屏幕到驱动触摸再到优化刷新和抗干扰踩了不少坑也总结了一套比较稳的实操流程。如果你也在为如何高效驱动一块小尺寸电容屏而头疼或者好奇SPI驱动LCD和触摸芯片的细节那这篇从硬件连接到软件调试的全程记录应该能给你省下不少时间。2. 核心组件深度解析为什么是ST7789和CST816D2.1 屏幕驱动ICST7789T3的能耐与局限ST7789是一颗非常经典的TFT LCD驱动芯片支持最高240x320的分辨率对于2英寸屏来说240x320即所谓的QVGA是一个甜点分辨率在精细度和驱动开销之间取得了很好的平衡。它内部集成了显存GRAM这意味着主控MCU只需要通过SPI接口将图像数据“喂”给ST7789后续的扫描和维持显示都由它自己完成大大减轻了MCU的负担特别适合那些没有专用LCD控制器或内存有限的MCU比如STM32F1系列。ST7789通常支持3线或4线SPI模式。3线模式只使用SDA数据线、SCL时钟线和CS片选通过DC数据/命令选择引脚的高低电平来区分发送的是命令还是数据。4线模式则把SDA拆分成SDO和SDI实现全双工但在驱动屏幕时我们通常只使用半双工通信所以3线模式更常见也更节省IO口。它的通信速率可以跑到几十MHz但对于2英寸屏实际上不需要那么高因为像素总量不大合理的速率设置如20-40MHz既能保证刷新率又能降低EMI干扰。需要注意的是ST7789的初始化序列比较长包含伽马校正、电源控制、内存访问控制等一大堆寄存器配置。不同厂家、不同分辨率的屏幕初始化序列可能有细微差别最好直接从卖家那里获取确切的初始化代码否则可能出现颜色不对、花屏、显示偏移等问题。2.2 触摸控制ICCST816D的交互逻辑CST816D是一颗专注于小尺寸电容屏的触摸芯片支持单点触摸有些型号支持两点通过I2C接口上报触摸事件。选择它而不是更常见的FT系列或其他SPI触摸芯片主要基于几点考虑首先是功耗CST816D在待机模式下功耗极低非常适合电池供电设备其次是集成度它内部集成了手势识别如上下左右滑动可以减轻MCU的运算负担最后是它的中断功能有触摸事件时才通过INT引脚唤醒MCU而不是让MCU不停地去轮询I2C总线这是实现低功耗交互的关键。它的工作原理是持续监测屏体的电容变化。当手指触摸时会改变局部电场芯片检测到这种变化通过算法计算出触摸点的坐标X, Y并通过I2C接口输出。这里有一个关键参数报点率。报点率太低触摸会感觉“卡顿”太高又会增加不必要的功耗和总线负载。CST816D通常可以配置在10Hz到100Hz之间对于2英寸屏和一般的手指操作30-60Hz是一个比较均衡的设置。2.3 SPI与I2C双总线架构的协同考量这个方案采用了SPI驱动显示、I2C驱动触摸的双总线架构。这几乎是这类分立芯片方案的标配。为什么不用统一的SPI因为功能侧重点不同。ST7789需要高速、单向主要是MCU到屏地传输大量像素数据SPI的推流能力更强。而CST816D只需要在触摸事件发生时低速、双向地传输几个字节的坐标和状态数据I2C的两线制SDA, SCL更节省IO且支持多设备挂载虽然这里只有一个。在实际布线时要特别注意两条总线的走线分离尤其是SPI的时钟线SCLK它是高速开关信号容易成为噪声源。如果可能尽量让SPI走线和I2C走线不要长距离平行或者在中间加地线隔离。同时给两个芯片的电源引脚加上足够的去耦电容通常用0.1uF和10uF并联并且尽可能靠近芯片的VCC和GND引脚这是保证稳定工作的基础。3. 硬件连接与电路设计要点3.1 引脚定义与连接对照表拿到一块2英寸电容屏模块背面通常会有一个FPC排线接口。我们需要将其引脚一一对应地连接到MCU。以下是一个典型的连接示意表格以STM32F103系列MCU为例屏幕模块引脚功能说明连接至STM32引脚备注VCC电源正极 (3.3V)3.3V绝对禁止接5VGND电源地GNDSCL / SCLKSPI时钟线PA5 (SPI1_SCK)SDA / MOSISPI主出从入数据线PA7 (SPI1_MOSI)发送数据/命令给屏幕RESET屏幕硬件复位PA0 (自定义GPIO)低电平有效用于彻底复位屏幕DC / RS数据/命令选择PA1 (自定义GPIO)高电平数据低电平命令CSSPI片选PA4 (SPI1_NSS)低电平选中屏幕SPISDO / MISOSPI主入从出数据线PA6 (SPI1_MISO)本例中ST7789未使用此功能可不接TP_INT触摸中断输出PA8 (自定义GPIO外部中断)触摸发生时CST816D拉低此引脚TP_SDA触摸I2C数据线PB7 (I2C1_SDA)需配置为上拉模式TP_SCL触摸I2C时钟线PB6 (I2C1_SCL)需配置为上拉模式注意不同厂家的模块引脚顺序可能不同务必以卖家提供的资料为准。RESET和DC引脚必须用普通GPIO控制不能用SPI的硬件控制信号替代。3.2 电源与信号完整性设计电源是第一个要保障的。ST7789和CST816D通常都是3.3V供电必须确保你的MCU系统也是3.3V电平。如果MCU是5V系统则需要电平转换电路或者寻找支持5V容忍IO的MCU并确保其IO口输出高电平为3.3V。直接接入5V会永久损坏芯片。去耦电容的布置是硬件调试中最容易忽视但至关重要的一环。理想情况下在每个芯片的VCC和GND引脚之间最近的位置应该并联一个0.1uF104的陶瓷电容和一个10uF的钽电容或电解电容。0.1uF负责滤除高频噪声比如SPI时钟产生的高频谐波10uF负责提供瞬时大电流比如屏幕全白刷新时电流会骤增。如果屏幕出现闪烁、花屏或者触摸乱跳首先检查电源纹波和去耦电容是否到位。对于信号线如果连接线较长超过10cm或者环境干扰较大可以考虑串联一个22Ω到100Ω的小电阻在SCLK、MOSI等高速线上这有助于抑制信号过冲和振铃改善信号质量。触摸芯片的I2C线SDA, SCL通常需要外部上拉电阻一般取值4.7kΩ到10kΩ如果MCU内部上拉足够强如STM32的GPIO可配置为上拉模式也可以省略外部电阻但为了可靠性我建议还是加上。4. 底层驱动软件实现详解4.1 ST7789 SPI显示驱动编写驱动ST7789本质上是按照它的指令集通过SPI总线发送一系列命令和数据来配置它然后向它的GRAM中写入像素数据。我们通常会用一组基本的底层函数来封装这些操作。首先是GPIO和SPI的初始化。以STM32 HAL库为例// SPI初始化 (以SPI1为例) void SPI1_Init(void) { hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; // 双线模式只发不收 hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; // 时钟极性CPOL0 hspi1.Init.CLKPhase SPI_PHASE_1EDGE; // 时钟相位CPHA0模式0 hspi1.Init.NSS SPI_NSS_SOFT; // 软件控制片选非常重要 hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_2; // 分频系数决定速率 hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; hspi1.Init.TIMode SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial 10; if (HAL_SPI_Init(hspi1) ! HAL_OK) { Error_Handler(); } }这里的关键是SPI_NSS_SOFT软件片选。硬件片选NSS在多个SPI设备时很有用但对于单个屏幕软件控制CS引脚更加灵活可靠。时钟模式CPOL和CPHA必须严格按照屏幕数据手册设置ST7789通常使用模式0。接着我们需要编写最基本的写命令和写数据函数// 写命令 void LCD_Write_Cmd(uint8_t cmd) { LCD_DC_GPIO_Port-BSRR (uint32_t)LCD_DC_Pin 16; // DC置低表示命令 LCD_CS_GPIO_Port-BSRR (uint32_t)LCD_CS_Pin 16; // CS置低选中设备 HAL_SPI_Transmit(hspi1, cmd, 1, HAL_MAX_DELAY); LCD_CS_GPIO_Port-BSRR LCD_CS_Pin; // CS置高释放 } // 写数据 void LCD_Write_Data(uint8_t data) { LCD_DC_GPIO_Port-BSRR LCD_DC_Pin; // DC置高表示数据 LCD_CS_GPIO_Port-BSRR (uint32_t)LCD_CS_Pin 16; HAL_SPI_Transmit(hspi1, data, 1, HAL_MAX_DELAY); LCD_CS_GPIO_Port-BSRR LCD_CS_Pin; }有了这两个函数就可以发送初始化序列了。初始化序列是一长串固定的命令和数据用于开启内部振荡器、设置驱动方向、伽马曲线、像素格式等等。这部分代码通常很长有上百行建议直接从卖家提供的示例代码中复制这是最稳妥的。初始化完成后最重要的函数是设置显示窗口和写入GRAM。由于屏幕是240x320但我们可能只需要更新其中一部分比如一个图标为了加速可以先设置一个矩形窗口然后连续向这个窗口内写入像素数据。// 设置显示窗口 (x_start, y_start) 到 (x_end, y_end) void LCD_SetWindow(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { LCD_Write_Cmd(0x2A); // 列地址设置命令 LCD_Write_Data(x1 8); LCD_Write_Data(x1 0xFF); LCD_Write_Data(x2 8); LCD_Write_Data(x2 0xFF); LCD_Write_Cmd(0x2B); // 行地址设置命令 LCD_Write_Data(y1 8); LCD_Write_Data(y1 0xFF); LCD_Write_Data(y2 8); LCD_Write_Data(y2 0xFF); LCD_Write_Cmd(0x2C); // 内存写命令接下来发送的都是像素数据 } // 填充颜色到指定窗口 void LCD_FillColor(uint16_t color, uint32_t size) { uint8_t hi color 8, lo color 0xFF; LCD_DC_GPIO_Port-BSRR LCD_DC_Pin; LCD_CS_GPIO_Port-BSRR (uint32_t)LCD_CS_Pin 16; for(uint32_t i0; isize; i) { // 连续发送效率较低后面会讲DMA优化 HAL_SPI_Transmit(hspi1, hi, 1, HAL_MAX_DELAY); HAL_SPI_Transmit(hspi1, lo, 1, HAL_MAX_DELAY); } LCD_CS_GPIO_Port-BSRR LCD_CS_Pin; }像素颜色格式通常是RGB565即16位色R(5位)、G(6位)、B(5位)。color参数就是一个16位的无符号整数。4.2 CST816D I2C触摸驱动与中断处理触摸驱动的核心是配置I2C和中断引脚并在中断服务函数中读取坐标。首先初始化I2C同样使用STM32 HAL库void I2C1_Init(void) { hi2c1.Instance I2C1; hi2c1.Init.ClockSpeed 400000; // 400kHz标准模式 hi2c1.Init.DutyCycle I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 0; hi2c1.Init.AddressingMode I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 0; hi2c1.Init.GeneralCallMode I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(hi2c1) ! HAL_OK) { Error_Handler(); } }CST816D的I2C设备地址通常是0x157位地址。触摸中断引脚TP_INT需要配置为外部中断下降沿触发因为触摸时通常是低电平有效。// 配置触摸中断引脚 void TP_EXTI_Init(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin TP_INT_Pin; GPIO_InitStruct.Mode GPIO_MODE_IT_FALLING; // 下降沿触发 GPIO_InitStruct.Pull GPIO_PULLUP; // 内部上拉确保空闲时为高电平 HAL_GPIO_Init(TP_INT_GPIO_Port, GPIO_InitStruct); HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); }在中断服务函数中我们读取触摸状态和坐标。CST816D有几个关键寄存器0x00: 手势ID如滑动方向0x01: 触摸点数通常为10x03: X坐标高8位0x04: X坐标低8位0x05: Y坐标高8位0x06: Y坐标低8位// 在EXTI中断服务函数中调用 void TP_IRQHandler(void) { if(__HAL_GPIO_EXTI_GET_IT(TP_INT_Pin) ! RESET) { __HAL_GPIO_EXTI_CLEAR_IT(TP_INT_Pin); uint8_t data[7]; // 从寄存器0x00开始连续读取7个字节 if(HAL_I2C_Mem_Read(hi2c1, CST816D_ADDR1, 0x00, I2C_MEMADD_SIZE_8BIT, data, 7, 10) HAL_OK) { if(data[1] 0) { // 有触摸点 uint16_t touchX ((data[3] 0x0F) 8) | data[4]; uint16_t touchY ((data[5] 0x0F) 8) | data[6]; // 将坐标存入全局变量供主循环处理 g_touchX touchX; g_touchY touchY; g_touchEvent 1; // 设置触摸事件标志 } } } }这里有一个关键点从I2C读取数据后需要根据屏幕的实际安装方向对坐标进行转换。因为触摸芯片的坐标系是固定的但屏幕可能被旋转了90°或180°显示。转换公式需要根据你调用LCD_SetRotation()函数设置的方向来调整。4.3 使用DMA优化SPI刷屏性能用HAL_SPI_Transmit一个字节一个字节地发数据来刷全屏240*32076800个像素每个像素2字节共153600字节速度会非常慢可能只有几帧每秒动画效果根本没法看。这时就必须请出DMA直接存储器访问。DMA可以让SPI控制器直接从内存中读取数据并发送而不需要CPU参与。配置步骤稍复杂开启DMA时钟并配置DMA流对于STM32F4/H7等或通道对于F1。将SPI的发送请求与DMA通道绑定。准备一个存放像素数据的缓冲区比如uint16_t frameBuffer[320][240]。调用HAL_SPI_Transmit_DMA启动传输。// 假设使用SPI1的TX DMA流3通道3 (STM32F4) uint16_t lcd_buffer[76800]; // 全屏缓冲区大小240*320 void SPI1_DMA_Init(void) { __HAL_RCC_DMA2_CLK_ENABLE(); hdma_spi1_tx.Instance DMA2_Stream3; hdma_spi1_tx.Init.Channel DMA_CHANNEL_3; hdma_spi1_tx.Init.Direction DMA_MEMORY_TO_PERIPH; hdma_spi1_tx.Init.PeriphInc DMA_PINC_DISABLE; hdma_spi1_tx.Init.MemInc DMA_MINC_ENABLE; hdma_spi1_tx.Init.PeriphDataAlignment DMA_PDATAALIGN_BYTE; hdma_spi1_tx.Init.MemDataAlignment DMA_MDATAALIGN_BYTE; // 注意是字节 hdma_spi1_tx.Init.Mode DMA_NORMAL; // 普通模式传输一次 hdma_spi1_tx.Init.Priority DMA_PRIORITY_HIGH; hdma_spi1_tx.Init.FIFOMode DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(hdma_spi1_tx) ! HAL_OK) { Error_Handler(); } __HAL_LINKDMA(hspi1, hdmatx, hdma_spi1_tx); } // 使用DMA刷屏 void LCD_Refresh_DMA(uint16_t *buf, uint32_t size) { LCD_SetWindow(0, 0, 239, 319); // 设置全屏窗口 LCD_DC_GPIO_Port-BSRR LCD_DC_Pin; // DC1写数据 LCD_CS_GPIO_Port-BSRR (uint32_t)LCD_CS_Pin 16; // CS0 // 注意buf中的数据必须是已经按RGB565排列好的字节流。 // 如果buf是uint16_t数组需要转换为uint8_t指针且size是字节数像素数*2 HAL_SPI_Transmit_DMA(hspi1, (uint8_t*)buf, size*2); // 传输完成后SPI的TX完成中断或DMA传输完成中断会被触发 }使用DMA后刷屏速度可以提升几十倍轻松达到30fps以上。但要注意内存对齐和缓冲区管理。另外在DMA传输期间CPU可以去处理其他任务比如解析触摸坐标实现并行。5. 高级优化与实战调试技巧5.1 显示性能优化从全屏刷新到局部更新即使用了DMA频繁刷新整个153600字节的帧缓冲区也是不小的负担。真正的优化在于“只更新变化的部分”。这就需要引入“脏矩形”机制。基本思路是在应用层当某个UI元素如按钮、图标状态改变时只标记它所在的屏幕矩形区域为“脏区”。在显示驱动层用一个任务或定时器检查所有脏矩形然后只向ST7789发送这些矩形区域内的像素数据。例如你有一个在(10,10)位置大小为50x30的按钮需要从灰色变成红色。应用层调用GUI_MarkDirtyRect(10, 10, 60, 40);。在VSync垂直同步或一个低优先级任务中驱动层收集所有脏矩形合并如果重叠然后对每个矩形LCD_SetWindow(dirtyRect.x, dirtyRect.y, dirtyRect.xdirtyRect.w-1, dirtyRect.ydirtyRect.h-1); // 然后只从帧缓冲区中提取这个矩形对应的数据通过DMA发送 uint32_t offset dirtyRect.y * SCREEN_WIDTH dirtyRect.x; LCD_Refresh_DMA(frameBuffer[offset], dirtyRect.w * dirtyRect.h);清空脏矩形列表。这样可以极大减少SPI总线上的数据量降低功耗并让CPU有更多时间处理业务逻辑。5.2 触摸响应与滤波算法从CST816D读出的原始坐标可能会有抖动和噪声直接使用会导致光标跳动。必须加入软件滤波。最简单有效的是“滑动平均滤波”。#define TOUCH_FILTER_DEPTH 5 uint16_t touchX_history[TOUCH_FILTER_DEPTH] {0}; uint16_t touchY_history[TOUCH_FILTER_DEPTH] {0}; uint8_t history_index 0; uint16_t Filter_TouchCoordinate(uint16_t newX, uint16_t newY) { touchX_history[history_index] newX; touchY_history[history_index] newY; history_index (history_index 1) % TOUCH_FILTER_DEPTH; uint32_t sumX 0, sumY 0; for(int i0; iTOUCH_FILTER_DEPTH; i) { sumX touchX_history[i]; sumY touchY_history[i]; } g_filteredX sumX / TOUCH_FILTER_DEPTH; g_filteredY sumY / TOUCH_FILTER_DEPTH; }更高级的可以加入“野值剔除”即判断新读到的坐标如果与前一个滤波后的坐标差值超过某个阈值比如20个像素则认为是噪声将其丢弃或用历史值替代。对于手势识别CST816D本身能上报简单手势通过0x00寄存器但有时不够用。我们可以自己实现在触摸按下时记录起始坐标和时间戳在触摸释放时计算位移和耗时从而判断是单击、长按、还是向某个方向的滑动。5.3 低功耗策略设计在电池供电的设备中屏幕和触摸是耗电大户。一个完整的低功耗策略包括屏幕背光控制通过PWM控制背光LED的亮度在无操作一段时间后自动调暗或关闭。这是省电的大头。屏幕睡眠ST7789支持睡眠命令0x10进入睡眠模式后功耗可以降到微安级。在设备待机时可以依次执行关闭背光 - 让ST7789进入睡眠 - 关闭其电源如果硬件支持独立电源控制。触摸芯片睡眠CST816D也有低功耗模式。可以通过I2C发送命令让其进入休眠此时它只会消耗极小的电流但依然能检测到触摸并通过INT引脚唤醒MCU。MCU自身也可以进入Stop或Sleep模式等待触摸中断唤醒整个系统。动态刷新率在显示静态画面时可以降低SPI的时钟频率或者延长全屏刷新的间隔时间。实现这些需要硬件上支持背光独立供电、屏幕电源控制以及软件上良好的状态机管理。6. 常见问题排查与解决方案实录在实际调试中你几乎一定会遇到下面这些问题。我把它们和排查思路整理成了表格方便快速对照。现象可能原因排查步骤与解决方案屏幕白屏或完全不亮1. 电源接错如接了5V2. 背光未开启3. 复位时序不对4. SPI根本不通1.立即断电用万用表测量屏幕VCC引脚电压是否为3.3V。2. 检查背光LED引脚可能叫BLK或LED是否接在了可控的GPIO或直接接3.3V尝试给高电平。3. 用逻辑分析仪或示波器抓取RESET引脚波形确保有一个10ms的低电平脉冲然后拉高。4. 用逻辑分析仪抓SPI的CS、DC、SCLK、MOSI信号看初始化序列是否正常发出。屏幕花屏、错位、颜色异常1. 初始化序列不正确2. 像素格式设置错误3. 内存访问控制MADCTL寄存器设置错误导致扫描方向不对4. SPI时钟极性/相位不对1. 这是最高发问题。务必使用卖家提供的、针对你这款屏幕的确切初始化代码不同批次的屏都可能不同。2. 检查发送的像素数据格式是否为RGB565通常命令0x3A后面跟0x55或0x65。3. 调整MADCTL寄存器的值命令0x36尝试0x00, 0xC0, 0xA0等不同值结合LCD_SetRotation函数找到正确的显示方向。4. 确认SPI的CPOL和CPHA设置与屏幕要求一致。触摸无反应1. I2C通信失败2. 触摸中断引脚配置错误3. 触摸芯片未正确初始化4. 坐标转换错误1. 用逻辑分析仪抓I2C总线看是否能正确读到设备地址0x15的应答。检查上拉电阻。2. 测量TP_INT引脚触摸时是否产生下降沿。检查GPIO中断配置。3. CST816D有时也需要简单的初始化如复位查阅其数据手册。4. 在中断中读出原始坐标后打印出来看是否在合理范围如0~2400~320然后根据屏幕旋转方向应用转换公式。触摸坐标跳变、不准确1. 电源噪声2. 没有软件滤波3. 屏体被外力挤压或贴合不良1. 用示波器测量触摸芯片的VCC看是否有毛刺。加强电源去耦。2. 实现如前文所述的滑动平均滤波和野值剔除算法。3. 检查屏幕的FPC排线是否连接牢固触摸屏与LCD之间是否有气泡或异物。使用DMA刷屏时系统卡死或数据错乱1. DMA缓冲区溢出或内存访问冲突2. SPI和DMA时钟未使能3. 数据传输未完成时修改了缓冲区数据1. 确保DMA缓冲区大小正确且位于有效的内存区域如CCM RAM或普通SRAM。2. 检查__HAL_RCC_SPI1_CLK_ENABLE()和__HAL_RCC_DMA2_CLK_ENABLE()是否被调用。3. 在DMA传输完成中断回调函数中设置一个标志位主循环检测这个标志位为“就绪”后再准备下一帧数据或修改已发送的缓冲区。整体功耗过高1. 背光常亮且亮度高2. 屏幕未进入睡眠3. MCU未进入低功耗模式4. SPI时钟速率过高1. 引入背光PWM调光并在空闲时降低亮度或关闭。2. 在长时间无显示更新时发送ST7789的睡眠命令0x10。3. 在系统空闲时调用__WFI()或HAL_PWR_EnterSLEEPMode()让MCU休眠由触摸中断唤醒。4. 在低速刷新静态画面时动态降低SPI的波特率分频系数。调试这类问题逻辑分析仪是比示波器更趁手的工具。它能同时捕获SPI、I2C的多条数据线并以时序和协议解析的方式直观显示对于检查初始化序列、触摸数据包是否正确至关重要。最后分享一个我踩过的坑有一次调试屏幕显示正常但触摸完全没反应。查了半天电路和代码都没问题。最后发现是屏幕的FPC排线座上有一个透明的塑料保护盖没有撕掉它绝缘了触摸屏的感应层。所以硬件检查一定要从最物理、最傻的地方开始。

相关新闻