K1Guitar/device/LCD_ILI9341/Drv_XPT2046_Touch.c

340 lines
8.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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+1;t++)
{
TP_Draw_Big_Point(uRow, uCol, color);//画点
xerr += delta_x;
yerr += delta_y;
if(xerr > 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----------------------- */