475 lines
11 KiB
C
475 lines
11 KiB
C
#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);
|
||
|
||
// DOUT:浮空输入(内部上拉易把空闲读成满量程)
|
||
gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
|
||
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
|
||
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:指令
|
||
//返回值:读到的数据
|
||
/* 在 CS 已拉低时写命令并读:与量产一致,16 时钟取高 12bit */
|
||
static uint16_t TP_Read_AD_CSHeld(uint8_t cmd)
|
||
{
|
||
uint8_t i;
|
||
uint16_t adc_val = 0;
|
||
|
||
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;
|
||
}
|
||
|
||
return (uint16_t)(adc_val >> 4);
|
||
}
|
||
|
||
uint16_t TP_Read_AD(uint8_t cmd)
|
||
{
|
||
uint16_t v;
|
||
|
||
TP_CS_Clr();
|
||
Delay_us(2);
|
||
v = TP_Read_AD_CSHeld(cmd);
|
||
TP_CS_Set();
|
||
return v;
|
||
}
|
||
|
||
#define TP_ADC_PRESS_MIN 120u
|
||
#define TP_ADC_PRESS_MAX 4095u
|
||
#define TP_PRESSURE_MIN 80u
|
||
/* 本板 IRQ 无效;压力仅作弱参考,不过度拦截(真触摸 z 也常偏大) */
|
||
#define TP_PRESSURE_MAX 5000u
|
||
|
||
static uint16_t TP_Median5(uint16_t *a)
|
||
{
|
||
uint8_t i, j;
|
||
uint16_t t;
|
||
|
||
for (i = 0; i < 4; i++) {
|
||
for (j = (uint8_t)(i + 1U); j < 5U; j++) {
|
||
if (a[j] < a[i]) {
|
||
t = a[i];
|
||
a[i] = a[j];
|
||
a[j] = t;
|
||
}
|
||
}
|
||
}
|
||
return a[2];
|
||
}
|
||
|
||
static void TP_SampleXY(uint16_t *out_x, uint16_t *out_y)
|
||
{
|
||
uint16_t xs[5], ys[5];
|
||
uint8_t i;
|
||
|
||
for (i = 0; i < 5; i++) {
|
||
xs[i] = TP_Read_AD(XPT2046_READ_X);
|
||
Delay_us(15);
|
||
ys[i] = TP_Read_AD(XPT2046_READ_Y);
|
||
Delay_us(15);
|
||
}
|
||
|
||
*out_x = TP_Median5(xs);
|
||
*out_y = TP_Median5(ys);
|
||
}
|
||
|
||
static uint16_t TP_ReadPressure(void)
|
||
{
|
||
uint16_t z1, z2;
|
||
|
||
z1 = TP_Read_AD(XPT2046_READ_Z1);
|
||
Delay_us(15);
|
||
z2 = TP_Read_AD(XPT2046_READ_Z2);
|
||
|
||
return (z1 > z2) ? (uint16_t)(z1 - z2) : 0U;
|
||
}
|
||
|
||
static uint8_t TP_AdcCoordValid(uint16_t x, uint16_t y)
|
||
{
|
||
/* 仅排除明显无效;双高在本板上也可能是真触摸(Y 常贴高,垂直靠 raw.x) */
|
||
if (x < TP_ADC_PRESS_MIN && y < TP_ADC_PRESS_MIN)
|
||
return 0U;
|
||
if (x > 4000u && y > 4000u)
|
||
return 0U;
|
||
return (x <= TP_ADC_PRESS_MAX && y <= TP_ADC_PRESS_MAX) ? 1U : 0U;
|
||
}
|
||
|
||
#define CALIB_X_MIN 200
|
||
#define CALIB_X_MAX 3800
|
||
#define CALIB_Y_MIN 200
|
||
#define CALIB_Y_MAX 3950
|
||
|
||
/*
|
||
* 竖屏线性映射(与 BOOT/量产一致):raw.x→屏幕X,raw.y→屏幕Y,不做镜像。
|
||
* 若 TP_MIRROR_Y=1 会导致 Y 轴反转(点「专业」误中「万能」)。
|
||
* DOUT(PA15) 浮空输入;空闲 raw.y 噪声禁止走 ADC 回退。
|
||
*/
|
||
#define TP_SWAP_XY 0
|
||
#define TP_MIRROR_X 0
|
||
#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;
|
||
}
|
||
|
||
static void TP_RawToScreen(uint16_t raw_x, uint16_t raw_y, uint16_t *sx, uint16_t *sy)
|
||
{
|
||
uint16_t ax, ay;
|
||
|
||
#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
|
||
*sx = ax;
|
||
*sy = ay;
|
||
}
|
||
|
||
void TP_LogStatus(void)
|
||
{
|
||
uint16_t x, y, z;
|
||
uint16_t sx, sy;
|
||
|
||
z = TP_ReadPressure();
|
||
TP_SampleXY(&x, &y);
|
||
TP_RawToScreen(x, y, &sx, &sy);
|
||
LOG_TPI("status irq=%u z=%u raw=%u,%u cal=%u,%u",
|
||
(unsigned)(TP_IRQ_Read() == 0U),
|
||
(unsigned)z,
|
||
(unsigned)x, (unsigned)y,
|
||
(unsigned)sx, (unsigned)sy);
|
||
}
|
||
|
||
static uint8_t s_tp_live;
|
||
|
||
void TP_SetLiveMode(uint8_t on)
|
||
{
|
||
s_tp_live = on ? 1U : 0U;
|
||
LOG_TPI("live %s (events only)", s_tp_live ? "on" : "off");
|
||
}
|
||
|
||
/* 不再空闲刷屏;按下事件仍由 touch task 的 scan/press/release 打印 */
|
||
void TP_LivePoll(void)
|
||
{
|
||
(void)s_tp_live;
|
||
}
|
||
|
||
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 + 1U) / 2U)) ? 1U : 0U;
|
||
}
|
||
|
||
TP_Point TP_ReadRaw(void)
|
||
{
|
||
TP_Point p = {0, 0, 0};
|
||
uint8_t irq_pressed;
|
||
static uint8_t s_raw_logged;
|
||
uint16_t rx, ry;
|
||
|
||
irq_pressed = TP_CheckPressed(3);
|
||
if (!irq_pressed) {
|
||
/*
|
||
* 空闲时 ADC 常读成 y=4095,会误触发幽灵点击。
|
||
* 本板真触摸 IRQ 能拉低,只认 IRQ 路径。
|
||
*/
|
||
s_raw_logged = 0U;
|
||
return p;
|
||
}
|
||
|
||
TP_SampleXY(&rx, &ry);
|
||
if (!TP_AdcCoordValid(rx, ry)) {
|
||
s_raw_logged = 0U;
|
||
return p;
|
||
}
|
||
|
||
p.x = rx;
|
||
p.y = ry;
|
||
p.pressed = 1;
|
||
if (!s_raw_logged) {
|
||
LOG_TPI("raw x=%u y=%u irq=1", (unsigned)p.x, (unsigned)p.y);
|
||
s_raw_logged = 1U;
|
||
}
|
||
return p;
|
||
}
|
||
|
||
TP_Point TP_Read(void)
|
||
{
|
||
TP_Point raw = TP_ReadRaw();
|
||
TP_Point calib = {0, 0, raw.pressed};
|
||
static uint8_t s_cal_logged;
|
||
|
||
if (!raw.pressed) {
|
||
s_cal_logged = 0U;
|
||
return calib;
|
||
}
|
||
|
||
TP_RawToScreen(raw.x, raw.y, &calib.x, &calib.y);
|
||
if (!s_cal_logged) {
|
||
LOG_TPI("cal x=%u y=%u (raw %u,%u)", (unsigned)calib.x, (unsigned)calib.y,
|
||
(unsigned)raw.x, (unsigned)raw.y);
|
||
s_cal_logged = 1U;
|
||
}
|
||
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----------------------- */
|
||
|
||
|