目录
一、XPT2046 触摸屏与 LTDC 联动实现
1. XPT2046 核心原理(与 LTDC 联动的基础)
2. 前置准备
(1)硬件连接(STM32H743 + XPT2046 + 800x480 TFT-LCD)
(2)软件环境
3. STM32CubeMX 配置(新增部分)
(1)SPI2 配置
(2)GPIO 配置(XPT2046_INT/CS)
(3)EXTI 中断配置
4. 核心代码实现
(1)宏定义与全局变量(touch.h)
(2)XPT2046 驱动实现(touch.c)
(3)LTDC 联动显示(main.c 中补充)
5. 测试效果
6. 避坑点
二、1024x768 高分辨率 LTDC 配置
1. 核心调整点
(1)1024x768 屏典型时序参数(通用款)
(2)像素时钟计算
2. STM32CubeMX 配置修改
(1)LTDC 时序参数修改
(2)Layer1 配置修改
3. 核心代码适配
(1)宏定义修改(lcd.h)
(2)LTDC 初始化代码(CubeMX 自动更新)
4. 测试效果
5. 避坑点
总结
关键点回顾
STM32H74X 中XPT2046 触摸屏与 LTDC 联动的完整实现代码(触摸坐标映射到 LCD 显示坐标),以及1024x768 高分辨率下 LTDC 的配置代码—— 核心是解决 “触摸坐标校准” 和 “高分辨率时序匹配” 两大问题,以下是可直接落地的完整实现,延续 HAL 库 + CubeMX 的风格,兼顾新手友好性。
一、XPT2046 触摸屏与 LTDC 联动实现
1. XPT2046 核心原理(与 LTDC 联动的基础)
XPT2046 是电阻式触摸屏的 AD 转换芯片,通过SPI 通信与 MCU 交互,核心是将触摸压力转换为 X/Y 轴的模拟电压,经 AD 转换得到数字坐标;与 LTDC 联动的关键是:
- 触摸坐标校准(消除屏的物理偏移,将 XPT2046 的原始 AD 值映射为 LCD 的像素坐标);
- 触摸事件触发(通过 INT 引脚中断检测触摸,避免 CPU 轮询);
- 联动显示(根据校准后的坐标,修改 LTDC 帧缓冲区数据,实现 “触摸画点 / 画线”)。
2. 前置准备
(1)硬件连接(STM32H743 + XPT2046 + 800x480 TFT-LCD)
| XPT2046 引脚 | STM32H743 引脚 | 功能说明 |
|---|---|---|
| CS | PG12 | 片选(低电平有效) |
| DIN | PB15 (SPI2_MOSI) | 数据输入 |
| DOUT | PB14 (SPI2_MISO) | 数据输出 |
| CLK | PB13 (SPI2_SCK) | SPI 时钟 |
| INT | PE3 | 触摸中断(低电平触发) |
| VCC | 3.3V | 供电 |
| GND | GND | 接地 |
注:LTDC 引脚保持之前 800x480 的配置不变。
(2)软件环境
- 保留之前 LTDC+SDRAM+DMA2D 的 CubeMX 配置;
- 新增 SPI2 和 GPIO 中断配置。
3. STM32CubeMX 配置(新增部分)
(1)SPI2 配置
- Mode:Master(主机模式);
- Hardware NSS Signal:Disabled(软件片选);
- SPI Clock Prescaler:16(时钟 = 400MHz/2/16=12.5MHz,XPT2046 最大支持 1MHz?错,XPT2046 实际支持≤25MHz,12.5MHz 稳定);
- Clock Polarity (CPOL):Low;
- Clock Phase (CPHA):1 Edge;
- Data Size:8 Bits;
- CRC Calculation:Disabled。
(2)GPIO 配置(XPT2046_INT/CS)
- PG12(XPT2046_CS):Output Push Pull,上拉,高速,默认高电平;
- PE3(XPT2046_INT):Input Pull Up,启用 EXTI 中断(下降沿触发)。
(3)EXTI 中断配置
- 启用 PE3 的 EXTI3 中断,优先级高于普通任务(如抢占优先级 2,子优先级 0)。
4. 核心代码实现
(1)宏定义与全局变量(touch.h)
#ifndef __TOUCH_H #define __TOUCH_H #include "stm32h7xx_hal.h" // 触摸屏分辨率(对应LCD 800x480) #define TOUCH_LCD_WIDTH 800 #define TOUCH_LCD_HEIGHT 480 // XPT2046指令定义 #define XPT2046_CMD_X 0x90 // 读X轴坐标 #define XPT2046_CMD_Y 0xD0 // 读Y轴坐标 #define XPT2046_CMD_Z1 0xB0 // 读Z1轴(压力) #define XPT2046_CMD_Z2 0xC0 // 读Z2轴(压力) // 校准参数(需实际校准后修改,示例值) #define X_OFFSET 200 // X轴原始值偏移 #define Y_OFFSET 180 // Y轴原始值偏移 #define X_SCALE 3700 // X轴原始值量程(最大值-最小值) #define Y_SCALE 3800 // Y轴原始值量程 // 触摸状态结构体 typedef struct { uint8_t is_pressed; // 是否触摸(0=未触摸,1=触摸) uint16_t x; // 校准后的LCD X坐标 uint16_t y; // 校准后的LCD Y坐标 } Touch_StateTypeDef; // 全局触摸状态 extern Touch_StateTypeDef Touch_State; // 函数声明 void XPT2046_Init(void); uint16_t XPT2046_ReadAD(uint8_t cmd); void Touch_Calibrate(void); void Touch_Scan(void); void Touch_IRQHandler(void); #endif(2)XPT2046 驱动实现(touch.c)
#include "touch.h" #include "spi.h" #include "gpio.h" // 全局触摸状态 Touch_StateTypeDef Touch_State = {0, 0, 0}; // SPI2句柄(CubeMX自动生成) extern SPI_HandleTypeDef hspi2; /** * @brief XPT2046片选控制 * @param state: 0=选中,1=取消选中 */ static void XPT2046_CS(uint8_t state) { HAL_GPIO_WritePin(GPIOG, GPIO_PIN_12, state ? GPIO_PIN_SET : GPIO_PIN_RESET); } /** * @brief XPT2046初始化 */ void XPT2046_Init(void) { XPT2046_CS(1); // 初始取消片选 Touch_State.is_pressed = 0; } /** * @brief 读取XPT2046 AD值 * @param cmd: 读取指令(X/Y/Z) * @retval AD值(0~4095) */ uint16_t XPT2046_ReadAD(uint8_t cmd) { uint8_t tx_data[2] = {0}; uint8_t rx_data[2] = {0}; uint16_t ad_value = 0; XPT2046_CS(0); // 选中芯片 // 发送读取指令(高位在前) tx_data[0] = cmd; HAL_SPI_TransmitReceive(&hspi2, tx_data, rx_data, 2, 100); // 解析AD值(12位有效,去掉前4位和后4位) ad_value = ((rx_data[0] & 0x0F) << 8) | rx_data[1]; ad_value >>= 4; XPT2046_CS(1); // 取消选中 return ad_value; } /** * @brief 触摸坐标校准(核心:映射到LCD像素坐标) * @param x_ad: X轴原始AD值 * @param y_ad: Y轴原始AD值 * @retval 0=成功,1=越界 */ static uint8_t Touch_Map(uint16_t x_ad, uint16_t y_ad) { // 过滤无效值 if(x_ad < 100 || y_ad < 100 || x_ad > 4000 || y_ad > 4000) { Touch_State.is_pressed = 0; return 1; } // 校准公式:LCD坐标 = (原始AD值 - 偏移) * LCD分辨率 / 量程 Touch_State.x = (x_ad - X_OFFSET) * TOUCH_LCD_WIDTH / X_SCALE; Touch_State.y = (y_ad - Y_OFFSET) * TOUCH_LCD_HEIGHT / Y_SCALE; // 反转Y轴(多数屏的触摸Y轴与LCD Y轴相反) Touch_State.y = TOUCH_LCD_HEIGHT - Touch_State.y; // 越界保护 if(Touch_State.x >= TOUCH_LCD_WIDTH) Touch_State.x = TOUCH_LCD_WIDTH - 1; if(Touch_State.y >= TOUCH_LCD_HEIGHT) Touch_State.y = TOUCH_LCD_HEIGHT - 1; Touch_State.is_pressed = 1; return 0; } /** * @brief 触摸扫描(建议中断触发后调用) */ void Touch_Scan(void) { uint16_t x_ad, y_ad; // 读取X/Y轴AD值(多次读取取平均,减少抖动) x_ad = 0; y_ad = 0; for(uint8_t i=0; i<3; i++) { x_ad += XPT2046_ReadAD(XPT2046_CMD_X); y_ad += XPT2046_ReadAD(XPT2046_CMD_Y); } x_ad /= 3; y_ad /= 3; // 坐标映射 Touch_Map(x_ad, y_ad); } /** * @brief 触摸中断处理函数(PE3下降沿触发) */ void Touch_IRQHandler(void) { if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3) == GPIO_PIN_RESET) { // 触摸按下,扫描坐标 Touch_Scan(); } else { // 触摸松开 Touch_State.is_pressed = 0; } // 清除中断标志 HAL_GPIO_EXTI_ClearITPendingBit(GPIO_PIN_3); } // 中断回调函数(CubeMX自动生成的EXTI3中断函数中调用) void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == GPIO_PIN_3) { Touch_IRQHandler(); } }(3)LTDC 联动显示(main.c 中补充)
#include "touch.h" #include "dma2d.h" #include "ltdc.h" // 新增:触摸画点函数(联动LTDC帧缓冲区) void LCD_DrawTouchPoint(uint16_t x, uint16_t y, uint16_t color, uint8_t radius) { if(x >= TOUCH_LCD_WIDTH || y >= TOUCH_LCD_HEIGHT) return; // 绘制实心圆(触摸点放大,便于观察) for(int i=-radius; i<=radius; i++) { for(int j=-radius; j<=radius; j++) { if((i*i + j*j) <= radius*radius) { LCD_DrawPoint(x+i, y+j, color); } } } } // 主函数修改 int main(void) { // 初始化流程(保留之前的LTDC/SDRAM/DMA2D) HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_FMC_Init(); MX_LTDC_Init(); MX_DMA2D_Init(); MX_SPI2_Init(); // 新增SPI2初始化 // 初始化XPT2046 XPT2046_Init(); // 启动LTDC HAL_LTDC_Enable(&hltdc); // 填充黑色背景 LCD_FillScreen_DMA2D(COLOR_BLACK); while (1) { // 检测触摸状态,联动画点 if(Touch_State.is_pressed) { // 触摸位置绘制红色圆点(半径5) LCD_DrawTouchPoint(Touch_State.x, Touch_State.y, COLOR_RED, 5); // 清除触摸状态(避免重复画点) Touch_State.is_pressed = 0; } HAL_Delay(10); } }5. 测试效果
- 硬件上电后,LCD 显示黑色背景;
- 用手指触摸 LCD 任意位置,触摸点会显示红色实心圆(半径 5);
- 滑动触摸时,会连续绘制红色圆点,实现 “手写涂鸦” 效果。
6. 避坑点
- 坐标校准:示例中的
X_OFFSET/Y_OFFSET/X_SCALE/Y_SCALE是默认值,需实际触摸 LCD 四个角,读取原始 AD 值后修正(如触摸左上角,记录 X/Y AD 值作为偏移); - SPI 速率:XPT2046 的 SPI 时钟建议≤15MHz,过高会导致 AD 值读取错误;
- 中断消抖:可在
Touch_IRQHandler中增加 10ms 延时消抖,避免误触发; - Y 轴反转:多数触摸屏的 Y 轴与 LCD Y 轴方向相反,需根据实际屏调整
Touch_Map中的反转逻辑。
二、1024x768 高分辨率 LTDC 配置
1. 核心调整点
1024x768 屏的驱动核心是匹配时序参数和调整像素时钟,同时确认 SDRAM 容量(1024x768 RGB565 需 1024×768×2=1,572,864 字节≈1.5MB,之前的 8MB SDRAM 完全满足)。
(1)1024x768 屏典型时序参数(通用款)
| 参数 | 数值 | 说明 |
|---|---|---|
| HSYNC | 136 个 PCLK | 行同步脉冲宽度 |
| HBP | 160 个 PCLK | 行后消隐 |
| HACTIVE | 1024 | 行有效像素数 |
| HFP | 24 个 PCLK | 行前消隐 |
| VSYNC | 6 行 | 场同步脉冲宽度 |
| VBP | 29 行 | 场后消隐 |
| VACTIVE | 768 | 场有效行数 |
| VFP | 3 行 | 场前消隐 |
| PCLK | 65MHz | 像素时钟(1024x768@60Hz) |
(2)像素时钟计算
帧率代入数值:(136+160+1024+24)×(6+29+768+3)×60≈65MHz
2. STM32CubeMX 配置修改
(1)LTDC 时序参数修改
- Horizontal Sync:135(136-1);
- Vertical Sync:5(6-1);
- Accumulated HBP:295(136+160-1);
- Accumulated VBP:34(6+29-1);
- Accumulated Active H:1319(295+1024-1);
- Accumulated Active V:802(34+768-1);
- Total Width:1343(1319+24);
- Total Height:805(802+3);
- PCLK:配置为 65MHz(通过 LTDC 时钟分频实现,H743 的 PLCK2=100MHz,分频后 65MHz)。
(2)Layer1 配置修改
- Window X0/X1:0/1024;
- Window Y0/Y1:0/768;
- Image Width/Height:1024/768;
- 帧缓冲区地址:仍为 0xD0000000(SDRAM 起始地址)。
3. 核心代码适配
(1)宏定义修改(lcd.h)
// 替换原800x480的宏定义 #define LCD_WIDTH 1024 #define LCD_HEIGHT 768 // 触摸分辨率同步修改 #define TOUCH_LCD_WIDTH 1024 #define TOUCH_LCD_HEIGHT 768(2)LTDC 初始化代码(CubeMX 自动更新)
void MX_LTDC_Init(void) { hltdc.Instance = LTDC; hltdc.Init.HSPolarity = LTDC_HSPOLARITY_LOW; hltdc.Init.VSPolarity = LTDC_VSPOLARITY_LOW; hltdc.Init.DEPolarity = LTDC_DEPOLARITY_HIGH; hltdc.Init.PCPolarity = LTDC_PCPOLARITY_RISING; // 1024x768时序参数 hltdc.Init.HorizontalSync = 135; hltdc.Init.VerticalSync = 5; hltdc.Init.AccumulatedHBP = 295; hltdc.Init.AccumulatedVBP = 34; hltdc.Init.AccumulatedActiveH = 1319; hltdc.Init.AccumulatedActiveV = 802; hltdc.Init.TotalWidth = 1343; hltdc.Init.TotalHeigh = 805; hltdc.Init.Backcolor.Blue = 0; hltdc.Init.Backcolor.Green = 0; hltdc.Init.Backcolor.Red = 0; if (HAL_LTDC_Init(&hltdc) != HAL_OK) { Error_Handler(); } // Layer1配置(1024x768) LTDC_LayerCfgTypeDef pLayerCfg = {0}; pLayerCfg.WindowX0 = 0; pLayerCfg.WindowX1 = 1024; pLayerCfg.WindowY0 = 0; pLayerCfg.WindowY1 = 768; pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565; pLayerCfg.Alpha = 255; pLayerCfg.Alpha0 = 0; pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA; pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA; pLayerCfg.FBStartAdress = 0xD0000000; pLayerCfg.ImageWidth = 1024; pLayerCfg.ImageHeight = 768; pLayerCfg.Backcolor.Blue = 0; pLayerCfg.Backcolor.Green = 0; pLayerCfg.Backcolor.Red = 0; if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK) { Error_Handler(); } }4. 测试效果
- 连接 1024x768 TFT-LCD 屏后,上电显示黑色背景;
- 运行触摸联动代码,触摸位置可精准显示红色圆点,无错位、花屏;
- 全屏填充颜色时,帧率稳定 60Hz,无明显闪烁。
5. 避坑点
- 像素时钟上限:STM32H743 的 LTDC PCLK 建议≤80MHz,65MHz 是 1024x768@60Hz 的最优值,过高会导致 EMI 干扰;
- SDRAM 带宽:1024x768@60Hz RGB565 需带宽 = 1024×768×2×60≈94MB/s,H743 的 SDRAM 带宽(100MHz×16 位 = 200MB/s)完全满足;
- 引脚驱动能力:1024x768 屏的 RGB 数据量更大,建议将 LTDC 引脚配置为 “GPIO_SPEED_FREQ_VERY_HIGH”,避免信号衰减。
总结
关键点回顾
- XPT2046 与 LTDC 联动:
- 核心是坐标校准(原始 AD 值→LCD 像素坐标)和中断触发扫描(避免 CPU 轮询);
- 联动逻辑:触摸中断→扫描坐标→修改 LTDC 帧缓冲区→硬件自动刷新显示;
- 1024x768 高分辨率配置:
- 核心是匹配屏的时序参数和调整像素时钟(65MHz);
- SDRAM 容量和带宽需满足高分辨率数据存储 / 传输需求;
- 通用避坑:
- 触摸屏需实际校准偏移和量程,避免坐标错位;
- LTDC 时序参数必须与屏的规格书完全一致,否则花屏 / 错位。
以上代码可直接复用,若需实现 “触摸按键(点击特定区域触发事件)” 或 “1024x768 屏显示位图”,可基于此扩展(如增加坐标区域判断、位图数据读取函数)。