Files
m3s_stm32/HW_Devices/touch.c
T
2021-08-16 12:33:10 +08:00

344 lines
7.2 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.
/*
* 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;
}
}
//直接读取
char TP_XY(int *x,int *y)
{
if(TPEN==0) //如果有触摸
{
TP_Read_XY2(&t0.adc_x,&t0.adc_y); //先读取ad值
*x=(t0.adc_x/tconfig.x_acc)-tconfig.x_offset;//转换为像素坐标
*y=(t0.adc_y/tconfig.y_acc)-tconfig.y_offset;
return 1;
}
return 0;
}
//校准用,画一个目标坐标
//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-30180=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;
}
}
}