news 2026/4/18 8:48:00

STM32H743: 实现XPT2046触摸屏与LTDC联动

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32H743: 实现XPT2046触摸屏与LTDC联动

目录

一、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 引脚功能说明
CSPG12片选(低电平有效)
DINPB15 (SPI2_MOSI)数据输入
DOUTPB14 (SPI2_MISO)数据输出
CLKPB13 (SPI2_SCK)SPI 时钟
INTPE3触摸中断(低电平触发)
VCC3.3V供电
GNDGND接地

注: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. 测试效果

  1. 硬件上电后,LCD 显示黑色背景;
  2. 用手指触摸 LCD 任意位置,触摸点会显示红色实心圆(半径 5);
  3. 滑动触摸时,会连续绘制红色圆点,实现 “手写涂鸦” 效果。

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 屏典型时序参数(通用款)
参数数值说明
HSYNC136 个 PCLK行同步脉冲宽度
HBP160 个 PCLK行后消隐
HACTIVE1024行有效像素数
HFP24 个 PCLK行前消隐
VSYNC6 行场同步脉冲宽度
VBP29 行场后消隐
VACTIVE768场有效行数
VFP3 行场前消隐
PCLK65MHz像素时钟(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. 测试效果

  1. 连接 1024x768 TFT-LCD 屏后,上电显示黑色背景;
  2. 运行触摸联动代码,触摸位置可精准显示红色圆点,无错位、花屏;
  3. 全屏填充颜色时,帧率稳定 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”,避免信号衰减。

总结

关键点回顾

  1. XPT2046 与 LTDC 联动
    • 核心是坐标校准(原始 AD 值→LCD 像素坐标)和中断触发扫描(避免 CPU 轮询);
    • 联动逻辑:触摸中断→扫描坐标→修改 LTDC 帧缓冲区→硬件自动刷新显示;
  2. 1024x768 高分辨率配置
    • 核心是匹配屏的时序参数和调整像素时钟(65MHz);
    • SDRAM 容量和带宽需满足高分辨率数据存储 / 传输需求;
  3. 通用避坑
    • 触摸屏需实际校准偏移和量程,避免坐标错位;
    • LTDC 时序参数必须与屏的规格书完全一致,否则花屏 / 错位。

以上代码可直接复用,若需实现 “触摸按键(点击特定区域触发事件)” 或 “1024x768 屏显示位图”,可基于此扩展(如增加坐标区域判断、位图数据读取函数)。

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

YOLOv10官方镜像训练500轮,收敛效果展示

YOLOv10官方镜像训练500轮&#xff0c;收敛效果展示 在目标检测模型的实际工程落地中&#xff0c;一个常被忽视却至关重要的问题浮出水面&#xff1a;训练过程是否稳定&#xff1f;收敛曲线是否健康&#xff1f;500轮训练后&#xff0c;模型性能是否真正达到平台期&#xff1f…

作者头像 李华
网站建设 2026/3/30 13:21:55

免费看直播APP下载,附m3u8直播源M3U直播源等下载

提供了一些可以免费看的软件 目前已经找到比较多的&#xff0c;部分是CSDN收费下载的&#xff0c;放在文章底部了&#xff0c;需要的大佬直接去拿吧。 有的是以前老的&#xff0c;就没下载&#xff0c;尽量下载一些新的&#xff0c;如果有不行的就换一个试试&#xff0c; 下载…

作者头像 李华
网站建设 2026/4/18 2:24:29

从0开始学AI建站:VibeThinker-1.5B带你飞速入门

从0开始学AI建站&#xff1a;VibeThinker-1.5B带你飞速入门 你是否试过在深夜赶一个静态页面&#xff0c;反复查MDN文档、复制粘贴CSS重置代码、纠结<section>和<article>该用哪个&#xff1f;又或者&#xff0c;刚学前端的你对着“写一个响应式导航栏”发呆&…

作者头像 李华
网站建设 2026/4/18 8:41:45

GLM-4v-9b实战:用AI一键解析图片中的文字和图表

GLM-4v-9b实战&#xff1a;用AI一键解析图片中的文字和图表 你是否遇到过这样的场景&#xff1a; 手里有一张会议现场的PPT截图&#xff0c;密密麻麻全是表格和小字&#xff0c;想快速提取关键数据却要手动抄写&#xff1f;收到客户发来的PDF扫描件&#xff0c;里面是带公式的…

作者头像 李华
网站建设 2026/4/18 0:18:50

移动应用消费创新高,订阅模式驱动收入增长

Appfigures&#xff1a;移动应用支出创下1558亿美元记录 尽管全球应用下载量达到疫情后的低点1069亿次&#xff0c;但应用内购和订阅模式推动了创纪录的收入增长。 Appfigures的年度报告指出&#xff0c;2025年通过某中心应用商店和某机构应用商店进行的全球移动应用和游戏下…

作者头像 李华