ADC和触摸屏
一、ADC和触摸屏介绍
这节内容我们来介绍S3c2440上边的ADC和触摸屏,ADC,Analog-to-Digital Converter的缩写,是一种把模拟信号转换为数字信号的转换器,比如我们要测量接有一个变阻器的电路的电压,2可以通过ADC进行转换,把电路的模拟信号转换为方便我们阅读的数字信号,
S3c2440上的ADC是一个10位的转换器,可以接收8路的模拟输入信号,触摸屏的模拟电压信号也是接到了ADC转换器上面
从上边的图可以看出来,触摸屏总共有4路模拟信号,分别是XP、XM、YP、YM、通过ADC转换器和触摸屏相连,触摸屏和ADC都有中断机制,中断信号接到了中断控制器上边,输出两路中断信号INT_ADC和INT_TC,它们之间的框架图并不复杂,那接下来就要了解一些触摸屏是怎么通过ADC测试触摸坐标的,下边的内容是我从《入式Linux应用完全开发手册》中截取出来的,想要了解更加详细的触摸屏原理的话需要自己去搜索相关资料
触摸屏的等效电路如下所示,图中粗黑线表示相互绝缘的两层导电层,当按压时,它们在触点处相连,不同的触点在x和y方向上的分压不一样,将这两个电压值经过转换,就可以得到x y的坐标
s4 s5闭合,s1 s2 s3断开,YM接地,XP上拉,XP和XP作为模拟输入,平时触摸屏没有被按下的时候,由于上拉电阻的关系,Y_ADC为高电平,当x轴和x轴收到挤压而接触导通时,Y_ADC的电压由于接到了y轴接地而变成了低电平,此低电平可以中断信号来通知CPU发生了按压事件,这在s3c2440中被称为等待中断模式。当采集坐标时,类似于如下的等效电路
s1 s2闭合,s3 s4 s4断开,即XP接到电源,XM接地,YP作为模拟输入,YM高阻,XP禁止上拉,这时,YP即X_ADC就是x轴的分压点,进行AD转换后就是x轴的坐标,同样的采集y点的坐标和x的坐标类似,等效电路如下所示,在这里就不再多讲了
二、编写程序
在写程序之前,先来梳理一下程序的逻辑和流程
使用触摸屏的流程
1、按下触摸屏,发生触摸中断,启动ADC转换
2、启动ADC之后,获取x y坐标
3、ADC转换完成之后,产生中断,在ADC中断里读取x y 坐标
后续进行改进
读取一次x y坐标之后,打开一个定时器,再去判断触摸屏是否被按下,如果按下再去启动ADC读取数
ADC和触摸屏的寄存器配置和中断部分
/* ADC的寄存器配置 */
void adc_ts_reg_init()
{
/*
[15] ECFLG 0 正在转换 1 转换结束
[14] PRSCEN 预分频 1启用
[13:6] PRSCVL 预分频系数
[5:3] SEL_MUX 选择要读取的信号 000 = AIN 0
*/
ADCCON = (1 << 14) | (49 << 6) | (0 << 3);
/* 按下触摸屏, 延时一会再发出TC中断
* 延时时间 = ADCDLY * 晶振周期 = ADCDLY * 1 / 12000000 = 5ms
*/
ADCDLY = 60000;
}
/* 打开ADC和触摸屏的中断 */
void adc_ts_int_init()
{
/* 先清一次中断 */
SUBSRCPND = (1 << 9) | (1 << 10);
/* 注册中断处理函数 */
register_irq(31, AdcTsIntHandle);
/* 打开第9、10位的ADC和触摸屏的中断 */
INTSUBMSK &= ~((1<<9) | (1<<10));
}
/* 中断处理函数 */
void AdcTsIntHandle(int irq)
{
if (SUBSRCPND & (1<<9)) /* 如果是触摸屏中断 */
Isr_Tc();
if (SUBSRCPND & (1<<10)) /* ADC中断 */
Isr_Adc();
SUBSRCPND = (1<<9) | (1<<10);
}
/* 触摸屏中断 */
void Isr_Tc(void)
{
/* ADCDAT0寄存器的第15位表示现在是按下还是松开的 */
if (ADCDAT0 & (1<<15))
{
//printf("pen up\n\r");
/* 如果已经松开了,则期待下一次的按下操作 */
enter_wait_pen_down_mode();
}
else
{
//printf("pen down\n\r");
/* 按下之后需要开启自动测量模式,然后开启ADC */
ADCTSC |= (1 << 2) | (0 << 0);
ADCCON |= (1 << 0);
}
}
/* ADC中断 */
void Isr_Adc()
{
int x = ADCDAT0;
int y = ADCDAT1;
/* 如果仍然是按下的,则打印 */
if (!((x << 15) & 0x1)) {
printf("\n\rx = %d y = %d",x & 0x3ff,y & 0x3ff);
}
/* 等待松开时发生的中断 */
enter_wait_pen_up_mode();
}
/* 等待按下中断 */
void enter_wait_pen_down_mode(void)
{
/*
[8] UD_SEN 0期待出现按下的中断信号 1抬起
[7] YM_SEN 0关闭YM开关 1关闭
[6] YP_SEN 0打开YP开关 1打开
[5] XM_SEN 0关闭XM开关 1关闭
[4] XP_SEN 0打开XP开关 1打开
[3] PULL_UP 0打开上拉 1打开
[1:0] XY_PST 11等待中断模式
*/
/* YM关闭,YP打开 XM打开 XP打开 关闭上拉,现在的状态
就是上边的等待中断的那张图的状态,通过第8位选择期待
发生的中断时按下还是抬起
*/
ADCTSC = (0 << 8) | (1 << 7) | (1 << 6) | (0 << 5) | (1 << 4) | (0 << 3) | (3 << 0);
}
/* 等待抬起中断 */
void enter_wait_pen_up_mode(void)
{
ADCTSC = (1 << 8) | (1 << 7) | (1 << 6) | (0 << 5) | (1 << 4) | (0 << 3) | (3 << 0);
}
测试
void adc_ts_test()
{
touchscreen_init();
}
void touchscreen_init(void)
{
/* 设置触摸屏接口:寄存器 */
adc_ts_reg_init();
/* 设置中断 */
adc_ts_int_init();
/* 让触摸屏控制器进入"等待中断模式" */
enter_wait_pen_down_mode();
}
梳理
1、在测试函数里调用触摸屏和AD的初始化函数,进入等待中断模式
2、发生中断之后,通过判断SUBSRCPND的第9位和第10位来区分中断,
如果发生的是触摸屏的中断就进入处理函数Isr_Tc中
3、在处理函数中通过ADCDAT0的第15位来判断函数触摸屏是按下还是松开
如果已经松开了,则期待下一次的按下操作,如果还是在按下状态,则启动
ADC进行测量,打开ADC的自动测量模式,启动ADC
4、ADC测量完成进入中断函数,判断中断类型,进入Isr_Adc
5、在ADC的中断处理函数中,再一次判断,如果还是按下的话,就读取xy坐标
保存在ADCDAT0和ADCDAT1寄存器里,打印信息,期待触摸屏松开操作。
好了,到这里触摸屏的代码功能已经写完了,并且经过实际的测量,是可以检测到触摸屏的按下操作,打印出在屏幕上按下的位置,
但是不足的是,此时的触摸屏代码不支持长按操作,为了支持触摸屏的长按,我们要使用定时器来进行检测
三、改进
开始之前,先来梳理一下使用定时器的代码逻辑
1、按下屏幕,出发触摸屏中断,在中断函数里启动ADC进行测量
2、ADC转换完毕后引发ADC中断,在ADC中断函数里判断屏幕是否还是
处于按下的操作,如果还是被按下的话,启动定时器
3、在定时器处理函数中进行判断,如果屏幕已经被松开了的话
就退出,期待下一次的按下操作
4、在定时器函数里判断如果还是被按下的话,就再一次的启动ADC转换
5、进入上面第2步,循环
代码
void touchscreen_init(void)
{
/* 省去和上面一样的代码 */
/* 注册用于触摸屏的定时器中断函数 */
time_func_register("touchscreen",ts_timer_func);
/* 省去和上面一样的代码 */
}
/* ADC的处理函数重写 */
void Isr_Adc()
{
int x = ADCDAT0;
int y = ADCDAT1;
/* 如果还是按下的 */
if (!((x << 15) & 0x1)) {
printf("\n\rx = %d y = %d",x & 0x3ff,y & 0x3ff);
/* 开启定时器,定义的一个标记 */
ts_status = ENABLE;
} else { //如果已经松开了
ts_status = DISABLE; //标记位被禁止
enter_wait_pen_down_mode();//期待下一次的按下操作
}
/* 定时器处理函数 */
void ts_timer_func()
{
/* 定时器函数是一直会进入的,在ADC中断处理函数中判断,如果
已经松开了就不在使能,这里直接退出, */
if (ts_status == DISABLE)
return;
/* 进入到这里,说明已经进行了一次ADC转化,并且在ADC函数里判断还是被按下的
标记位被置位,进入定时器,开始下一次的数据读取
*/
/* 如果已经松开 */
if (ADCDAT0 & (1<<15)) /* 如果松开 */
{
ts_status == DISABLE;//标记位禁止
enter_wait_pen_down_mode();//期待下一次的按下
return;
} else { //如果还是在按下的话
/* 如果是还在按下,进入自动测量模式,开启ADC */
ADCTSC |= (1 << 2) | (0 << 0);
ADCCON |= (1 << 0);
}
}
好了,在上面的程序里添加了使用定时器的程序,已经测试过了,当你长按在屏幕上的话,是一直会打印出触点的坐标。
上一篇: 1005:地球人口承载力估计
下一篇: 1565:营业额统计(平衡树)