TFT-LCD屏幕触摸校准
触摸屏幕分类
1.电阻屏
电阻屏的主要部分是一块与显示器表面配合非常好的电阻薄膜屏,这是一种多层的复合薄膜,由一层玻璃或有机玻璃作为基层,表面涂有一层秀明的导电层,上面再盖有一层外表硬化处理、光滑防刮的塑料层,它的内表面也涂有一层透明导电层,在两层导电层之间有许多细小(小于千分之一英寸)的透明隔离点把它们隔开绝缘。当手指触摸屏幕时,平常绝缘的两层导电层在触摸点位置就有了一个接触,控制器侦测到这个接通后,其中一面导电层接通У轴方向的5Ⅴ均匀电压场,另一导电层将接触点的电压引至控制卡进行A/D转换,得到电压值后与5Ⅴ相比即可得触摸点的у轴坐标,同理得出Χ轴的坐标,这就是所有电阻技术触摸屏共同的最基本原理。
2.电容屏
电容屏是利用人体的电流感应进行工作的。当触摸电容屏时,由于人体电场,用户手指和工作面形成一个耦合电容,因为工作面上接有高频信号,于是手指吸收走一个很小的电流,这个电流分别从屏的四个角上的电极中流出,且理论上流经四个电极的电流与手指头到四角的距离成比例,控制器通过对四个电流比例的精密计算,得出位置。可以达到99%的精确度,具备小于3ms的响应速度。
电阻屏与电容屏的区别
1.电阻屏在触模时需要轻触压按,而电容屏即使很轻微的手指触碰就能激活。
2.电阻屏可以用任何物体来触摸,而电容屏是人体热感应工作原理,只能用手指的热感区来触摸,指甲和手写笔均无效。由于手指头的面积比手写笔大很多,因此电容屏的手机,触摸比较小图标或者菜单的时候,触摸精度无法做到电阻屏那么高。
3.电容屏可以很容易进行多点触摸,电阻屏一般不能实现多点触摸的。
4.电阻屏内部是软的,一般是在4到5层超薄的钢化玻璃中间夹杂细微的炭粒(显微镜下才能看见),通过按压导致上下两层的炭粒相互接触而接通触屏电路,产生触摸反应,容易产生划痕,易坏,容易触屏不灵,而电容屏都是采用单层加厚钢化玻璃,硬度大,耐旧,使用寿命长。
5.电阻屏在阳光下可视性稍差,电容屏则非常好,在阳光写可视性依然很强。
触摸芯片XPT2046
XPT2046是个ADC转换芯片,对屏幕被触摸时的点进行采样,然后转换为数字量,再通过SPI接口与STM32单片机通信
芯片基本工作原理
XPT2046 是一种典型的逐次逼近型模数转换器(SAR ADC),包含了采样/保持、模数转换、串口数据输出等功能。同时芯片集成有一个 2.5V的内部参考电压源、温度检测电路,工作时使用外部时钟。XPT2046 可以单电源供电,电源电压范围为 2.7V~5.5V。参考电压值直接决定ADC的输入范围,参考电压可以使用内部参考电压,也可以从外部直接输入 1V~VCC范围内的参考电压(要求外部参考电压源输出阻抗低)。X、Y、Z、VBAT、Temp和AUX模拟信号经过片内的控制寄存器选择后进入ADC,ADC可以配置为单端或差分模式。选择VBAT、Temp和AUX时可以配置为单端模式;作为触摸屏应用时,可以配置为差分模式,这可有效消除由于驱动开关的寄生电阻及外部的干扰带来的测量误差,提高转换准确度
时序图
CS为片选信号,为低表示芯片被选中
DCLK为时钟信号,由STM32产生
DIN为输入引脚,连接到STM32的MOSI引脚,STM32为主机,主机输出,从机输入;通信开始时STM32需要先发送一个字节(8位)来设置芯片,这里需要8个时钟
BUSY为忙信号,当DIN输入一个字节来设置芯片后,BUSY线会被拉高,表示芯片在进行设置或转换数据,待数据转换完毕,则BUSY线被拉低,这里需要1个时钟
DOUT为输出引脚 ,因为是12位的ADC芯片,所以转换后的数据有12位,对照着时钟线频率,可以先给7个时钟,读出7位数据,再给5个时钟,读出剩下的5位,再给3个时钟,数据后面的Zero Filled自动填零
DIN输入的控制字节
起始位——第一位,即 S 位。控制字的首位必须是 1,即 S=1。在 XPT2046 的 DIN 引脚检测到起始位前,所有的输入将被忽略。
地址——接下来的 3 位(A2、A1 和 A0)选择多路选择器的现行通道,触摸屏驱动和参考源输入;因为这个芯片不但能采集屏幕,还能采集温度,采集电池,所以要设置通道,设置为采集屏幕的
根据手册上的这个表格,看到想要设置Y轴则A2、A1、A0分别为0、0、1,要设置X轴则A2、A1、A0分别为1、0、1
MODE——模式选择位,用于设置 ADC 的分辨率。MODE=0,下一次的转换将是 12 位模式;MODE=1,下一次的转换将是 8 位模式;选择12位模式,所以该位为0
SER/ DFR位控制参考源模式,选择单端模式(SER/DFR=1),或者差分模式(SER/DFR=0)。在X坐标、Y坐标和触摸压力测量中,为达到最佳性能,首选差分工作模式;所以该位为0
注意:差分模式仅用于 X 坐标、Y 坐标和触摸压力的测量,其它测量要求采用单端模式
PD0 和 PD1——设置掉电和内部参考电压配置的关系。根据下表,一般设置为00
综上,控制字节为
读X轴坐标——11010000——0xD0
读Y轴坐标——10010000——0x90
CubeMx配置
需要注意XPT2046芯片的输入引脚DIN,是在时钟信号DCLK上升沿锁存数据的,在手册中有说到
因为SPI有4种模式,而在上升沿采集数据的是模式0和模式3,所以在配置与XPT2046通信的SPI时,要配置成模式0或者模式3
XPT2046芯片与STM32引脚连接
XPT2046 | STM32 |
---|---|
CS | SPI2_NSS |
DCLK | SPI2_SCK |
DIN | SPI2_MOSI |
DOUT | SPI2_MISO |
因为用到了SPI2,所以CubeMX中选择初始化SPI2
配置界面
选择全双工主机模式
波特率分频系数选择256,然后波特率速率就为70.312KBits/s,通信速率可以设置得小一点,因为XPT2046在接收到设置命令后转换数据是需要时间的,如果分频系数最小,通信速率最快,则通信时偶尔会不稳定,在保证稳定的前提下将速率设低点,其他没问题后生成keil工程
如果Baud Rate(波特率)不是70.312KBits/s,那是因为时钟配置中将APB1的时钟设置为了36M,将时钟配置中APB1分频设置为4分频,则APB1变为了18M,则可以得到70.312KBits/s
tips:SPI2挂载到了APB1总线上
触摸校准原理
需要触摸校准的原因:
1、2.8寸的TFT屏幕的分辨率为240x320,而触摸屏的ADC值为0-4096,由于是线性变化的,所以两者之间存在比列关系,需要计算出比例因素xFactor和yFactor
解释:屏幕显示是2.8寸,分辨率是240x320的,作显示时操作的是分辨率的坐标值,但屏幕同时有触摸功能,触摸功能也是2.8寸(同一块屏幕),作触摸时读取的值不是分辨率的坐标值,而是ADC值,这个ADC值在屏幕宽度上是0 ~ 4096,在高度上也是0 ~ 4096的,所以ADC值与屏幕分辨率存在线性关系
触摸时的ADC值 * 比例因素xFactor = 屏幕X轴坐标
触摸时的ADC值 * 比例因素yFactor = 屏幕Y轴坐标
2、TFT屏幕的坐标原点(0,0),并非与触摸屏的原点完全对应,有一定的偏移量,需要计算出偏移量xOffset与yOffset。
校准后,根据比例因素与偏移量计算出TFT屏幕坐标LCD_X和LCD_Y
LCD_X = (ADC_X * xFactor ) - xOffset
LCD_Y = (ADC_Y * yFactor ) - yOffset
进行屏幕校准,计算比例因素xFactor、yFactor和偏移量xOffset与yOffset的大概思路
1、首先在屏幕上取4个角和1个中心点,假设4个点与屏幕的距离都是20,则周围的4个点的坐标分别是
(x1,y1)= (20,20)
(x2,y2)=(20,LCD_Height-20)
(x3,y3)=(LCD_Width-20,LCD_Height-20)
(x4,y4)=(LCD_Width-20,20)
(x5,y5)=(LCD_Width/2,LCD_Height/2)
其中位置1 - 4用于计算比例因子与偏移量,位置5用于校验触摸校准是否正确
2、在屏幕上依次显示5个十字光标,它们的坐标就是上面4个角和1个中心点的坐标,等待用户进行触摸,然后依次获取5个位置的ADC坐标值,分别记为ADC_x1至ADC_x5,ADC_y1至ADC_y5
3、在上一步通过触摸5个点,获取到了它们的ADC值,但因为触摸是有误差的,如果直接用这些ADC值,则最后算出来的比例因素和偏移量都是不太准确的,所以要将4个触摸点整合成对角的两个点,减少触摸误差
说明:要计算的是左上角和右下角的坐标,通过周围4个触摸点的坐标来计算
左上角的X坐标 = (触摸点0的X坐标 + 触摸点1的X坐标)/ 2
左上角的Y坐标 = (触摸点0的Y坐标 + 触摸点3的Y坐标)/ 2
右下角的X坐标 = (触摸点2的X坐标 + 触摸点3的X坐标)/ 2
右下角的Y坐标 = (触摸点1的Y坐标 + 触摸点2的Y坐标)/ 2
这样就得到了左上角的(x,y)坐标,和右下角的(x,y)坐标
4、取上一步得到的左上角坐标和右下角坐标的平均值,与中间触摸点的坐标进行对比,超过一定范围则认为触摸校准失败,不超过则成功
5、根据对角坐标和屏幕分辨率,计算出计算比例因素xFactor、yFactor和偏移量xOffset与yOffset
6、将校准参数存储在外部Flash当中
部分程序
用SPI2读取触摸屏的ADC值,参数_ucXPT2046_CMD为0xD0时是读取X轴坐标,为0x90时是读取Y轴坐标
/**
* @name Touch_Read_ADC
* @brief 读取触摸屏的ADC值
* @param _ucXPT2046_CMD:触摸IC命令
* @retval None
*/
static uint16_t Touch_Read_ADC(uint8_t _ucXPT2046_CMD)
{
uint8_t i,j;
uint16_t usValue_Buf[TOUCH_READ_TIMERS],usTemp;
uint32_t uiSumValue = 0;
//通过SPI接口循环读取TOUCH_READ_TIMES次数的ADC值(5次)
for(i=0;i<TOUCH_READ_TIMERS;i++)
{
//选择触摸芯片,CS输出低电平
CLR_SPI_TOUCH_CS;
/*
在差分模式下,XPT2046转换需要24个时钟,8个时钟输入命令,延时一会,
之后1个时钟去除忙信号,接着输出12位转换结果,剩下3个时钟是忽略位
*/
//发送控制命令
SPI_Touch_WriteByte(_ucXPT2046_CMD);
//延时一会,等待ADC转换
for(j=0;j<100;j++);
//读取数据
usValue_Buf[i] = SPI_Touch_ReadByte(); //获取前面7位(一个周期),其中最高位无效
usValue_Buf[i] &= (~BIT7); //最高位清零
usValue_Buf[i] <<= 8; //左移至最高字节
usValue_Buf[i] += SPI_Touch_ReadByte(); //获取后面5个字节,其中低3位无效
usValue_Buf[i] >>= 3; //右移3位,得到12位有效数据
//禁用触摸芯片,CS输出高电平
SET_SPI_TOUCH_CS;
}
//采样值从大到小排序,冒泡排序
for(i=0;i<(TOUCH_READ_TIMERS-1);i++) //趟数:4次
{
for(j=i+1;j<TOUCH_READ_TIMERS;j++) //比较次数
{
if(usValue_Buf[i]<usValue_Buf[j])
{
usTemp = usValue_Buf[i];
usValue_Buf[i] = usValue_Buf[j];
usValue_Buf[j] = usTemp;
}
}
}
//去掉最大最小值,求和
for(i=1;i<(TOUCH_READ_TIMERS-1);i++)
{
uiSumValue += usValue_Buf[i];
}
//返回平均值
return uiSumValue/(TOUCH_READ_TIMERS-2);
}