s3c2440 LCD及触摸屏的学习笔记(2) 触摸屏是通过中断来实现的。我的程序是在sdram中运行的,要想正确的实现中断跳转,就要使MMU工作,实现物理地址与虚拟地址的映射,把虚拟地址0x00000000映射到物理地址0x30000000(sdram的首地址)。MMU相应的函数在2440slib.s中。 触摸屏可分为矢量压力传感式、电阻式、电容式、红外式和表面声波式等,我用的是最普遍的-四线电阻式。
s3c2440集成了4线制电阻式的触摸屏接口,触点坐标的检测是通过A/D转换来实现的。s3c2440提供8路A/D模拟输入,其中有4路是与触摸屏复用的,s3c2440比s3c2410改进的地方是片内部加入了开关用的MOS管,在设计电路时,直接将4路触摸屏引出外加一路基准电压(3.3v)就可以了。 还有就是触摸屏的校准, 校准就是实现触摸屏上的x、y坐标和LCD的像素坐标对应起来。比较常见的校正方法是三点校正法(网上找的),即 LCD上每个点PD的坐标为[XD,YD],触摸屏上每个点PT的坐标为[XT,YT]。要实现触摸屏上的坐标转换为LCD上的坐标,需要下列公式进行转换:
XD=A×XT+B×YT+C YD=D×XT+E×YT+F 其中有ABCDEF六个参数, 分别解ABC和DEF,需要3组数据,那就需要采集3个校准点: 程序要实现的功能是:程序一开始,界面上会出现3个十字的取样点,分别标有1、2、3,然后用触笔依次的点击,完成后,会进入第二个界面,用触笔随机的点击屏幕,会以触点绘制出边长为4的小正方形。
简单的程序实现:
代码
#include "def.h"
#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:访问权限和缓冲属性 } |
|