不知道大家有没有发现GG/LG 的DK上是附带了一个4线制触摸屏的。如果大家手头有DK的话,不妨载入Touch的demo,来试用一下。
这里就不说4线制的触摸屏的原理了,但是为了后续说明方便,我把这部分的硬件连接,以及软硬件处理方式单独提了出来,做了一个简要的说明。
1.硬件连接
如下图所示,触摸屏的四根信号线是直接连接到了MCU的AD脚。DISPLAY_TOUCH_xx以及EFM32_Bxx信号,是原理图上的Net(网络标号). 因为DK上的MCU是单独的一块小班,触摸屏在主板上,两者是通过插座连接在一起的,这些个Net就是是插座上的或则多路开关上的标识.
相对应的原理图的设计也比较简单,如下图所示:
R457下面标识了一个NM,表示No mount,不焊接的意思。但是从我个人角度来讲,这样的电路是有些简单了。因为触摸屏信号是直接到MCU的IO口,而且触摸屏是人直接接触的。大家应该都有冬天被静电打的过的经验吧,那可不仅仅只有2Kv哦。另外一个方面就是现在产品的安规等级越来越高了,甚至有客户接触需要过10Kv,12Kv的。。如果仅仅是通过阻容来保护的话,是不够的,建议在接口处加低漏电流的TVS管。因此改进之后的原理图大约如下:
2.软件实现:
软件部分,可以分为三个部分,一个是触摸屏校正,一个是检测是否有触摸动作,还有一个就是检测触摸的位置。
触摸屏的校正,还没有整理完毕,放在下节说明。
2.1 触摸动作的检测
2.1 GG/LG Touch demo中的触摸检测:
在软件中,调用了Touch()函数来判断是否有触摸的动作发生,具体检测的流程如下:
1. 将X1设置成输出0,X2设置成输出1,Y1配制成输入,Y2配制成推挽输出1,等待10uS,再切换成上拉输入。等待10uS
2.在Y2上进行一次ADC转换,并检查ADC的结果。
但是这样做虽然简单,但是效率却是不高。因为MCU仅仅是被动的检测是否有触摸发生。
整个代码如下:
static bool touched( void )
{
uint32_t adcValue;
GPIO_PinModeSet(LCD_TOUCH_X1, gpioModePushPull, 0);
GPIO_PinModeSet(LCD_TOUCH_X2, gpioModePushPull, 1);
GPIO_PinModeSet(LCD_TOUCH_Y1, gpioModeInput, 0);
GPIO_PinModeSet(LCD_TOUCH_Y2, gpioModePushPull, 1);
delayUs( 10 );
GPIO_PinModeSet(LCD_TOUCH_Y2, gpioModeInputPull, 1);
delayUs( 10 );
sInit.input = ADC_Y;
ADC_InitSingle(ADC0, &sInit);
adcValue = readAdc();
GPIO_PinModeSet(LCD_TOUCH_X1, gpioModeInput, 0);
GPIO_PinModeSet(LCD_TOUCH_X2, gpioModeInput, 0);
GPIO_PinModeSet(LCD_TOUCH_Y2, gpioModeInput, 0);
return ( adcValue < 3800 ) ? true : false;
}
ADC检测的是Y2引脚,即PD1。目前还不理解为什么ADC在Y2引脚上做。而且是使用先充电,在检测的方式。感觉在Y1引脚上也是可以做的。
另外一种检测的方式,就是使用GPIO口中断的方式来进行。检测的步骤如下:
1. X1,X2配置为输出0.Y1配置为开漏带上拉输出。Y2配置为输入。
2.如果没有触摸,则Y2上的电平为高。因为Y1上有上拉。
3.如果有触摸,由于X1,X2上对地的电阻很小,因此被X1,X2上的电阻拉低。此时可以产生一个下降沿中断。
这个部分的原理,是因为电阻屏的阻值一般在几百ohm左右,再加上X1,X2对地电阻并联效应,相对于40kohm的上下拉来说,暂时可以忽略不记。虽未被实际验证过,但是理论上应该是没有问题。
如上仅仅是一种方式,由于屏本身的阻值较小,因此还有好几种方式可以实现触摸的中断检测。
2.2 触摸位置的检测:
这里先只讨论如何获得ADC值。如果要则算坐标,则需要先校正才能折算。
检测的步骤:
1.Y1输出0,Y2输出1,X1,X2输入。等待10us
2.开启ADC转换,取得Y的ADC值。
3.Y1,Y2输入,X1输出1,X2输出0,等待10us
4.开启ADC转换,取得X的ADC值。
5.配置所有的IO口为输入口。
之所以等待10us,相当于用10us的时间给外部充电。除了如上的操作之前,我们还带了最大值,最小值检测,平均算法等来增加触摸检测的精度。
函数的整体调用关系如下:
static uint32_t getTouchChSample12bit( ADC_SingleInput_TypeDef ch )
{
int i;
uint32_t value, min, max, acc;
if ( ch == ADC_X )
{
GPIO_PinModeSet(LCD_TOUCH_Y1, gpioModePushPull, 0);
GPIO_PinModeSet(LCD_TOUCH_Y2, gpioModePushPull, 1);
GPIO_PinModeSet(LCD_TOUCH_X1, gpioModeInput, 0);
GPIO_PinModeSet(LCD_TOUCH_X2, gpioModeInput, 0);
}
else
{
GPIO_PinModeSet(LCD_TOUCH_X1, gpioModePushPull, 1);
GPIO_PinModeSet(LCD_TOUCH_X2, gpioModePushPull, 0);
GPIO_PinModeSet(LCD_TOUCH_Y1, gpioModeInput, 0);
GPIO_PinModeSet(LCD_TOUCH_Y2, gpioModeInput, 0);
}
sInit.input = ch;
ADC_InitSingle(ADC0, &sInit);
delayUs( 10 );
acc = 0;
max = 0;
min = 4096;
for ( i=0; i<5; i++ )
{
value = readAdc();
acc += value;
min = EFM32_MIN( min, value );
max = EFM32_MAX( max, value );
}
/* Throw away largest and smallest sample */
acc = acc - min - max;
/* Average */
acc = acc / 3;
if ( ch == ADC_X )
{
GPIO_PinModeSet(LCD_TOUCH_Y1, gpioModeInput, 0);
GPIO_PinModeSet(LCD_TOUCH_Y2, gpioModeInput, 0);
}
else
{
GPIO_PinModeSet(LCD_TOUCH_X1, gpioModeInput, 0);
GPIO_PinModeSet(LCD_TOUCH_X2, gpioModeInput, 0);
}
return acc;
}
如上是目前电阻式触摸屏在GG/LG DK上的实现。