332 lines
6.9 KiB
C
332 lines
6.9 KiB
C
/*
|
||
* touch.c
|
||
*
|
||
* Created on: 2021年8月7日
|
||
* Author: wuwenfeng
|
||
*/
|
||
|
||
#include "touch.h"
|
||
#include "LCD.h"
|
||
#include "eeprom.h"
|
||
|
||
//默认为touchtype=0的数据.
|
||
#define CMD_RDX 0X90
|
||
#define CMD_RDY 0XD0
|
||
|
||
|
||
//SPI写数据
|
||
//向触摸屏IC写入1byte数据
|
||
//num:要写入的数据
|
||
void TP_Write_Byte(char num)
|
||
{
|
||
for(uint8_t count=0;count<8;count++)
|
||
{
|
||
if(num&0x80){TDIN(1);}
|
||
else {TDIN(0);}
|
||
num<<=1;
|
||
TCLK(0);
|
||
TCLK(1); //上升沿有效
|
||
}
|
||
}
|
||
|
||
//SPI读数据
|
||
//从触摸屏IC读取adc值
|
||
//CMD:指令
|
||
//返回值:读到的数据
|
||
uint16_t TP_Read_AD(char CMD)
|
||
{
|
||
uint16_t Num=0;
|
||
TCLK(0); //先拉低时钟
|
||
TDIN(0); //拉低数据线
|
||
TCS(0); //选中触摸屏IC
|
||
TP_Write_Byte(CMD);//发送命令字
|
||
HAL_GetTick(); //稍微延时,ad转换需要时间
|
||
HAL_GetTick();
|
||
HAL_GetTick();
|
||
HAL_GetTick();
|
||
HAL_GetTick();
|
||
HAL_GetTick();
|
||
TCLK(1); //给1个时钟,清除BUSY
|
||
TCLK(0);
|
||
for(uint8_t count=0;count<16;count++)//读出16位数据,只有高12位有效
|
||
{
|
||
Num<<=1;
|
||
TCLK(0); //下降沿有效
|
||
TCLK(1);;
|
||
if(TDOUT){Num++;}
|
||
}
|
||
Num>>=4; //只有高12位有效.
|
||
TCS(1); //释放片选
|
||
return(Num);
|
||
}
|
||
|
||
//读取一个坐标值(x或者y)
|
||
//连续读取READ_TIMES次数据,对这些数据升序排列,
|
||
//然后去掉最低和最高LOST_VAL个数,取平均值
|
||
//xy:指令(CMD_RDX/CMD_RDY)
|
||
//返回值:读到的数据
|
||
#define READ_TIMES 5 //读取次数
|
||
#define LOST_VAL 1 //丢弃值
|
||
uint16_t TP_Read_XOY(uint8_t xy)
|
||
{
|
||
uint16_t i, j;
|
||
uint16_t buf[READ_TIMES];
|
||
uint16_t sum=0;
|
||
uint16_t temp;
|
||
for(i=0;i<READ_TIMES;i++)buf[i]=TP_Read_AD(xy);
|
||
for(i=0;i<READ_TIMES-1; i++)//排序
|
||
{
|
||
for(j=i+1;j<READ_TIMES;j++)
|
||
{
|
||
if(buf[i]>buf[j])//升序排列
|
||
{
|
||
temp=buf[i];
|
||
buf[i]=buf[j];
|
||
buf[j]=temp;
|
||
}
|
||
}
|
||
}
|
||
sum=0;
|
||
for(i=LOST_VAL;i<READ_TIMES-LOST_VAL;i++)sum+=buf[i];
|
||
temp=sum/(READ_TIMES-2*LOST_VAL);
|
||
return temp;
|
||
}
|
||
|
||
//读取x,y坐标
|
||
//x,y:读取到的坐标ADC值
|
||
void TP_Read_XY_ADC(int16_t *x,int16_t *y)
|
||
{
|
||
int16_t xtemp,ytemp;
|
||
xtemp=TP_Read_XOY(CMD_RDX);
|
||
ytemp=TP_Read_XOY(CMD_RDY);
|
||
*x=xtemp;
|
||
*y=ytemp;
|
||
}
|
||
//连续2次读取触摸屏IC,且这两次的偏差不能超过
|
||
//ERR_RANGE,满足条件,则认为读数正确,否则读数错误.
|
||
//该函数能大大提高准确度
|
||
//x,y:读取到的坐标值
|
||
//返回值:0,失败;1,成功。
|
||
#define ERR_RANGE 10 //误差范围
|
||
uint8_t TP_Read_XY2(int16_t *x,int16_t *y)
|
||
{
|
||
int16_t x1,y1;
|
||
int16_t x2,y2;
|
||
|
||
TP_Read_XY_ADC(&x1,&y1);
|
||
|
||
TP_Read_XY_ADC(&x2,&y2);
|
||
|
||
if(((x2<=x1&&x1<x2+ERR_RANGE)||(x1<=x2&&x2<x1+ERR_RANGE))//前后两次采样在+-50内
|
||
&&((y2<=y1&&y1<y2+ERR_RANGE)||(y1<=y2&&y2<y1+ERR_RANGE)))
|
||
{
|
||
*x=(x1+x2)/2;
|
||
*y=(y1+y2)/2;
|
||
return 1;
|
||
}else return 0;
|
||
}
|
||
touch_device t0;// t0 yyds~
|
||
touch_config tconfig;
|
||
//触摸更新服务,状态机写法,循环获取坐标
|
||
void TP_Server()
|
||
{
|
||
if(TPEN==0) //如果有触摸
|
||
{
|
||
TP_Read_XY2(&t0.adc_x,&t0.adc_y); //先读取ad值
|
||
t0.pix_x=(t0.adc_x/tconfig.x_acc)-tconfig.x_offset;//转换为像素坐标
|
||
t0.pix_y=(t0.adc_y/tconfig.y_acc)-tconfig.y_offset;
|
||
|
||
}
|
||
}
|
||
//校准用,画一个目标坐标
|
||
//r=坐标半径,显示特效用
|
||
void TP_DrwaTrage(int x,int y,int r)
|
||
{
|
||
Draw_Circle(x,y,r+1,GRAY);
|
||
Draw_Circle(x,y,r,RED);
|
||
LCD_DrawLine(x,y,x+10,y,RED);
|
||
LCD_DrawLine(x,y,x,y+10,RED);
|
||
LCD_DrawLine(x,y,x-10,y,RED);
|
||
LCD_DrawLine(x,y,x,y-10,RED);
|
||
}
|
||
//触摸屏校准
|
||
void TP_adjustment()
|
||
{
|
||
//判断是否需要校准,从eeprom获取数据
|
||
EEPROM_READ_BATY(16,(char *)&tconfig,sizeof(touch_config));
|
||
if(tconfig.begin==0xab&&tconfig.end==0xcd) //判断校准标记
|
||
{
|
||
return; //已经校准过了
|
||
}
|
||
//校准方法比较简单,读取4个坐标计算ad值与像素的关系
|
||
char str[64]; //用于字符串提示
|
||
uint16_t y_adc,x_adc,step=0,r=10; //adc缓存,校准步骤,坐标的半径
|
||
uint16_t y1,y2,y3,y4,x1,x2,x3,x4; //4个点缓存
|
||
int y5,x5,xd,xl,yd,yl; //通过4个点算出xy的长边和短边
|
||
float acc_x,acc_y; //算出的关系倍率
|
||
int offset_x,offset_y; //算出的偏差
|
||
uint32_t wait=HAL_GetTick()+50000,ms100=0; //校准时间,50秒没操作就自动退出
|
||
//显示字符串提示
|
||
LCD_Clear(GRAY);
|
||
LCD_ShowString(0,50,"Calibrate the touch screen",16,RED,RED);
|
||
//TP_DrwaTrage(30,30,10);
|
||
//开始校准
|
||
while(HAL_GetTick()<wait)
|
||
{
|
||
if(TPEN==0) //如果屏幕被按下
|
||
{
|
||
wait=HAL_GetTick()+50000; //重置50秒
|
||
|
||
TP_Read_XY2(&x_adc,&y_adc); //读取xy ad值
|
||
//将读到的值显示出来
|
||
sprintf(str,"ADC_X:%04d",x_adc);
|
||
LCD_ShowString(100, 0, str, 16, RED, GRAY);
|
||
sprintf(str,"ADC_Y:%04d",y_adc);
|
||
LCD_ShowString(100, 16, str, 16, RED, GRAY);
|
||
//特效,半径开始收缩
|
||
if(HAL_GetTick()>ms100)
|
||
{
|
||
ms100=HAL_GetTick()+100;
|
||
if(r>0){r--;}
|
||
}
|
||
}
|
||
//步骤0,将点画在(30,30)此时半径为10
|
||
if(step==0)
|
||
{
|
||
TP_DrwaTrage(30,30,r);
|
||
if(r==0)//当半径收缩为0的时候
|
||
{
|
||
//进入下一个步骤,缓存这个点的值,显示出来
|
||
step+=1;
|
||
y1=y_adc;
|
||
x1=x_adc;
|
||
sprintf(str,"point_1 x:%d y:%d",x1,y1);
|
||
LCD_ShowString(0,66,str,16,RED,RED);
|
||
}
|
||
}
|
||
//步骤1,等待屏幕被松开,进入下一个步骤,重置半径
|
||
if(step==1)
|
||
{
|
||
if(TPEN==1)
|
||
{
|
||
step+=1;
|
||
r=10;
|
||
}
|
||
|
||
}
|
||
//下面几个步骤和上面一样
|
||
if(step==2)
|
||
{
|
||
TP_DrwaTrage(290,30,r);
|
||
if(r==0)
|
||
{
|
||
step+=1;
|
||
y2=y_adc;
|
||
x2=x_adc;
|
||
sprintf(str,"point_2 x:%d y:%d",x2,y2);
|
||
LCD_ShowString(0,66+16,str,16,RED,RED);
|
||
}
|
||
}
|
||
if(step==3)
|
||
{
|
||
if(TPEN==1)
|
||
{
|
||
step+=1;
|
||
r=10;
|
||
}
|
||
|
||
}
|
||
if(step==4)
|
||
{
|
||
TP_DrwaTrage(30,210,r);
|
||
if(r==0)
|
||
{
|
||
step+=1;
|
||
y3=y_adc;
|
||
x3=x_adc;
|
||
sprintf(str,"point_3 x:%d y:%d",x3,y3);
|
||
LCD_ShowString(0,66+16+16,str,16,RED,RED);
|
||
}
|
||
}
|
||
if(step==5)
|
||
{
|
||
if(TPEN==1)
|
||
{
|
||
step+=1;
|
||
r=10;
|
||
}
|
||
|
||
}
|
||
if(step==6)
|
||
{
|
||
TP_DrwaTrage(290,210,r);
|
||
if(r==0)
|
||
{
|
||
step+=1;
|
||
y4=y_adc;
|
||
x4=x_adc;
|
||
sprintf(str,"point_4 x:%d y:%d",x4,y4);
|
||
LCD_ShowString(0,66+16+16+16,str,16,RED,RED);
|
||
}
|
||
}
|
||
if(step==7)
|
||
{
|
||
if(TPEN==1)
|
||
{
|
||
step+=1;
|
||
r=10;
|
||
}
|
||
}
|
||
//当4个点读取完,开始计算关系
|
||
if(step==8)
|
||
{
|
||
//其实只需要两个点就能校准,通过取平均值获得xy的长边和短边
|
||
xd=((x1+x3)/2);
|
||
xl=((x2+x4)/2);
|
||
yd=((y1+y2)/2);
|
||
yl=((y3+y4)/2);
|
||
//长边减去短边可以再获得一个点
|
||
x5=xl-xd;
|
||
y5=yl-yd;
|
||
//这个点如果是负数,肯定有错,可能是xy搞反了
|
||
if(x5<0||y5<0)
|
||
{
|
||
//显示error
|
||
sprintf(str,"ERROR");
|
||
LCD_ShowString(0,66+16+16+16+16,str,16,RED, GRAY);
|
||
}else
|
||
{
|
||
//计算关系倍率
|
||
//ad的长边减去短边再除去实际屏幕像素的长边减短边(260=320-30-30,180=240-30-30)
|
||
acc_x=x5/260.0;
|
||
acc_y=y5/180.0;
|
||
//验证倍率,将实际值减去验证值就等于误差值,因为有两个点,所以计算了两个误差后求了平均值
|
||
offset_x=(((xd/acc_x)-30)+((xl/acc_x)-290))/2;
|
||
offset_y=(((yd/acc_y)-30)+((yl/acc_y)-210))/2;
|
||
//保存计算结果
|
||
tconfig.x_acc=acc_x;
|
||
tconfig.x_offset=offset_x;
|
||
tconfig.y_acc=acc_y;
|
||
tconfig.y_offset=offset_y;
|
||
//eeprom块标记
|
||
tconfig.begin=0xab;
|
||
tconfig.end=0xcd;
|
||
|
||
//显示计算结果
|
||
sprintf(str,"x_acc=%f y_acc=%f",acc_x,acc_y);
|
||
LCD_ShowString(0,66+16+16+16+16,str,16,RED,RED);
|
||
sprintf(str,"x_offset=%d y_offset=%d",offset_x,offset_y);
|
||
LCD_ShowString(0,66+16+16+16+16+16,str,16,RED,RED);
|
||
|
||
}
|
||
//将结果保存起来
|
||
EEPROM_WRITE_BATY(16,(char *)&tconfig,sizeof(touch_config));
|
||
HAL_Delay(1000);
|
||
return;
|
||
|
||
}
|
||
}
|
||
|
||
}
|
||
|