对于触摸屏首先说说他们的工作原理:
1 电阻屏
当手指触摸屏幕时,两个相互绝缘的导电层在触摸点处连接,顶层的5伏电压就会加到底层触摸点处,底层该点的电压会发生改变。控制器检测到该点的
变化后,将该点的电压进行A/D转换,得到的值与5伏相比,再乘以该轴总长度即可得触摸点靠地那一端的坐标。
2 电容屏:
给工作面通上一个很低的电压,当用户触摸屏幕时, 手指头吸收走一个很小的电流, 这个电流分从触摸屏四个角上的电极中流出, 并且理论上流经这四个电极的电流与手指到四角的距离成比例, 控制器通过对这四个电流比例的精密计算, 得出触摸点的位置。
接下来一电阻屏为例做一个裸机驱动程序:
此程序可分三部分: 触摸屏初始化,中断处理和进入中断;
一: 触摸屏初始化:ts_init()
对于触摸屏的驱动设计其实就是ADC和中断;所以初始化的因为就是ADC和中
1 配置ADCCON;主要是使能ADC及计算AD转换时钟 公式:AD=pclk/(PRSCVL+1) 其中 AD自己定义,一般不能大于2MHZ,pclk 是时钟频率,PRSCVL 需要配置的;我的配置是:ADCCON = (0<<16)|(1<<14)|(49<<6)|(0<<2)|(0<<1)|(0<<0);//
2 开启使能中断,有两个 1 开启ADC中断,2 开启触摸屏下/上中断 寄存器分别是 VIC1IRQSTATUS = (1<<31);/// ADCUPDN = (1 << 0);///
3 一切准备就绪 开始等待中断 //注;在此驱动中只要是需要等待中断的就要使用语句 ADCTSC = 0xd3; 来进入等待中断模式;
二:中断处理:irq_adc()
1 对于触摸屏两种获取x y 坐标点的方法 一是手动获取,二是自动获取 我再次用的是自定获取x y坐标,并开启ADC ; 寄存器设置是: ADCTSC = (1<<2); ADCCON |= (1<<0);
2 既然用了自动转换,又已经开启了ADC,现在要做的就是等待ADC完成,判断方法是:while (!(ADCCON & (1<<15)));
3 当转换完成之后,它会自动的将x y坐标的值分别放在ADCDAT0[9:0]和ADCDAT1[9:0],如果我们想用的话就要在这两个寄存器中取值;
4 到这里中断处理已经完成了,所以我们要清除中断,以便于下次中断,有两个需要清除 1 清除ADC中断,2 清除按下中断 寄存器设置是: ADCCLRINT = 0; ADCCLRINTPNDNUP = (1 << 0);
5 按下去之后还没有弹起,所以要等待弹起中断; 寄存器设置时: ADCTSC = 0xd3; ADCTSC |= (1<<8);
6 当弹起中断产生之后,在这一阶段的中断圆满结束,但还要做的就是清除弹起中断 寄存器设置是:
ADCCLRINTPNDNUP = (1 << 0);
VIC0ADDRESS = 0; //
VIC1ADDRESS = 0; //
ADCCLRINT = 0;
ADCUPDN = 0x0;
7 接下来就是等待进入下次中断; ADCTSC = 0xd3;
三: 进入中断;
对于进入中断有两种方法,一是 在ts.c 中直接写一个函数来将中断处理的函数地址给了相应寄存器,但好像有问题 他只能进一次中断 代码是:
void ts_irq_init()
{
VIC1VECTADDR30 = irq_adc;
VIC1INTENABLE |=(1<<30);//|(1<<31);//使能INT_ADC ,INT_PENDNUP
VIC1INTSELECT = VIC1INTSELECT&(~(1<<30));//
}
二是在我以前写得中断程序interrupt.c中将其添加进去,没错误 代码如下:
ts_irq()
{
//保存环境
__asm__(
"sub lr,lr,#4\n"
"stmfd sp!, {r0-r12,lr}\n"
:
:
);
irq_adc();
__asm__(
"ldmfd sp!,{r0-r12,pc}^\n"
:
:
);
}
VIC1VECTADDR30 = ts_irq; //保存触摸屏处理地址
VIC1INTENABLE |=(1<<30);//|(1<<31);//使能INT_ADC ,INT_PENDNUP
具体代码如下:
#define ADCCON *((volatile signed long*)0x7E00B000)
#define ADCDLY *((volatile signed long*)0x7E00B008)
#define ADCTSC *((volatile signed long*)0x7E00B004)
#define ADCDAT0 *((volatile signed long*)0x7E00B00C)
#define ADCDAT1 *((volatile signed long*)0x7E00B010)
#define ADCUPDN *((volatile signed long*)0x7E00B014)
#define ADCCLRINTPNDNUP *((volatile signed long*)0x7E00B020)
#define VIC1VECTADDR30 *((volatile signed long*)0x71300178)
#define VIC1VECTADDR31 *((volatile signed long*)0x7130017c)
#define ADCCLRINT *((volatile signed long*)0x7E00B018)
#define VIC1INTENABLE *((volatile signed long*)0x71300010)
#define VIC1INTSELECT *((volatile signed long*)0x7130000C)
#define VIC1ADDRESS *((volatile signed long*)0x71300f00)
#define VIC0ADDRESS *((volatile signed long*)0x71200f00)
#define VIC1IRQSTATUS (*(volatile unsigned *)0x71300000) //IRQ Status Register (VIC1)
int x,y;
void irq_adc()
{
//1. 启动xy坐标自动转换
ADCTSC = (1<<2);
ADCCON |= (1<<0);
// 2 等待转换完成
while (!(ADCCON & (1<<15)));
//3 获取坐标
x = ADCDAT0 & 0x3ff;
y = ADCDAT1 & 0x3ff;
//4清除中断
ADCCLRINTPNDNUP = (1 << 0);
//5. 进入等待弹起中断
ADCTSC = 0xd3;
ADCTSC |= (1<<8);
//6.清除弹起中断
ADCCLRINTPNDNUP = (1 << 0);
VIC0ADDRESS = 0; //
VIC1ADDRESS = 0; //
ADCCLRINT = 0;
ADCUPDN = 0x0;
printf("x is %d,y is %d\n",x,y);
//7. 再次进入等待按下中断的状态
ADCTSC = 0xd3;
void ts_irq_init()
{
VIC1VECTADDR30 = irq_adc;
VIC1INTENABLE |=(1<<30);//|(1<<31);//使能INT_ADC ,INT_PENDNUP
VIC1INTSELECT = VIC1INTSELECT&(~(1<<30));//
}
void ts_init()
{
//1 设置AD转换时钟
ADCCON = (0<<16)|(1<<14)|(49<<6)|(0<<2)|(0<<1)|(0<<0);//
//2 开启中断使能位(2个)
VIC1IRQSTATUS = (1<<31);///
ADCUPDN = (1 << 0);///
//3 进入等待中断模式!
ADCTSC = 0xd3;
}