触摸屏是通过中断来实现的。我的程序是在sdram中运行的,要想正确的实现中断跳转,就要使MMU工作,实现物理地址与虚拟地址的映射,把虚拟地址0x00000000映射到物理地址0x30000000(sdram的首地址)。MMU相应的函数在2440slib.s中。
触摸屏可分为矢量压力传感式、电阻式、电容式、红外式和表面声波式等,我用的是最普遍的-四线电阻式。
s3c2440集成了4线制电阻式的触摸屏接口,触点坐标的检测是通过A/D转换来实现的。s3c2440提供8路A/D模拟输入,其中有4路是与触摸屏复用的,s3c2440比s3c2410改进的地方是片内部加入了开关用的MOS管,在设计电路时,直接将4路触摸屏引出外加一路基准电压(3.3v)就可以了。
s3c2440一共有4种触摸屏接口模式,其中,自动(连续)XY坐标转换模式和等待中断模式应用地比较常见。在此,为实现触摸屏的功能,先是设置为等待中断模式,在产生中断后,再设置为自动(连续)XY坐标转换模式,依次读取触点的坐标值。
还有就是触摸屏的校准, 校准就是实现触摸屏上的x、y坐标和LCD的像素坐标对应起来。比较常见的校正方法是三点校正法(网上找的),即 LCD上每个点PD的坐标为[XD,YD],触摸屏上每个点PT的坐标为[XT,YT]。要实现触摸屏上的坐标转换为LCD上的坐标,需要下列公式进行转换:
简单的程序实现:
#include " option.h "
#include " 2440addr.h "
#include " 2440lib.h "
#include " 2440slib.h "
#include " mmu.h "
// ================================
extern char __ENTRY[];
// void MMU_SetMTT(int vaddrStart,int vaddrEnd,int paddrStart,int attr);
// void MMU_Init(void);
// LCD*****************************************************
#define M5D(n) ((n) & 0x1fffff) // 用于设置显示缓存区时,取低21位地址
#define LCD_WIDTH 240 // 屏幕的宽
#define LCD_HEIGHT 320 // 屏幕的高
// 垂直同步信号的脉宽、后肩和前肩-----这些值是根据TopPoly-TD035STED4.pdf文件中的13页的表
#define VSPW 1 // (3-1)
#define VBPD 1 // (15-1)
#define VFPD 1 // (12-1)
// 水平同步信号的脉宽、后肩和前肩-----这些值是根据TopPoly-TD035STED4.pdf文件中的13页的表
#define HSPW (10-1)
#define HBPD (20-1)
#define HFPD (10-1)
// 显示尺寸
#define LINEVAL (LCD_HEIGHT-1)
#define HOZVAL (LCD_WIDTH-1)
// for LCDCON1
#define CLKVAL_TFT 4 // 设置时钟信号
#define MVAL_USED 0 //
#define PNRMODE_TFT 3 // TFT型LCD
#define BPPMODE_TFT 13 // 24位TFT型LCD
// for LCDCON5
#define BPP24BL 0 // 32位数据表示24位颜色值时,低位数据有效,高8位无效
#define INVVCLK 0 // 像素值在VCLK下降沿有效
#define INVVLINE 1 // 翻转HSYNC信号
#define INVVFRAME 1 // 翻转VSYNC信号
#define INVVD 0 // 正常VD信号极性
#define INVVDEN 0 // 正常VDEN信号极性
#define PWREN 1 // 使能PWREN信号
#define BSWP 0 // 颜色数据字节不交换
#define HWSWP 0 // 颜色数据半字不交换
// 定义显示缓存区
volatile unsigned int LCD_BUFFER[LCD_HEIGHT][LCD_WIDTH];
int A,B,C,D,E,F,K;
volatile int xdata, ydata;
int flagIIC; // IIC标志
int flagTS; // 触摸屏标志
// lCD******************************************************
// LCD显示函数 //
// 延时程序
void delay( int a)
{
int k;
for (k = 0 ;k < a;k ++ ) ;
}
// 绘制屏幕背景颜色,颜色为c
void Brush_Background( unsigned int c)
{
int x,y ;
for ( y = 0 ; y < LCD_HEIGHT ; y ++ )
{
for ( x = 0 ; x < LCD_WIDTH ; x ++ )
{
LCD_BUFFER[y][x] = c ;
}
}
}
// 绘制“十”字型
void drawCross(unsigned int x,unsigned int y,unsigned int color)
{
int i;
for (i = x - 10 ;i < x + 11 ;i ++ )
LCD_BUFFER[y][i] = color;
for (i = y - 10 ;i < y + 11 ;i ++ )
LCD_BUFFER[i][x] = color;
}
// 绘制正方形
void drawsquare(unsigned int x,unsigned int y,unsigned int color)
{
int i,j;
int x1 = x - 2 ;
int y1 = y - 2 ;
for (i = y1;i < y + 2 ;i ++ )
{
for (j = x1;j < x + 2 ;j ++ )
LCD_BUFFER[i][j] = color ;
}
}
// 绘制大小为8×16的ASCII码
void Draw_ASCII(unsigned int x,unsigned int y,unsigned int color, const unsigned char * ch)
{
unsigned short int i,j;
unsigned char mask,buffer;
for (i = 0 ;i < 16 ;i ++ )
{
mask = 0x80 ;
buffer = ch[i];
for (j = 0 ;j < 8 ;j ++ )
{
if (buffer & mask)
{
LCD_BUFFER[y + i][x + j] = color;
}
mask = mask >> 1 ;
}
}
}
void init_port_lcd() // 初始化
{
rGPCCON = 0xaaaaaaaa ;
rGPCUP = 0xffff ; // The pull up function is disabled GPC[15:0]
// *** PORT D GROUP
// Ports : GPD15 GPD14 GPD13 GPD12 GPD11 GPD10 GPD9 GPD8 GPD7 GPD6 GPD5 GPD4 GPD3 GPD2 GPD1 GPD0
// Signal : VD23 VD22 VD21 VD20 VD19 VD18 VD17 VD16 VD15 VD14 VD13 VD12 VD11 VD10 VD9 VD8
// Binary : 10 10 , 10 10 , 10 10 , 10 10 , 10 10 , 10 10 , 10 10 ,10 10
rGPDCON = 0xaaaaaaaa ;
rGPDUP = 0xffff ;
rLCDCON1 = (CLKVAL_TFT << 8 ) | (MVAL_USED << 7 ) | (PNRMODE_TFT << 5 ) | (BPPMODE_TFT << 1 ) | 0 ;
rLCDCON2 = (VBPD << 24 ) | (LINEVAL << 14 ) | (VFPD << 6 ) | (VSPW);
rLCDCON3 = (HBPD << 19 ) | (HOZVAL << 8 ) | (HFPD);
rLCDCON4 = (HSPW); //
// rLCDCON4 = (5<< 0);
rLCDCON5 = (BPP24BL << 12 ) | (INVVCLK << 10 ) | (INVVLINE << 9 ) | (INVVFRAME << 8 ) | ( 0 << 7 ) | (INVVDEN << 6 ) | (PWREN << 3 ) | (BSWP << 1 ) | (HWSWP);
rLCDSADDR1 = (((unsigned int )LCD_BUFFER >> 22 ) << 21 ) | M5D((unsigned int )LCD_BUFFER >> 1 );
rLCDSADDR2 = M5D( (M5D((unsigned int )LCD_BUFFER >> 1 ) + ((LCD_WIDTH * 32 / 16 + 0 ) * 320 )) ); // LCD_WIDTH*LCD_HEIGHT*4
rLCDSADDR3 = (LCD_WIDTH * 32 / 16 ) & 0x7ff ; // 参考s3c2440的手册
rLCDINTMSK |= ( 3 ); // 屏蔽LCD中断
// rTCONSEL = 0; // 无效LPC3480
rTCONSEL &= ( ~ 7 );
rTPAL = 0x0 ;
rTCONSEL &= ~ (( 1 << 4 ) | 1 );
rGPGUP = rGPGUP & ( ~ ( 1 << 4 )) | ( 1 << 4 ); // GPG4上拉电阻无效
rGPGCON = rGPGCON & ( ~ ( 3 << 8 )) | ( 3 << 8 ); // 设置GPG4为LCD_PWREN
rGPGDAT = rGPGDAT | ( 1 << 4 ); // GPG4置1
rLCDCON5 = rLCDCON5 & ( ~ ( 1 << 3 )) | ( 1 << 3 ); // 有效PWREN信号
rLCDCON5 = rLCDCON5 & ( ~ ( 1 << 5 )) | ( 0 << 5 ); // PWREN信号极性不翻转
rLCDCON1 |= 1 ; // LCD开启
}
// LCD显示函数 //
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 & ((unsigned int ) 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 ; // 写1清除标志
rINTSUBMSK =~ ( 0x1 << 9 ); // 清0中断使能,ADC的子中断
rSRCPND = 0x1 << 31 ; // 写1清除标志
rINTPND = 0x1 << 31 ; // 再次清A/D中断,开启A/D中断屏蔽
rADCTSC = 0xd3 ; // 设置等待光标按下中断模式,为下一次触笔的落下做准备
// 01101 0011-- // XP_PU, XP_Dis, XM_Dis, YP_Dis, YM_En.
}
const unsigned char one[] = { 0x00 , 0x00 , 0x00 , 0x10 , 0x70 , 0x10 , 0x10 , 0x10 , 0x10 , 0x10 , 0x10 , 0x10 , 0x10 , 0x7C , 0x00 , 0x00 };
const unsigned char two[] = { 0x00 , 0x00 , 0x00 , 0x3C , 0x42 , 0x42 , 0x42 , 0x04 , 0x04 , 0x08 , 0x10 , 0x20 , 0x42 , 0x7E , 0x00 , 0x00 };
const unsigned char three[] =
{ 0x00 , 0x00 , 0x00 , 0x3C , 0x42 , 0x42 , 0x04 , 0x18 , 0x04 , 0x02 , 0x02 , 0x42 , 0x44 , 0x38 , 0x00 , 0x00 };
// 用PCtoLCD2002字符生成软件
// 触摸屏校正
void TSCal( void )
{
int i = 0 ;
int xt[ 3 ],yt[ 3 ];
Brush_Background( 0xFFFFFF );
drawCross( 24 , 32 , 0xFF0000 );
Draw_ASCII( 28 , 36 , 0xFF0000 ,one); // ----
drawCross( 216 , 160 , 0xFF0000 );
Draw_ASCII( 220 , 164 , 0xFF0000 ,two); // ----
drawCross( 120 , 288 , 0xFF0000 );
Draw_ASCII( 124 , 292 , 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 ]);
D = ( 32 - 288 ) * (yt[ 1 ] - yt[ 2 ]) - ( 160 - 288 ) * (yt[ 0 ] - yt[ 2 ]);
E = (xt[ 0 ] - xt[ 2 ]) * ( 160 - 288 ) - ( 32 - 288 ) * (xt[ 1 ] - xt[ 2 ]);
F = yt[ 0 ] * (xt[ 2 ] * 160 - xt[ 1 ] * 288 ) + yt[ 1 ] * (xt[ 0 ] * 288 - xt[ 2 ] * 32 ) + yt[ 2 ] * (xt[ 1 ] * 32 - xt[ 0 ] * 160 );
A = ( 24 - 120 ) * (yt[ 1 ] - yt[ 2 ]) - ( 216 - 120 ) * (yt[ 0 ] - yt[ 2 ]);
B = (xt[ 0 ] - xt[ 2 ]) * ( 216 - 120 ) - ( 24 - 120 ) * (xt[ 1 ] - xt[ 2 ]);
C = yt[ 0 ] * (xt[ 2 ] * 216 - xt[ 1 ] * 120 ) + yt[ 1 ] * (xt[ 0 ] * 120 - xt[ 2 ] * 24 ) + yt[ 2 ] * (xt[ 1 ] * 24 - xt[ 0 ] * 216 );
}
int Main( int argc, char ** argv)
{
int i;
unsigned char key;
unsigned int mpll_val = 0 ;
int data;
int xLcd,yLcd;
mpll_val = ( 92 << 12 ) | ( 1 << 4 ) | ( 1 );
// init FCLK=400M, so change MPLL first
ChangeMPllValue((mpll_val >> 12 ) & 0xff , (mpll_val >> 4 ) & 0x3f , mpll_val & 3 );
ChangeClockDivider(key, 12 );
rINTMOD = 0x0 ; // All=IRQ mode
rINTMSK = BIT_ALLMSK; // All interrupt is masked.
MMU_Init();
init_port_lcd();
// *********************
rADCDLY = 50000 ; // 设置延时
rADCCON = ( 1 << 14 ) + ( 9 << 6 ); // 设置A/D预分频
rADCTSC = 0xd3 ; // 设置cm屏为等待中断模式。
pISR_ADC = ( int )ADCTs;
rINTMSK =~ ( 0x1 << 31 ); // 开启中断
rINTSUBMSK =~ (BIT_SUB_TC);
flagTS = 0 ;
// *********************
TSCal(); // 校正
delay( 200000 );
Brush_Background( 0xFFFFFF );
while ( 1 )
{
if (flagTS)
{
flagTS = 0 ;
xLcd = (A * xdata + B * ydata + C) / K; // 计算Y轴坐标
yLcd = (D * xdata + E * ydata + F) / K; // 计算X轴坐标
drawsquare(xLcd,yLcd, 0xFF0000 ); // 绘制正方形
}
delay( 1000000 );
}
return 0 ;
}
// -------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------
void MMU_Init( void ) // 配置MMU
{
int i,j;
MMU_DisableDCache();
MMU_DisableICache();
for (i = 0 ;i < 64 ;i ++ )
for (j = 0 ;j < 8 ;j ++ )
MMU_CleanInvalidateDCacheIndex((i << 26 ) | (j << 5 ));
MMU_InvalidateICache();
MMU_DisableMMU();
MMU_InvalidateTLB();
MMU_SetMTT( 0x00000000 , 0x03f00000 , 0x30000000 ,RW_CB); // bank0 __ENTRY的地址就是0x30000000
MMU_SetMTT( 0x04000000 , 0x07f00000 , 0 ,RW_NCNB); // bank0
MMU_SetMTT( 0x08000000 , 0x0ff00000 , 0x08000000 ,RW_CNB); // bank1
MMU_SetMTT( 0x10000000 , 0x17f00000 , 0x10000000 ,RW_NCNB); // bank2
MMU_SetMTT( 0x18000000 , 0x1ff00000 , 0x18000000 ,RW_NCNB); // bank3
// MMU_SetMTT(0x20000000,0x27f00000,0x20000000,RW_CB); // bank4
MMU_SetMTT( 0x20000000 , 0x27f00000 , 0x20000000 ,RW_CNB); // bank4 for STRATA Flash
MMU_SetMTT( 0x28000000 , 0x2ff00000 , 0x28000000 ,RW_NCNB); // bank5
// 30f00000->30100000, 31000000->30200000
MMU_SetMTT( 0x30000000 , 0x30100000 , 0x30000000 ,RW_CB); // bank6-1
MMU_SetMTT( 0x30200000 , 0x33e00000 , 0x30200000 ,RW_NCNB); // bank6-2
//
MMU_SetMTT( 0x33f00000 , 0x33f00000 , 0x33f00000 ,RW_CB); // bank6-3
MMU_SetMTT( 0x38000000 , 0x3ff00000 , 0x38000000 ,RW_NCNB); // bank7
MMU_SetMTT( 0x40000000 , 0x47f00000 , 0x40000000 ,RW_NCNB); // SFR
MMU_SetMTT( 0x48000000 , 0x5af00000 , 0x48000000 ,RW_NCNB); // SFR
MMU_SetMTT( 0x5b000000 , 0x5b000000 , 0x5b000000 ,RW_NCNB); // SFR
MMU_SetMTT( 0x5b100000 , 0xfff00000 , 0x5b100000 ,RW_FAULT); // not used
MMU_SetTTBase(_MMUTT_STARTADDRESS); // 写转换表基地址到C2
MMU_SetDomain( 0x55555550 | DOMAIN1_ATTR | DOMAIN0_ATTR); // 写域访问控制位到C3
// DOMAIN1: no_access, DOMAIN0,2~15=client(AP is checked)
MMU_SetProcessId( 0x0 );
MMU_EnableAlignFault();
MMU_EnableMMU(); // 使能MMU
MMU_EnableICache(); // 使能ICache
MMU_EnableDCache(); // DCache 必须要打开,当MMU打开时. DCache should be turned on after MMU is turned on.
}
// vaddrStart:虚拟起始地址
// vaddrEnd: 虚拟结束地址
// paddrStart:物理起始地址
// attr:访问属性
void MMU_SetMTT( int vaddrStart, int vaddrEnd, int paddrStart, int attr)
{
volatile unsigned int * pTT; // 定义了页表的指针
volatile int i,nSec;
pTT = (unsigned int * )_MMUTT_STARTADDRESS + (vaddrStart >> 20 ); // 由于内存块是1M的,写页表的基地址
nSec = (vaddrEnd >> 20 ) - (vaddrStart >> 20 ); // 计数有多少个段(每个段为1M)
for (i = 0 ;i <= nSec;i ++ )
* pTT ++= attr | (((paddrStart >> 20 ) + i) << 20 );
// 页表存储访问信息和存储块的基地址
// (((paddrStart>>20)+i)<<20) :对应的物理内存页的地址
// attr:访问权限和缓冲属性
}
XD=A×XT+B×YT+C
YD=D×XT+E×YT+F
其中有ABCDEF六个参数, 分别解ABC和DEF,需要3组数据,那就需要采集3个校准点:
XD0=A×XT0+B×YT0+C YD0=D×XT0+E×YT0+F
XD1=A×XT1+B×YT1+C YD1=D×XT1+E×YT1+F
XD2=A×XT2+B×YT2+C YD2=D×XT2+E×YT2+F
可以用矩阵方面的知识解这三元一次函数得:
D0=(XT0-XT2)×(YT1-YT2)-(XT1-XT2)×(YT0-YT2);
D1=(XD0-XD2)×(YT1-YT2)-(XD1-XD2)×(YT0-YT2);
D2=(XT0-XT2)×(XD1-XD2)-(XD0-XD2)×(XT1-XT2);
D3=YT0×(XT2×XD1-XT1×XD2)+YT1×(XT0×XD2-XT2×XD0)+YT2×(XT1×XD0-XT0×XD1);
D4=(YD0-XD2)×(YT1-YT2)-(YD1-XD2)×(YT0-YT2);
D5=(XT0-XT2)×(YD1-YD2)-(YD0-YD2)×(XT1-XT2);
D6=YT0×(XT2×YD1-XT1×YD2)+YT1×(XT0×YD2-XT2×YD0)+YT2×(XT1×YD0-XT0×YD1);
A=D1/D0; B=D2/D0; C=D3/D0; D=D4/D0; F=D5/D0; A=D6/D0;
程序要实现的功能是:程序一开始,界面上会出现3个十字的取样点,分别标有1、2、3,然后用触笔依次的点击,完成后,会进入第二个界面,用触笔随机的点击屏幕,会以触点绘制出边长为4的小正方形。