K1Guitar/device/LCD_ILI9341/Drv_XPT2046_Touch.c

378 lines
8.7 KiB
C
Raw Normal View History

#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+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----------------------- */