1 电阻触摸屏原理#
触摸屏包含上下叠合的两个透明层,一般覆盖在lcd表面,两个透明层是由均匀的电阻介质组成,如下图:
当触摸屏表面受到的压力(如通过笔尖或手指进行按压)足够大时,顶层与底层之间的薄膜会产生接触,此时会形成x方向和y方向的坐标。那么x,y坐标的值是怎么得来的呢?本质上就是通过ADC转换得来的。
触摸屏的等效电路可以看成如下图:
计算触点的X,Y坐标分为如下两步:
1.1 计算Y坐标#
在Y+电极施加驱动电压Vdrive, Y-电极接地,由于上下两层膜形成触点,X+做为触点的引出端,测量得到接触点的电压,触点电压与Vdrive电压之比等于触点Y坐标与屏高度之比。如下图:
1.2 计算X坐标#
在X+电极施加驱动电压Vdrive, X-电极接地,由于上下两层膜形成触点,Y+做为触点的引出端,测量得到接触点的电压,Y+做为引出端测量得到接触点的电压,触点电压与Vdrive电压之比等于触点X坐标与屏宽度之比。如下图:
2 电阻触摸屏的几种模式#
2.1 等待中断模式#
平时的时候上下两层膜并不粘在一起,我们把这种状态称为“等待中断模式”, 等效电路如下图的右边那幅图:
s5、s4闭合,s1、s2、s3断开,这个时候Y_ADC/XP通过S5接上拉电阻,处于高电平状态,X_ADC/YP接地。没法读取x,y坐标。
2.2 读取x坐标模式#
给X方向通电,也就是让S1、S3开关闭合,s2、s4断开,那么当屏幕按下,触点YP的电平就对应x坐标。(XP到XM之间是均匀的电阻介质)
x_adc电压/vcc = x坐标/width, 所以x坐标= width * x_adc电压/vcc
2.3 读取y坐标模式#
给Y方向通电,也就是让S2、S4开关闭合,s1、s3断开,那么当屏幕按下,触点XP的电平就对应y坐标。(YP到YM之间是均匀的电阻介质)
y_adc电压/vcc = Y坐标/height, 所以y坐标= height * y_adc电压/vcc
2.4 TS中断流程#
总结一下单次触发TS中断,使用触摸屏的流程:
1 | 1. 按下触摸屏,产生TS中断 |
我们知道,现在的手机都是支持屏幕滑动翻页和长按的功能。那么这些功能是如何做到的呢?
2.4.1 中断加入定时器#
如何让触摸屏支持长按或者滑动操作(多次触发TS中断)?
答案:定时器,当长按屏幕,会产生多次TS中断,因此我们需要用定时器来判断,当定时一段时间后,还有TS中断产生,那么我们认为是长按操作,进行中断响应。滑动也是类似的道理,当定时时间到后,如果还有TS中断产生,且坐标发生了改变,就认为是滑动操作。
<5> 启动定时器
<6> 一段时间后,定时器中断发生,判断触摸屏是否仍被按下(是否有定时器中断产生),如果有就循环上述过程<2><3><4><5>
可以用如下流程图概括TSC的整个SW flow.
2.4.2 带定时器的TS中断处理流程#
3 触摸屏接口模式#
3.1 Normal Conversion Mode#
正常转换模式,一般情况下可以配置ADCCON和ADCDAT0来读取数据。
3.2 Separate X/Y position conversion Mode#
x,y坐标分离转换格式,x坐标会写入ADCDAT0, y坐标会写入ADCDAT1,所以会产生2次中断开分开完成x,y的坐标转换。
3.3 Auto(Sequential) X/Y Position Conversion Mode#
自动转换模式,当触摸屏按下后,会一次性对x,y方向的坐标进行转换,x坐标会写入ADCDAT0, x坐标会写入ADCDAT1。会产生一次中断进行x,y坐标的自动转换。
3.4 Waiting for Interrupt Mode#
等待中断模式 。可以设置rADCTSC=0xd3;也就是对应下图寄存器 // XP_PU, XP_Dis, XM_Dis, YP_Dis, YM_En.当产生中断信号(INT_TC)后,等待中断模式必须清除.(即XY_PST sets to the No operation Mode).
4 触摸屏控制器#
4.1 TS控制寄存器#
电阻触摸屏的原理本质上就是ADC,ADC相关寄存器介绍详见s3c2440裸机-ADC编程或者s3c2440裸机编程-ADC | Hexo (fuzidage.github.io)
TSC相比ADC多了一个ADCTSC寄存器,如下图:
当bit[2]=0,normal mode时,那么bit[1:0]需要配置成01或者10进行手工测量x,y.
当bit[2]=1,auto mode时,那么bit[1:0]需要配置成0,进行自动测量。
4.2 DATA寄存器#
4.2.1 x坐标ADCDATA0#
4.2.2 y坐标ADCDATA1#
4.3 松开按下检测寄存器#
这个寄存器可以检测是否有触摸中断产生,是按下触摸屏了,还是松开触摸屏了。
5 触摸屏编程示例#
5.1 ADC中断产生#
5.1.1 中断源#
ADC和TSC共用一个中断源,如下:
SRCPND表示哪个中断源产生了中断请求。
5.1.2 中断模式#
5.1.3 中断屏蔽寄存器#
5.1.4 中断挂起寄存器#
用来显示当前优先级最高的、正在发生的中断, 需要清除对应位。
从SRCPND寄存器可以读到ADC和TSC复用的同一个中断源,那么如何区分呢?
可以从SUBSRCPND寄存器配置,如下:
5.1.4.1 SUBSRCPND寄存器#
当bit 9被置1时,表示TSC中断。那么我们需要打开subsrcmask寄存器:
5.1.4.2 INTSUBMSK寄存器#
所以TSC中断的产生流程如下:
5.2 TS触摸屏编程流程#
1 | 1. 初始化TSC,ADCTSC寄存器 |
5.2.1 初始化#
1 | void touchscreen_init(void) { |
5.2.1.1 ts寄存器初始化#
主要是设置预分频,产生ADC clk = 1MHz。
1 | void adc_ts_reg_init(void) { |
5.2.1.2 ts 中断初始化#
为了将中断源开启,这里设置SUBSRCPND 和INTSUBMSK让中断源开启。通过register_irq()注册中断号和中断服务程AdcTsIntHandle,查表得出中断号为31,这样当硬件产生中断后可以从INTOFFSET区分是哪个中断号。如下图:
1 | void adc_ts_int_init(void) { |
5.2.1.3 进入”等待中断模式”#
进入等待中断模式,YM闭合, YP, XP, XM断开,需要pull up,WAIT_PEN_DOWN表示要等待的是按下中断,当触摸屏按下时就会产生一个TSC irq,反之WAIT_PEN_UP表示要等待的是松开中断。
1 |
|
5.2.2 ts中断服务程序#
SUBSRCPND的bit9, bit10可以区分是TC中断还是ADC中断。
1 | void Isr_Tc(void)/*触摸屏中断服务程序*/ { |
1 | AdcTsIntHandle函数: 这里先注解掉ADC中断,只检测单独的按下松开触摸屏操作。那当isr处理完后为了能够正常响应下一次中断,需要清中断,否则会一直触发interrupt。 |
5.2.2.1 获取触摸屏坐标#
5.2.2.1.1 进入自动测量模式#
Auto(Sequential) X/Y Position Conversion Mode。打开TS控制寄存器,也就是ADCTSC寄存器:
让bit[2] =1, bit[1:0]=00,则会进入auto measurement。如果bit[2]=0,则需配置bit[1::0]=01 or 10是手动测量x,y坐标。
1 |
|
5.2.2.1.2 启动ADC#
触摸屏坐标就是通过ADC获取的。
1 | ADCCON |= (1<<0); |
所以TSC isr程序如下:
1 | void Isr_Tc(void) {/*触摸屏中断服务程序*/ |
那么当检测到按下后,需要进入auto measure mode,启动adc,然后就会进行自动坐标转换,转换结束后又会触发ADC中断,再次进入AdcTsIntHandle
函数,进而进入Isr_Adc
,SUBSRCPND可以区分中断源 。如下:
1 | void AdcTsIntHandle(int irq) { |
我们知道ADC进行坐标转换结束后,那么会产生ADC中断,在Isr_Adc
中即可获取我们的x,y坐标数据。由于我们按下后是进入了 “自动测量” 模式,因此那当数据获取完后我们得进入 “等待松开” 模式。
1 | void Isr_Adc(void) { |
有可能触摸屏的测量过程非常长,那当ADC转换结束后,它已经松开了,这时不应该进行打印出坐标,所以这里在isr_Tc按下后,如果仍然按下才打印。
5.2.2.2 ADCDLY寄存器#
由于触摸屏采样的转换速率问题,按下后需要过一段电压才能稳定下来,那么数据才能稳定可能需要一定的延迟,所以需要配置ADC delay,让ADC慢一点产生中断,也就是等坐标稳定后在通知用户。
ADCDLY就是用来延时ADC启动的时间,让数据稳定后再进行转换。
可以看到,进行auto or manual measure 坐标转换的时序要满足:A = Dx,D表示ADCDLY的值。 现在晶振的频率是12Mhz, 那么根据触摸屏规格书我们取A= 5ms,那么D= 0.005s *12*1000000 = 60000,所以ADCDLY配置成60000.
修改前面的adc_ts_reg_init
函数:
1 | void adc_ts_reg_init(void) { |
5.3 TS触摸屏测试#
从左往右依次点击触摸屏,可以看到x坐标没有明显变化,y坐标反而线性变大。
同理,从上往下依次按下触摸屏,可以看到y坐标没有明显变化,x坐标反而线性变大。
这里是由于硬件上xp与yp接反了,ym与xm接反了,如下图:但这里并不影响我们的时候,这里我们软件上可以进行x,y坐标的转换:
我们软件上可以对x,y轴进行flip, mirror, rotaion旋转等一系列操作即可。比如:
Case1:ts与lcd吻合
Case2:ts与lcd相反
5.4 利用定时器支持屏幕长按和滑动#
5.4.1 改进定时器#
前面s3c2440裸机-异常中断 | Hexo (fuzidage.github.io)有讲到在handle_irq_c()
中去区分中断源,执行不同的isr
。
那现在通过register_timer
注册对应的定时器中断服务程序,timer_irq
进行执行不同的定时器中断服务程序。
1 |
|
我们想要用timer来进行进行流水灯实验,那么假如点灯函数为:Isr_timer_led(){}
那么则只需要在led init
的时候进行调用register_timer(“led”, Isr_timer_led)
, 那么当时间到后触发定时器中断,便会执行timer_irq
.进入Isr_timer_led
。
5.4.2 初始化定时器#
前面s3c2440裸机-异常中断 | Hexo (fuzidage.github.io)有具体讲解,这里采用PWM定时器。
1 | void timer_init(void) { |
5.4.2.1 支持长按和滑动#
我们之前是2s timer触发一次中断,那如果是要支持触摸屏,我们必须让定时器10ms就触发一次中断。因此需要修改timer_init中的寄存器参数。
当按下触摸屏会产生TSC中断,然后启动ADC进而产生adc
中断的时候,在Isr_Adc
函数中进行定时器的设置,检测长按和滑动操作。
5.4.2.1.1 定义touchscreen_timer_irq#
1 | static volatile int g_ts_timer_enable = 0; |
来分析一下这个程序的过程:
1 | 1. 在touchscreen_init的时候我们先注册了一个timer,然后修改了定时器的产生中断的时间间隔为10ms中断一次,所以touchscreen_timer_irq会每间隔10ms调用一次。没有按下,则touchscreen_timer_irq虽然也有走,但是就直接return. |