#include "includes.h" // 触摸初始化(含引脚配置) void XPT2046_Init(void) { gpio_init_type gpio_init_struct; /* 必须先用默认值填充结构体,否则 out_type/drive_strength 等字段是随机值 */ gpio_default_para_init(&gpio_init_struct); /* 使能用到的 GPIO 时钟 */ crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE); crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); // 输出引脚:CLK、DIN、CS(推挽输出) gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; gpio_init_struct.gpio_pull = GPIO_PULL_NONE; gpio_init_struct.gpio_pins = TP_CLK_PIN | TP_DIN_PIN | TP_CS_PIN; gpio_init(GPIOC, &gpio_init_struct); // 输入引脚:OUT(上拉)、IRQ(上拉) gpio_init_struct.gpio_mode = GPIO_MODE_INPUT; gpio_init_struct.gpio_pull = GPIO_PULL_UP; gpio_init_struct.gpio_pins = TP_OUT_PIN; gpio_init(TP_OUT_PORT, &gpio_init_struct); gpio_init_struct.gpio_pins = TP_IRQ_PIN; gpio_init_struct.gpio_pull = GPIO_PULL_UP; gpio_init(TP_IRQ_PORT, &gpio_init_struct); // 初始状态 TP_CS_Set(); TP_CLK_Set(); } // 配置IRQ中断(可选,用于触摸触发) void XPT2046_IRQ_Init(void) { // GPIO_InitTypeDef GPIO_InitStruct = {0}; // EXTI_HandleTypeDef EXTI_HandleStruct = {0}; // // 复用IRQ引脚为外部中断 // GPIO_InitStruct.Pin = TP_IRQ_PIN; // GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿触发(触摸时IRQ变低) // GPIO_InitStruct.Pull = GPIO_PULLUP; // HAL_GPIO_Init(TP_IRQ_PORT, &GPIO_InitStruct); // // 配置EXTI中断线(PC12对应EXTI12) // EXTI_HandleStruct.Line = EXTI_LINE_12; // EXTI_HandleStruct.Mode = EXTI_MODE_INTERRUPT; // EXTI_HandleStruct.Pull = EXTI_PULLUP; // HAL_EXTI_Init(&EXTI_HandleStruct); // // 使能中断并设置优先级 // HAL_NVIC_SetPriority(EXTI15_10_IRQn, 2, 0); // EXTI12属于EXTI15_10中断线 // HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); } //SPI写数据 //向XPT2046写入1字节命令(MCU→DIN输出,XPT2046→DIN输入) //num:要写入的数据 void TP_Write_Byte(uint8_t data) { uint8_t i; TP_CLK_Clr(); // 时钟拉低,准备发送数据 Delay_us(1); for (i = 0; i < 8; i++) { // MCU通过DIN输出数据位(高位先传),XPT2046通过自身DIN输入接收 if (data & 0x80) { TP_DIN_Set(); // DIN输出高 } else { TP_DIN_Clr(); // DIN输出低 } data <<= 1; // 左移,准备下一位 // 时钟上升沿:XPT2046采样DIN引脚的输入数据(匹配XPT2046时序) TP_CLK_Set(); Delay_us(1); TP_CLK_Clr(); Delay_us(1); } } //SPI读数据 //从触摸屏IC读取adc值 //CMD:指令 //返回值:读到的数据 uint16_t TP_Read_AD(uint8_t cmd) { uint8_t i; uint16_t adc_val = 0; TP_CS_Clr(); // 片选拉低,选中XPT2046 Delay_us(2); // 片选稳定时间 TP_Write_Byte(cmd); // MCU通过DIN发送命令(XPT2046的DIN输入接收) // 等待XPT2046完成AD转换(约6us,转换期间OUT无有效数据) Delay_us(6); // 读取16位数据(XPT2046通过自身OUT输出数据,MCU通过OUT输入读取) for (i = 0; i < 16; i++) { adc_val <<= 1; // 左移,准备接收下一位 // 时钟上升沿:XPT2046更新OUT引脚输出;下降沿:MCU读取OUT输入 TP_CLK_Set(); Delay_us(1); TP_CLK_Clr(); Delay_us(1); // MCU读取OUT引脚的输入数据(高12位有效,低4位丢弃) if (TP_OUT_Read()) { adc_val |= 0x01; } } TP_CS_Set(); // 释放片选 return adc_val >> 4; // 取高12位有效数据 } // 简单去抖动读取(连续n次相同状态视为有效) uint8_t TP_CheckPressed(uint8_t n) { uint8_t count = 0; for (uint8_t i = 0; i < n; i++) { if (TP_IRQ_Read() == 0) { // IRQ低电平表示按下 count++; } Delay_us(100); } return (count >= n/2) ? 1 : 0; } // 读取原始坐标(未校准) TP_Point TP_ReadRaw(void) { TP_Point p = {0, 0, 0}; if (!TP_CheckPressed(3)) { // 检查触摸状态 return p; } // 多次读取取平均值,减少噪声 uint32_t x_sum = 0, y_sum = 0; for (uint8_t i = 0; i < 5; i++) { x_sum += TP_Read_AD(XPT2046_READ_X); y_sum += TP_Read_AD(XPT2046_READ_Y); } p.x = x_sum / 5; p.y = y_sum / 5; p.pressed = 1; return p; } // 校准参数(需实际触摸四个角后修改,示例为240x320屏) #define CALIB_X_MIN 200 // 最小X原始值 #define CALIB_X_MAX 3800 // 最大X原始值 #define CALIB_Y_MIN 200 // 最小Y原始值 #define CALIB_Y_MAX 3800 // 最大Y原始值 #define LCD_WIDTH 240 // LCD宽度 #define LCD_HEIGHT 320 // LCD高度 // 校准后获取LCD坐标 TP_Point TP_Read(void) { // char Info[40]; TP_Point raw = TP_ReadRaw(); // sprintf (Info, "(x=%03d y=%03d) ", raw.x, raw.y); // LCD_ShowString(2, 176+16, (const uint8_t*)Info, RED, WHITE, 16, 0); TP_Point calib = {0, 0, raw.pressed}; if (!raw.pressed) { return calib; } // 线性映射:将原始ADC值转换为LCD像素坐标 calib.x = (raw.x - CALIB_X_MIN) * LCD_WIDTH / (CALIB_X_MAX - CALIB_X_MIN); calib.y = (raw.y - CALIB_Y_MIN) * LCD_HEIGHT / (CALIB_Y_MAX - CALIB_Y_MIN); // 边界限制 if (calib.x > LCD_WIDTH - 1) calib.x = LCD_WIDTH - 1; if (calib.x == 0) calib.x = 0; if (calib.y > LCD_HEIGHT - 1) calib.y = LCD_HEIGHT - 1; if (calib.y == 0) calib.y = 0; return calib; } // EXTI15_10中断服务函数(处理TP_IRQ触发) //void EXTI15_10_IRQHandler(void) //{ // HAL_EXTI_IRQHandler(&EXTI_HandleStruct); // 调用HAL库中断处理 //} // 中断回调函数(触摸触发时执行) //void HAL_EXTI_Callback(EXTI_HandleTypeDef *hexti) //{ // if (hexti->Line == EXTI_LINE_12) // { // TP_Point p = TP_Read(); // 读取触摸坐标 // if (p.pressed) // { // // 处理触摸事件(如绘图、按键响应等) // // LCD_DrawPixel(p.x, p.y, 0xF800); // 示例:画红点 // } // } //} ////////////////////////////////////////////////////////////////////////////////// //与LCD部分有关的函数 //画一个触摸点 //用来校准用的 //x,y:坐标 //color:颜色 void TP_Drow_Touch_Point(uint16_t x, uint16_t y, uint16_t color) { LCD_DrawLine(x - 12, y, x + 13, y, color);//横线 LCD_DrawLine(x, y - 12, x, y + 13, color);//竖线 LCD_DrawPoint(x + 1, y + 1, color); LCD_DrawPoint(x - 1, y + 1, color); LCD_DrawPoint(x + 1, y - 1, color); LCD_DrawPoint(x - 1, y - 1, color); LCD_DrawCircle(x, y, 6, color);//画中心圈 } //画一个大点(2*2的点) //x,y:坐标 //color:颜色 void TP_Draw_Big_Point(uint16_t x, uint16_t y, uint16_t color) { LCD_DrawPoint(x, y, color);//中心点 LCD_DrawPoint(x + 1, y, color); LCD_DrawPoint(x, y + 1, color); LCD_DrawPoint(x + 1, y + 1, color); } /****************************************************************************** 函数说明:画线 入口数据:x1,y1 起始坐标 x2,y2 终止坐标 color 线的颜色 返回值: 无 ******************************************************************************/ void LCD_DrawRoughLine(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2,uint16_t color) { uint16_t t; int xerr = 0, yerr = 0, delta_x, delta_y, distance; int incx, incy, uRow, uCol; delta_x = x2 - x1; //计算坐标增量 delta_y = y2 - y1; uRow = x1;//画线起点坐标 uCol = y1; if(delta_x > 0) { incx = 1; //设置单步方向 } else if (delta_x == 0) { incx = 0;//垂直线 } else { incx = -1; delta_x = -delta_x; } if(delta_y > 0) { incy = 1; } else if (delta_y == 0) { incy = 0;//水平线 } else { incy = -1; delta_y = -delta_y; } if(delta_x > delta_y) { distance = delta_x; //选取基本增量坐标轴 } else { distance = delta_y; } for(t=0;t distance) { xerr -= distance; uRow += incx; } if(yerr > distance) { yerr -= distance; uCol += incy; } } } // 辅助函数:格式化坐标字符串(x,y转字符串) void TP_FormatCoord(char *buf, TP_Point p) { sprintf(buf, "X:%4d Y:%4d", p.x, p.y); // 格式化为"X:xxxx Y:yyyy" } /* ------------------------END----------------------- */