S3C2440芯片内部共有8路A/D转换通道,AIN0-AIN7,转换器只有一个,转换精度为10位,最大转换率为2.5MHz
A/D
转换器时钟下的500
KSPS。A/D
转换器支持片上采样-保持功能和掉电模式的操作。在常见的设计中,一般AIN4,AIN5,AIN6,AIN7被用作四线电阻触摸屏的YM、YP、XM、XP通道,剩余的AIN0~AIN3被引出,其中AI0外接一个可调电阻。
ADC的配置流程如下:
1、ADCDLY(P446)
rADCDLY=50000; //Normal conversion mode delay about ( 1/3.6864M)*50000=13.56ms
2、ADCCON(P444)的设置,选择转换通道和设置转换频率
ADCCON[0],AD转换开始有效,1转换开始且该位在转换开始后变为0,通过这个特点可以判断是否开始转换。什么意思呢,设为1只是认为地让它转换开始,但是未必开始转换,还必须通过while循环查询方式判断该位是否变为0,变为0表示转换开始。
ADCCON[1],AD转换通过读取有效,1通过读取操作有效;ADCCON[2],备用操作模式选择,0普通操作模式,1,备用操作模式;
ADCCON[5:3]:转换通道选择;
000 = AIN 0
001 = AIN 1
010 = AIN 2
011 = AIN 3
100 = YM
101 = YP
110 = XM
111 = XP
ADCCON[13:6],AD转换器预分频器值0~255,ADC频率应该小于PCLK的1/5;
ADCCON[14],AD转换器预分频器使能,1使能;
ADCCON[15],AD转换结束标志,0:转化过程中,1:转换结束;
ADC初始化程序示例如下:
#define ADC_FREQ 2500000 //希望的ADC转换频率
volatile U32 preScaler;
volatile U32 adc_value=0;//在程序开始处声明
void adc_init(void)
{
//选择输入通道,AIN0,对应开发板上W1可调电阻
int channel=0;
preScaler = ADC_FREQ;
preScaler = 50000000/ADC_FREQ - 1; //PCLK=50M
rADCCON = (1<<14)|(preScaler<<6)|(channel<<3); //setup channel
delay(1000);
}
3、ADCDAT0(ADCDAT1)P447,读取转换值
ADCDAT0[9:0],X坐标转换结果值,包括普通模ADC转换结果值;
(ADCDAT1[9:0],Y坐标转换结果;)
ADC转换程序示例,通过轮询方式
void Main(void)
{
Set_Clk();
adc_init();
while(1)
{
adc_value=ReadAdc(0);
delay(1000) ;
}
}
int ReadAdc(int channel)
{
rADCCON |= 0x01; //start ADC
while(rADCCON & 0x1); //check if Enable_start is low
while(!(rADCCON & 0x8000)); //check if EC(End of Conversion) flag is high
return ( (int)rADCDAT0 & 0x3ff );
}
ADC通过中断方式读取转换值
注意ADC的中断有两个子中断,INT_ADC_S和INT_TC 需要先处理一下子中断INT_ADC_S ,再处理INT_ADC。
void adc_init(void)
{
int channel=0; /选择输入通道,AIN0,对应开发板上W1可调电阻
preScaler = ADC_FREQ; //设置分频时钟
preScaler = 50000000/ADC_FREQ - 1; //PCLK=50M
rADCCON = (1<<14)|(preScaler<<6)|(channel<<3);
ClearSubPending(BIT_SUB_ADC);//清子中断处理寄存器 ClearPending(BIT_ADC); //清中断处理寄存器
pISR_ADC=(U32)adc_ISR;
EnableSubIrq(BIT_SUB_ADC); //开AD子中断
EnableIrq(BIT_ADC); //开AD中断
delay(1000);
}
void __irq adc_ISR(void)
{
int adc_value; //adc_value应该设为全局变量,这里放这里以便分析
ClearSubPending(BIT_SUB_ADC);//清子中断处理寄存器 ClearPending(BIT_ADC); //清中断处理寄存器
adc_value = (int)rADCDAT0 & 0x3ff;
}
触摸屏工作流程以及程序设计流程:
一、触摸屏初始化:
1、ADCTSC设置(P445),
[1:0]:11设置触摸屏接口为中断等待模式,等待触摸笔按下
[2]: 0 ADC普通转换模式,1自动连续测量X坐标和Y坐标
[3]: 0 XP上拉有效,1 XP上拉无效
2、ClearSubPending(BIT_SUB_ADC);//清子中断处理寄存器 ClearPending(BIT_ADC); //清中断处理寄存器
ClearSubPending(BIT_SUB_TC);
清除源挂起寄存器(SRCPND)、中断挂起寄存器(INTPND)、子源挂起寄存器(SUBSRCPND)。注意有两个中断,触摸屏中断:当触摸笔按下或抬起产生的中断,ADC中断:触摸屏坐标AD转换结束产生的中断。
3、EnableSubIrq(BIT_SUB_ADC); //开AD子中断
EnableIrq(BIT_ADC); //开AD中断
EnableSubIrq(BIT_SUB_TC); //开AD子中断TC
关中断屏蔽寄存器和子中断屏蔽寄存器(INTMSK,INTSUBMSK)。 4、pISR_ADC = (U32)AdcTsAuto;
程序入口函数,中断模式和中断优先级默认即可。
二、触摸屏中断服务子程序:
一)触摸笔按下中断
4、如果中断发生,设置x,y坐标为自动转换模式
rADCTSC = (1<<3)|(1<<2);
[2]: 0 ADC普通转换模式,1自动连续测量X坐标和Y坐标
[3]: 0 XP上拉有效,1 XP上拉无效
5、启动AD转换,然后检测AD转换是否启动
rADCCON|=0x1; //start ADC
while(rADCCON & 0x1); //check if Enable_start is low
6、检测AD转换是否结束,若结束,获取x,y坐标的值
通过轮询方式,也可以是中断方式判断转换结束。
while(!(rADCCON & 0x8000)); //check if EC(End of Conversion) flag is high, This line is necessary~!!
while(!(rSRCPND & 0x80000000)); //check if ADC is finished with interrupt bit
xdata=(rADCDAT0&0x3ff);
ydata=(rADCDAT1&0x3ff);
7、对几个寄存器写1清零,防止反复发生中断(这里的中断是笔尖按下中断)
ClearSubPending(BIT_SUB_TC);
ClearPending(BIT_ADC);
/rSRCPND = 0x80000000;rINTPND = 0x80000000; 也可以
8、再次允许中断
允许触摸笔被弹起的中断
EnableSubIrq(BIT_SUB_TC);
EnableIrq(BIT_ADC); // rINTMSK = 0x7fffffff;
二)触摸笔抬起中断
9、设置触摸屏即可为等待中断模式,等待触摸笔抬起(ADCTSC,关键是要设置触摸笔抬起中断信号)
rADCTSC =0xd3; //Waiting for interrupt
rADCTSC=rADCTSC|(1<<8); // Detect stylus up interrupt signal.
10、如果发生中断,不做任何操作,只打印出一句触摸笔抬起中断信息
while(1) //to check Pen-up state
{
if(rSUBSRCPND & (BIT_SUB_TC)) //check if ADC is finished with interrupt bit
{
Uart_Printf("Stylus Up Interrupt~!
");
break; //if Stylus is up(1) state
}
}
Uart_Printf("count=d XP=d, YP=d
", count++, xdata, ydata);
11、触摸笔抬起之后,把得到的x,y坐标值发送给PC机,显示出具体数值
三)再次设置触摸屏为等待中断模式,等待下次触摸屏被按下
rADCTSC =0xd3; //Waiting for interrupt
ClearSubPending(BIT_SUB_TC);
ClearPending(BIT_ADC);
EnableSubIrq(BIT_SUB_TC);
EnableIrq(BIT_ADC);
示例程序如下:
#define GLOBAL_CLK 1
#include
#include
#include "def.h"
#include "option.h"
#include "2440addr.h"
#include "2440lib.h"
#include "2440slib.h"
#include "mmu.h"
#include "profile.h"
#include "memtest.h"
#define ADC_FREQ 2500000
int count = 0;
volatile U32 preScaler;
int xdata,ydata;
void Test_Touchpanel(void);
static void __irq AdcTsAuto(void);
static void cal_cpu_bus_clk(void);
void Set_Clk(void);
void delay(int times)
{
int i,j;
for(i=0;i
for(j=0;j<400;j++);
}
int Main(void)
{
int Scom=0;
Set_Clk();
Uart_Init(0,115200);
Uart_Select(Scom);
Test_Touchpanel();
while(1);
return 0;
}
[page]
void Test_Touchpanel(void)
{
rADCDLY=50000; //Normal conversion mode delay about (1/3.6864M)*50000=13.56ms
preScaler = ADC_FREQ;
preScaler = 50000000/ADC_FREQ - 1; //PCLK=50M
rADCCON = (1<<14)|(preScaler<<6); //ADCPRS En,PRSCVL
rADCTSC=0xd3; //Wfait,XP_PU,XP_Dis,XM_Dis,YP_Dis,YM_En
rSRCPND = 0x80000000;
rINTPND = 0x80000000;
ClearSubPending(BIT_SUB_TC);
pISR_ADC = (U32)AdcTsAuto;
rINTMSK = 0x7fffffff;
EnableSubIrq(BIT_SUB_TC);
}
static void __irq AdcTsAuto(void)
{
U32 saveAdcdly;
if(rSUBSRCPND & (BIT_SUB_TC))
{
if( !(rADCDAT0&0x8000))
Uart_Printf("
Stylus down
");
else
Uart_Printf("
Stylus up
");
}
rADCTSC = (1<<3)|(1<<2);
saveAdcdly=rADCDLY;
rADCDLY=40000; //Normal conversion mode delay about (1/50M)*40000=0.8ms
rADCCON|=0x1; //start ADC
while(rADCCON & 0x1); //check if Enable_start is low
while(!(rADCCON & 0x8000)); //check if EC(End of Conversion) flag is high, This line is necessary~!!
while(!(rSRCPND & 0x80000000)); //check if ADC is finished with interrupt bit
xdata=(rADCDAT0&0x3ff);
ydata=(rADCDAT1&0x3ff);
ClearSubPending(BIT_SUB_TC);
//ClearPending(BIT_ADC);
rSRCPND = 0x80000000;
rINTPND = 0x80000000;
EnableSubIrq(BIT_SUB_TC);
//EnableIrq(BIT_ADC);
rINTMSK = 0x7fffffff;
rADCTSC =0xd3; //Waiting for interrupt
rADCTSC=rADCTSC|(1<<8); // Detect stylus up interrupt signal.
while(1) //to check Pen-up state
{
if(rSUBSRCPND & (BIT_SUB_TC)) //check if ADC is finished with interrupt bit
{
Uart_Printf("Stylus Up Interrupt~!
");
break; //if Stylus is up(1) state
}
}
Uart_Printf("count=d XP=d, YP=d
", count++, xdata, ydata);
rADCDLY=saveAdcdly;
rADCTSC =0xd3; //Waiting for interrupt
ClearSubPending(BIT_SUB_TC);
//ClearPending(BIT_ADC);
rSRCPND = 0x80000000;
rINTPND = 0x80000000;
EnableSubIrq(BIT_SUB_TC);
//EnableIrq(BIT_ADC);
rINTMSK = 0x7fffffff;
}
void Set_Clk(void)
{
int i;
U8 key;
U32 mpll_val = 0 ;
i = 2 ; //don't use 100M!
//boot_params.cpu_clk.val = 3;
switch ( i ) {
case 0: //200
key = 12;
mpll_val = (92<<12)|(4<<4)|(1);
break;
case 1: //300
key = 13;
mpll_val = (67<<12)|(1<<4)|(1);
break;
case 2: //400
key = 14;
mpll_val = (92<<12)|(1<<4)|(1);
break;
case 3: //440!!!
key = 14;
mpll_val = (102<<12)|(1<<4)|(1);
break;
default:
key = 14;
mpll_val = (92<<12)|(1<<4)|(1);
break;
}
//init FCLK=400M, so change MPLL first
ChangeMPllValue((mpll_val>>12)&0xff, (mpll_val>>4)&0x3f, mpll_val&3); //set the register--rMPLLCON
ChangeClockDivider(key, 12); //the result of rCLKDIVN [0:1:0:1] 3-0 bit
cal_cpu_bus_clk(); //HCLK=100M PCLK=50M
}
static void cal_cpu_bus_clk(void)
{
static U32 cpu_freq;
static U32 UPLL;
U32 val;
U8 m, p, s;
val = rMPLLCON;
m = (val>>12)&0xff;
p = (val>>4)&0x3f;
s = val&3;
//(m+8)*FIN*2 不要超出32位数!
FCLK = ((m+8)*(FIN/100)*2)/((p+2)*(1< //FCLK=400M FIN=12000000
val = rCLKDIVN;
m = (val>>1)&3;
p = val&1;
val = rCAMDIVN;
s = val>>8;
switch (m) {
case 0:
HCLK = FCLK;
break;
case 1:
HCLK = FCLK>>1;
break;
case 2:
if(s&2)
HCLK = FCLK>>3;
else
HCLK = FCLK>>2;
break;
case 3:
if(s&1)
HCLK = FCLK/6;
else
HCLK = FCLK/3;
break;
}
if(p)
PCLK = HCLK>>1;
else
PCLK = HCLK;
if(s&0x10)
cpu_freq = HCLK;
else
cpu_freq = FCLK;
val = rUPLLCON;
m = (val>>12)&0xff;
p = (val>>4)&0x3f;
s = val&3;
UPLL = ((m+8)*FIN)/((p+2)*(1<
UCLK = (rCLKDIVN&8)?(UPLL>>1):UPLL;
}