#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(); Delay_us(2); TP_Write_Byte(cmd); Delay_us(6); for (i = 0; i < 16; i++) { adc_val <<= 1; TP_CLK_Set(); Delay_us(1); TP_CLK_Clr(); Delay_us(1); if (TP_OUT_Read()) adc_val |= 0x01; } TP_CS_Set(); return adc_val >> 4; } uint8_t TP_CheckPressed(uint8_t n) { uint8_t count = 0; uint8_t i; for (i = 0; i < n; i++) { if (TP_IRQ_Read() == 0) count++; Delay_us(100); } return (count >= (n / 2)) ? 1 : 0; } static uint16_t TP_SampleAxis(uint8_t cmd) { (void)TP_Read_AD(cmd); Delay_us(15); return TP_Read_AD(cmd); } TP_Point TP_ReadRaw(void) { TP_Point p = {0, 0, 0}; uint32_t x_sum = 0, y_sum = 0; uint8_t i; if (!TP_CheckPressed(3)) return p; for (i = 0; i < 5; i++) { x_sum += TP_SampleAxis(XPT2046_READ_X); Delay_us(15); y_sum += TP_SampleAxis(XPT2046_READ_Y); Delay_us(15); } p.x = (uint16_t)(x_sum / 5); p.y = (uint16_t)(y_sum / 5); /* Y 通道常为 0 时,交换 X/Y 命令再采一次 */ if (p.y < 80u && p.x > 80u) { uint32_t sx = 0, sy = 0; for (i = 0; i < 3; i++) { sx += TP_SampleAxis(XPT2046_READ_Y); Delay_us(15); sy += TP_SampleAxis(XPT2046_READ_X); Delay_us(15); } if (sy / 3u > p.y) { p.x = (uint16_t)(sx / 3); p.y = (uint16_t)(sy / 3); } } p.pressed = 1; return p; } #define CALIB_X_MIN 200 #define CALIB_X_MAX 3800 #define CALIB_Y_MIN 200 #define CALIB_Y_MAX 3800 /* 竖屏:多数 ILI9341+XPT2046 板级需 XY 对调;X 镜像与历史 240-tp.x 一致 */ #define TP_SWAP_XY 1 #define TP_MIRROR_X 1 #define TP_MIRROR_Y 0 static uint16_t TP_MapAxis(uint16_t adc, uint16_t adc_min, uint16_t adc_max, uint16_t px_max) { int32_t v = (int32_t)adc; int32_t a0 = (int32_t)adc_min; int32_t a1 = (int32_t)adc_max; int32_t span = a1 - a0; int32_t px; if (span <= 0) return 0; if (v < a0) v = a0; if (v > a1) v = a1; px = (v - a0) * (int32_t)px_max / span; if (px < 0) px = 0; if (px > (int32_t)px_max) px = (int32_t)px_max; return (uint16_t)px; } TP_Point TP_Read(void) { TP_Point raw = TP_ReadRaw(); TP_Point calib = {0, 0, raw.pressed}; uint16_t ax, ay; if (!raw.pressed) return calib; #if TP_SWAP_XY ax = TP_MapAxis(raw.y, CALIB_Y_MIN, CALIB_Y_MAX, (uint16_t)(LCD_W - 1)); ay = TP_MapAxis(raw.x, CALIB_X_MIN, CALIB_X_MAX, (uint16_t)(LCD_H - 1)); #else ax = TP_MapAxis(raw.x, CALIB_X_MIN, CALIB_X_MAX, (uint16_t)(LCD_W - 1)); ay = TP_MapAxis(raw.y, CALIB_Y_MIN, CALIB_Y_MAX, (uint16_t)(LCD_H - 1)); #endif #if TP_MIRROR_X ax = (uint16_t)(LCD_W - 1 - ax); #endif #if TP_MIRROR_Y ay = (uint16_t)(LCD_H - 1 - ay); #endif calib.x = ax; calib.y = ay; 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----------------------- */