分类: LINUX
2012-03-14 14:36:10
A/D转换,又称为模数转换,是将模拟信号转换为计算机能够处理的数字信号。s3c2440集成了8通道10位CMOS A/D转换器。
对于s3c2440来说,实现A/D转换比较简单,主要应用的是ADC控制寄存器ADCCON和ADC转换数据寄存器ADCDAT0。寄存器ADCDAT0的低10位用于存储A/D转换后的数据。寄存器ADCCON的第15位用于标识A/D转换是否结束。第14位用于使能是否进行预分频,而第6位到第13位则存储的是预分频数值,因为A/D转换的速度不能太快,所以要通过预分频处理才可以得到正确的A/D转换速度,如我们想要得到A/D转换频率为1MHz,则预分频的值应为49。第3位到第5位表示的是A/D转换的通道选择。第2位可以实现A/D转换的待机模式。第1位用于是否通过读取操作来使能A/D转换的开始。第0位则是在第1位被清零的情况下用于开启A/D转换。
下面我们就给出一个A/D转换的实例。我们在AIN2引脚上接了一个温度传感器,被检测的温度范围为0度~99度,它对应于A/D转换数据的0~0x3FF。检测到的温度被实时地显示在LCD上,这里我们只显示温度的整数部分。由于要实时显示数据,因此就涉及到LCD刷新的问题,如果处理得不好,会出现LCD闪烁的现象。所以在程序中,我们只对要刷新的区域进行刷新处理,而对其他区域不更新,这样就有效地防止了LCD的闪烁。
S3C2410 芯片自带一个 8 通道输入10bit CMOS 模数转换器。在 2.5MHz 的转换器时钟下,可以达到500KSPS ,ADC具有采样保持、低功耗等功能。
特点:
z 精度(Resolution):10-bit
z 微分线性误差(Differential Linearity Error ):± 1.5 LSB
z 积分线性误差(Integral Linearity Error )± 2.0 LSB :
z 最大转换速率(Maximum Conversion Rate)500 KSPS
z 输入电压(Input voltage range ):0-3.3V
z 片上采样保持电路
z 正常模式
z 单独 X,Y 坐标转换模式
z 自动 X,Y 坐标顺序转换模式
z 等待中断模式
A/D转换的转换时间计算
例如PCLK为50MHz,PRESCALER=49;所有10位转换时间为:
50 MHz / (49+1) =1MHz
转换时间为1/(1M/5 cycles)=5us,
注意,A/D转换器的最大工作时钟为2.5MHz,所以最大的采样率可以达到500ksps
//重新改写刷屏的程序,确定了要刷屏的区域范围
void Brush_Background( int x,int y,int width,int height,U32 c)
{
int i,j ;
for( j = y ; j
{
for( i = x ; i < width ; i++ )
{
LCD_BUFFER[j][i] = c ;
}
}
}
int readADC(void)
{
rADCCON = (1<<14)|(49<<6)|(2<<3); //设置预分频器和A/D通道
rADCCON|=0x1; //启动A/D转换
while(rADCCON & 0x1)
; //确认A/D转换是否开始
while(!(rADCCON & 0x8000))
; //等待A/D转换的结束
return ( (int)rADCDAT0 & 0x3ff ); //读取A/D转换的数据
}
void Main(void)
{
int temperature,oldTemp;
unsigned char value[2],oldValue;
LCD_Init();
rLCDCON1|=1;
Brush_Background(0,0,LCD_WIDTH,LCD_HEIGHT,0xFFFFFF); //刷写这个LCD
Draw_Text48(16,96,0xff0000,wen) ;
Draw_Text48(64,96,0xff0000,du) ;
Draw_Text48(112,96,0xff0000,colon) ;
Draw_Text48(208,96,0xff0000,degree) ;
oldTemp=0;
oldValue=0;
while(1)
{
temperature = (int)readADC()*99/0x3ff; //读取温度
if(oldTemp != temperature) //判断温度是否有变化
{
oldTemp = temperature;
value[0] = (unsigned char)temperature/10; //温度的十位数据
value[1] = (unsigned char)temperature%10; //温度的个位数据
if(oldValue!=value[0]) //十位数据没有变化就不需要更新
{
oldValue=value[0];
Brush_Background(150,96,150+24,96+48,0xFFFFFF); //刷新十位数据的区域
Draw_ASCII(150,96,0xff0000,digital+144*value[0]); //显示十位数据
}
Brush_Background(174,96,174+24,96+48,0xFFFFFF); //刷新个位数据区域
Draw_ASCII(174,96,0xff0000,digital+144*value[1]); //显示个位数据
}
delay(1000000);
}
}
通过adc实现的触摸屏
触摸屏是当今最流行的一种人机交互接口,它被广泛地应用于手机等消费类电子产品中,目前这种技术有向PC机方向发展的趋势。基于原理的不同,触摸屏可以分为电阻式、电容式、表面声波式等。电阻式是应用较广的一种触摸屏,它的原理是通过测量横向和纵向的电阻值来获得触点的坐标。
s3c2440集成了4线制电阻式的触摸屏接口,触点坐标的检测是通过A/D转换来实现的。s3c2440一共有4种触摸屏接口模式,其中,自动(连续)XY坐标转换模式和等待中断模式应用地比较常见。等待中断模式是在触笔落下时产生一个中断,在这种模式下,A/D触摸屏控制寄存器ADCTSC的值应为0xD3,在系统响应中断后,XY坐标的测量模式必须为无操作模式,即寄存器ADCTSC的低两位必须清零。自动(连续)XY坐标转换模式是系统依次转换触点的X轴坐标和Y轴坐标,其中X轴坐标值写入寄存器ADCDAT0的低10位中,Y轴坐标写入寄存器ADCDAT1的低10位中,在这种模式下,系统同样会产生中断信号。在一般情况下,为实现触摸屏功能,先是设置为等待中断模式,在产生中断后,再设置为自动(连续)XY坐标转换模式,依次读取触点的坐标值。在实现触摸屏功能的过程中,除了上面介绍的几个寄存器外,还会用到以下寄存器。寄存器ADCTSC的第8位能够实现是触笔落下中断还是触笔抬起中断,如果写过基于视窗应用程序的人对这一点会很熟悉,它就好像单击鼠标操作一样,一次单击操作包括两个动作:按下和释放,这两个动作可以完成不同的命令。寄存器ADCTSC的第3位可以选择上拉电阻的使能,在等待中断模式下,上拉电阻要有效,在触发中断后,上拉电阻要无效。寄存器ADCTSC的第2位用于选择自动(连续)XY坐标转换模式。触笔抬起/落下中断状态寄存器ADCUPDN的低2位能够判断触笔在何种状态下引起的中断。A/D延时寄存器ADCDLY可以设置开始中断到真正开始A/D转换这段时间的延时长度,它的时钟源频率为3.68MHz。
在开始实现触摸屏功能之前,还需要解决一个问题,那就是触摸屏的校正。触摸屏和LCD是两种不同的物理器件。对于一个分辨率为320×240的LCD,它的宽度为320个像素,高度为240个像素。而触摸屏处理的数据是点的物理坐标,该坐标是通过触摸屏控制器采集得到的。要想实现触摸屏上的物理坐标与LCD上的像素点坐标一一对应上,两者之间就需要一定的转换,即校正。而且电阻式触摸屏由于自身的原因参数会发生变化,因此需要经常性的校正。比较常见的校正方法是三点校正法,它的原理是:
设LCD上每个点PD的坐标为[XD,YD],触摸屏上每个点PT的坐标为[XT,YT]。要实现触摸屏上的坐标转换为LCD上的坐标,需要下列公式进行转换:
XD=A×XT+B×YT+C
YD=D×XT+E×YT+F
因为其中一共有六个参数(A,B,C,D,E,F),因此只需要三个取样点就可以求得这六个参数。这六个参数一旦确定下来,只要给出任意触摸屏上的坐标点PT,代入这个公式,就可以得到它所对应的LCD上像素点的坐标PD。具体的求解过程就不细讲,只给出最终的结果。已知LCD上的三个取样点为:PD0,PD1,PD2,它们所对应的触摸屏上的三个点为:PT0,PT1,PT2。A,B,C,D,E,F这六个参数最终的结果都是一个分式,而且都有一个共同的分母,为:
K=(XT0-XT2)×(YT1-YT2)-(XT1-XT2)×(YT0-YT2)
那么这六个参数分别为:
A=[(XD0-XD2)×(YT1-YT2)-(XD1-XD2)×(YT0-YT2)] / K
B=[(XT0-XT2)×(XD1-XD2)-(XD0-XD2)×(XT1-XT2)] / K
C=[YT0×(XT2×XD1-XT1×XD2)+YT1×(XT0×XD2-XT2×XD0)+YT2×(XT1×XD0-XT0×XD1)] / K
D=[(YD0-YD2)×(YT1-YT2)-(YD1-YD2)×(YT0-YT2)] / K
E=[(XT0-XT2)×(YD1-YD2)-(YD0-YD2)×(XT1-XT2)] / K
F=[YT0×(XT2×YD1-XT1×YD2)+YT1×(XT0×YD2-XT2×YD0)+YT2×(XT1×YD0-XT0×YD1)] / K
下面的程序是实现触摸屏功能的简单实例——以触点为中心,绘制出一个红色的边长为10个像素的正方形。触点的坐标是用下面方法得到的:当触笔落下时,进入中断,然后读取触点处的坐标,直到触笔的抬起,才退出该次中断。由于触摸屏需要校正,因此在使用之前需要进行校正处理。但并不是每次使用都要校正,只要坐标没有发生漂移,就不需要再次校正。所以在进行一次校正后,只要把那几个参数保存起来,下次需要时直接使用上次保存下来的参数即可。在这里,我们利用EEPROM来保存这几个参数,即A,B,C,D,E,F,K分别保存在以0x20,0x30,0x40,0x50,0x60,0x70,0x80为首地址内存的连续4个字节空间内,另外内存地址0x1F保存一个标识信息,当为0x6A时,表示这几个参数已计算并保存好了,只需从上述内存地址中读取参数就行,而当为其他值时,就需要进行校正。校正时,需要三个取样点,在这里我们选取LCD上的(32,24),(160,216),(288,120)为这三个取样点,我们在这三个取样点上画一个十字(如下图所示),只需要依次点击这三个点,即可完成触摸屏的校正。
…… ……
volatile U32 LCD_BUFFER[LCD_HEIGHT][LCD_WIDTH];
unsigned char iic_buffer[8];
unsigned char devAddr=0xa0;
int A,B,C,D,E,F,K;
volatile int xdata, ydata;
int flagIIC; //IIC标志
int flagTS; //触摸屏标志
…… ……
//触摸屏中断
void __irq ADCTs(void)
{
rADCTSC = (1<<3)|(1<<2); //上拉电阻无效,自动连续XY坐标转换模式开启
rADCDLY = 40000; //延时
rADCCON|=0x1; //开始A/D转换
while(rADCCON & 0x1)
; //检查A/D转换是否开始
while(!(rADCCON & 0x8000))
; //等待A/D转换的结束
while(!(rSRCPND & ((U32)0x1<<31)))
; //判断A/D中断的悬挂位
xdata=(rADCDAT0&0x3ff); //读取X轴坐标
ydata=(rADCDAT1&0x3ff); //读取Y轴坐标
flagTS = 1; //置标志
rSUBSRCPND|=0x1<<9;
rSRCPND = 0x1<<31;
rINTPND = 0x1<<31;
rINTSUBMSK=~(0x1<<9);
rINTMSK=~(0x1<<31); //清A/D中断,开启A/D中断屏蔽
rADCTSC =0xd3; //再次设置等待中断模式,这一次是判断触笔的抬起
rADCTSC=rADCTSC|(1<<8); //设置触笔抬起中断
while(1) //等待触笔的抬起
{
if(rSUBSRCPND & (0x1<<9)) //检查A/D触摸屏中断悬挂
{
break; //如果触笔抬起,则跳出该循环
}
}
rADCDLY=50000;
rSUBSRCPND|=0x1<<9;
rINTSUBMSK=~(0x1<<9);
rSRCPND = 0x1<<31;
rINTPND = 0x1<<31; //再次清A/D中断,开启A/D中断屏蔽
rADCTSC =0xd3; //设置等待中断模式,为下一次触笔的落下做准备
}
//绘制“十”字型
void drawCross(U32 x,U32 y,U32 color)
{
int i;
for(i=x-10;i
PutPixel(i,y, color);
for(i=y-10;i
PutPixel(x,i, color);
}
//触摸屏校正
void TSCal(void)
{
int i=0;
int xt[3],yt[3];
Brush_Background(0,0,LCD_WIDTH,LCD_HEIGHT,0xFFFFFF);
drawCross(32,24,0xFF0000);
Draw_ASCII(36,28,0xFF0000,one);
drawCross(160,216,0xFF0000);
Draw_ASCII(164,220,0xFF0000,two);
drawCross(288,120,0xFF0000);
Draw_ASCII(292,124,0xFF0000,three);
//依次读取三个采样点的坐标值
for(i=0;i<3;i++)
{
while(flagTS==0)
delay(500);
xt[i]=xdata;
yt[i]=ydata;
flagTS=0;
}
//计算参数
K=(xt[0]-xt[2])*(yt[1]-yt[2])-(xt[1]-xt[2])*(yt[0]-yt[2]);
A=(32-288)*(yt[1]-yt[2])-(160-288)*(yt[0]-yt[2]);
B=(xt[0]-xt[2])*(160-288)-(32-288)*(xt[1]-xt[2]);
C=yt[0]*(xt[2]*160-xt[1]*288)+yt[1]*(xt[0]*288-xt[2]*32)+yt[2]*(xt[1]*32-xt[0]*160);
D=(24-120)*(yt[1]-yt[2])-(216-120)*(yt[0]-yt[2]);
E=(xt[0]-xt[2])*(216-120)-(24-120)*(xt[1]-xt[2]);
F=yt[0]*(xt[2]*216-xt[1]*120)+yt[1]*(xt[0]*120-xt[2]*24)+yt[2]*(xt[1]*24-xt[0]*216);
}
//把一个32位整型转换为4个8位字节型,并写入EEPROM中
void wrTStoIIC(int coef,unsigned char address)
{
iic_buffer[0]=(unsigned char)((coef&0xFF000000)>>24);
iic_buffer[1]=(unsigned char)((coef&0x00FF0000)>>16);
iic_buffer[2]=(unsigned char)((coef&0x0000FF00)>>8);
iic_buffer[3]=(unsigned char)(coef&0x000000FF);
wr24c02a(address,iic_buffer,4);
}
//读取EEPROM中的4个8位字节,并把它们组合成一个32位的整型。
int rdTStoIIC(unsigned char address)
{
int temp;
rd24c02a(address,iic_buffer,4);
temp=(iic_buffer[0]<<24)|(iic_buffer[1]<<16)|(iic_buffer[2]<<8)|(iic_buffer[3]);
return temp;
}
void Main(void)
{
LCD_Init();
rLCDCON1|=1;
rADCDLY=50000; //设置延时
rADCCON=(1<<14)+(9<<6); //设置A/D预分频
rADCTSC=0xd3; //设置触摸屏为等待中断模式。
pISR_ADC = (U32)ADCTs;
…… ……
flagTS = 0;
flagIIC = 1;
//读取EEPROM中的标志地址内容,用于判断触摸屏校正的参数是否已计算并保存好
rd24c02a(0x1F,iic_buffer,1);
if(iic_buffer[0]!=0x6A) //如果触摸屏的校正参数没有计算并保存,重新校正
{
TSCal();
rINTMSK=~((0x1<<31)|(0x1<<27)); //开启IIC中断屏蔽
iic_buffer[0]=0x6A;
wr24c02a(0x1F,iic_buffer,1); //置“触摸屏校正参数计算并保持好”的标志信息
delay(3000); //等待一段时间,一定要有,否则EEPROM不能正确读写
wrTStoIIC(A,0x20);
delay(3000);
wrTStoIIC(B,0x30);
delay(3000);
wrTStoIIC(C,0x40);
delay(3000);
wrTStoIIC(D,0x50);
delay(3000);
wrTStoIIC(E,0x60);
delay(3000);
wrTStoIIC(F,0x70);
delay(3000);
wrTStoIIC(K,0x80);
}
else //如果触摸屏校正参数已准备好,则直接读取
{
A=rdTStoIIC(0x20);
delay(3000);
B=rdTStoIIC(0x30);
delay(3000);
C=rdTStoIIC(0x40);
delay(3000);
D=rdTStoIIC(0x50);
delay(3000);
E=rdTStoIIC(0x60);
delay(3000);
F=rdTStoIIC(0x70);
delay(3000);
K=rdTStoIIC(0x80);
}
Brush_Background(0,0,LCD_WIDTH,LCD_HEIGHT,0xFFFFFF);
while(1)
{
if(flagTS)
{
flagTS=0;
xLcd = (A*xdata+B*ydata+C)/K; //计算X轴坐标
yLcd = (D*xdata+E*ydata+F)/K; //计算Y轴坐标
Brush_Background(xLcd-5,yLcd-5,xLcd+5,yLcd+5,0xFF0000); //绘制正方形
}
delay(1000000);
}
}
本文介绍的程序要略显复杂一些,这里对触摸屏校正和中断再做一总结:
1、一般地,在第一次使用触摸屏时,需要校正一次,以后可以不再校正,除非发生了明显的漂移。我们是把校正参数存储在EEPROM中,下次再使用时,只需读取该组数据即可。EEPROM中的0x1F地址用于存储校正参数标识信息,在已经校正过的情况下,该位内容为0x6A,这样在下次开机使用触摸屏时,只要读取该位内容,就可知道触摸屏是否已校正,没有校正则需要进行校正,已经校正过了则无需再重复校正了。
2、先设置等待中断模式,以等待触摸屏中断的发生,当中断发生并进入中断以后,再转换为自动连续XY坐标转换模式,可正确读取触点的坐标值。在中断程序中,当触笔落下时进入中断,当触笔抬起时退出中断,程序只记录触笔落下时触点的坐标值。
3.、触摸屏的流程
触摸屏的流程我觉得是整个触摸屏比较重要的地方,其他的设置好寄存器之后读数据就可以了。流程中比较重要的是俩个中断,触摸屏中断和ADC中断,其中触摸屏中断包括触摸屏按下中断和触摸屏松开中断。
初始化中断和寄存器——>等待触摸屏中断模式——>TC 中断(触摸屏按下中断)——>(进入xy自动转换模式)——>(ADC转换完成后)ADC中断——>(松开触摸屏)——>TC中断(松开触摸屏)——>等待触摸屏中断模式(循环)