??xml version="1.0" encoding="utf-8" standalone="yes"?> Z个新的硬件设备定?/font>WinCE6.0操作pȝQ一般需要完成以下几个主要步骤: 1. 针对特定的硬件设备创建板U支持包(Board Support Package~写?/span>BSP)Q?/span>BSP必须包括BOOTLOADER?/span>OEM适配?/span>(OEM Adaptation Layer~写?/span>OAL)和一些必要的驱动?/span> 2. 利用创徏?/span>BSPQ定制一个系l设?/span>(OS Design)。即通过VS2005创徏一?/span>Platform Builder的工E。该工程可编译生最l的q行时映像文ӞRum-time ImageQ?/span> 3. 针对板上的外围设备创建相关驱动,q添加到BSP中?/span> 4. 通过创徏子工E和Catalog Items的方式,修改OS Design?/span> 5. ~译OS DesignQ下载编译得到的q行时映像文件到目标讑֤。此Ӟ可通过q程调试工具q行调试?/span> 6. 在完成所有的调试工作之后Q导q行时映像对应的SDK(Software Development Kit)Q应用程序的开发h员可Z?/span>SDK~写该设备的应用E序?/span> 可以看出Q在整个WinCE操作pȝ的移植过E中Q?/span>BSP的移植是最基础也是最关键的一步。而创?/span>BSP的过E主要包括以下几个内容: 1. 创徏BOOTLOADER?/span>BOOTLOADER在开发的q程中用于下载操作系l映像文件?/span> 2. 创徏OAL?/span>OAL最l被链接到内核映像文Ӟ它主要完成硬件的初始化和理?/span> 3. 创徏讑֤驱动。设备驱动是板上外围讑֤的Y件支持?/span> 4. 修改q行时映像的配置文g。配|文件主要包?/span>BIB?/span>REG{文件?/span> BOOTLOADER的主要作用是操作系l运行时映像加蝲到内存,q蟩转到OS的启动程序处。它的这一作用跟前一介l的NBOOT的作用完全一致?/span>BOOTLOADER获取q行时映像(一般对应的文g名ؓNKQ一般有两种Ҏ。它可以通过有线q接的方式象|络(Ethernet)?/span>USB或串口从外部下蝲NK。它也可以从本地的存储器(Flash?/span>Hard Disk)中加?/span>NK。通常Q?/span>BOOTLOADER通过Ethernet下蝲操作pȝ映像故将其称?/span>EBOOT。在开发的q程中?/span>EBOOTQ可以提高开发效率。通过使用EBOOTQ你可以很快速的下蝲NK到目标设备中。而利?/span>Flash~程工具或者是通过JTAG下蝲则很慢。在一些品最l发布时Q?/span>EBOOT是可以去掉的Q但也有一些则必须包括BOOTLOADERQ像X86的^台就是如此?/span> xQ我们已l了解了EBOOT的主要功能,Z实现q些功能Q?/span>EBOOT必须完成以下工作Q?/span> 1. 初始?/span>MCU。包括初始化MCU的相兛_存器、中断、看门狗、系l时钟、内存和MMU。前面几跟NBOOT基本一_但这里增加了?/span>MMU的初始化?/span> 2. 在完成所有的初始化工作之后,调用BootloaderMain()。这个函数的定义?/span>WinCE6.0中对应的文g?/span>C:"WINCE600"PLATFORM"COMMON"SRC"COMMON"BOOT"BLCOMMON"blcommon.c 3. BootloaderMain()主要依次调用以下几个函数Q?/span>OEMDebugInit()?/span>OEMPlatformInitQ)?/span>OEMPreDownloadQ)?/span>OEMLaunchQ)Q而这些函数必ȝEBOOT的代码来实现?/span> 4. 最l蟩转到OAL.exe?/span>StartUp处,q而启?/span>WinCE操作pȝ?/span> 整个程如下图所C: EBOOT的代码可参考C:"WINCE600"PLATFORM"DEVICEEMULATOR"SRC"BOOTLOADER"EBOOT目录。这里针对S3C2410的EBOOT做几点说明。前一介lNBOOT加蝲EBOOT的方法时提到QNBOOT必须EBOOT攑֜内存中指定的位置Q这个位|是由EBOOT的来军_的。具体的Q在EBOOT中的体现是boot.bib里的内存配置Q如下图所C? NBOOT加蝲EBOOT到内存的地址必须与此地址对应。由于在NBOOT中没有?/span>MMUQ所?/span>NBOOT使用的实际地址应该?/span>0x30021000Q否则系l将不能正常启动。第二点Q如果没有采?/span>NBOOT加蝲EBOOT的方法,而是?/span>EBOOT直接存储?/span>NOR Flash中,此时必须?/span>EBOOT的代码中实现自加载的q程Q即?/span>NOR Flash中的EBOOT全部加蝲?/span>RAM中,q执行,实现代码如下: Windows CE的有些函C需要用到物理内存结构体PHYSICAL_ADDRESSQ?/span> Windows CE?/span>ceddk.h中定义了PHYSICAL_ADDRESSQ它其实?/span>LARGE_INTEGERcdQ其定义如下Q?/span> // in ceddk.h typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS; // in winnt.h typedef union _LARGE_INTEGER{ struct{ DWORD LowPart; LONG HighPart; }; LONGLONG QuadPart; } LARGE_INTEGER; 可见Q?/span>Windows CE中用64?/span>Bit来代表物理地址Q对于大多数32位的CPU而言Q只需要把它的HighPart讄?/span>0可以了?/span> 如果要直接访问某一个地址的物理内存,Windows CE提供?/span>VirtualAlloc()?/span>VirtualCopy()函数Q?/span>VirtualAlloc负责在虚拟内存空间内保留一D虚拟内存,?/span>VirtualCopy负责把一D늉理内存和虚拟内存l定Q这P最l对物理内存的访问还是通过虚拟地址q行。它们的声明如下Q?/span> // 甌虚拟内存 LPVOID VirtualAlloc( LPVOID lpAddress, // 希望的虚拟内存v始地址 DWORD dwSize, // 以字节ؓ单位的大?/span> DWORD flAllocationType, // 甌cdQ分?/span>Reserve?/span>Commit DWORD flProtect // 讉K权限 ); // 把物理内存绑定到虚拟地址I间 BOOL VirtualCopy( LPVOID lpvDest, // 虚拟内存的目标地址 LPVOID lpvSrc, // 物理内存地址 DWORD cbSize, // 要绑定的大小 DWORD fdwProtect // 讉K权限 ); VirtualAlloc对虚拟内存的甌分ؓ两步Q保?/span>MEM_RESERVE和提?/span>MEM_COMMIT。其?/span>MEM_RESERVE只是在进E的虚拟地址I间内保留一D,q不分配实际的物理内存,因此保留的虚拟内存ƈ不能被应用程序直接用?/span>MEM_COMMIT阶段才真正的拟内存分配物理内存?/span> 下面的代码显CZ如何使用VirtualAlloc?/span>VirtualCopy来访问物理内存。因?/span>VirtualCopy负责把一D늉理内存和虚拟内存l定Q所?/span>VirtualAlloc的时候只需要对内存保留Q没有必要提交?/span> FpDriverGlobals = (PDRIVER_GLOBALS) VirtualAlloc( 0, DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE, MEM_RESERVE, PAGE_NOACCESS); if (FpDriverGlobals == NULL) { ERRORMSG(DRIVER_ERROR_MSG, (TEXT(" VirtualAlloc failed!\r\n"))); return; } else { if (!VirtualCopy( (PVOID)FpDriverGlobals, (PVOID)(DRIVER_GLOBALS_PHYSICAL_MEMORY_START), DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE, (PAGE_READWRITE | PAGE_NOCACHE))) { ERRORMSG(DRIVER_ERROR_MSG, (TEXT("VirtualCopy failed!\r\n"))); return; } } CEDDKq提供了函数MmMapIoSpace用来把一D늉理内存直接映到虚拟内存。此函数的原形如下: PVOID MmMapIoSpace( PHYSICAL_ADDRESS PhysicalAddress, // 起始物理地址 ULONG NumberOfBytes, // 要映的字节?/span> BOOLEAN CacheEnable // 是否~存 ); 其实Q?/span>MmMapIoSpace函数内部也是调用VirtualAlloc?/span>VirtualCopy函数来实现物理地址到虚拟地址的映的?/span>MmMapIoSpace函数的原代码是公开的,我们可以?/span>%_WINCEROOT%\PUBLIC\COMMON\OAK\DRIVERS\CEDDK\DDK_MAP\ddk_map.c得到。从MmMapIoSpace的实现我们也可以看出VirtualAlloc?/span>VirtualCopy的用法: PVOID MmMapIoSpace ( IN PHYSICAL_ADDRESS PhysicalAddress, IN ULONG NumberOfBytes, IN BOOLEAN CacheEnable ) { PVOID pVirtualAddress; ULONGLONG SourcePhys; ULONG SourceSize; BOOL bSuccess; SourcePhys = PhysicalAddress.QuadPart & ~(PAGE_SIZE - 1); SourceSize = NumberOfBytes + (PhysicalAddress.LowPart & (PAGE_SIZE - 1)); pVirtualAddress = VirtualAlloc(0, SourceSize, MEM_RESERVE, PAGE_NOACCESS); if (pVirtualAddress != NULL) { bSuccess = VirtualCopy( pVirtualAddress, (PVOID)(SourcePhys >> 8), SourceSize, PAGE_PHYSICAL | PAGE_READWRITE | (CacheEnable ? 0 : PAGE_NOCACHE)); if (bSuccess) { (ULONG)pVirtualAddress += PhysicalAddress.LowPart & (PAGE_SIZE - 1); } else { VirtualFree(pVirtualAddress, 0, MEM_RELEASE); pVirtualAddress = NULL; } } return pVirtualAddress; } 此外Q?/span>Windows CEq供?/span>AllocPhysMem函数?/span>FreePhysMem函数Q用来申请和释放一D连l的物理内存。函数可以保证申L物理内存是连l的Q如果函数成功,会返回虚拟内存的句柄和物理内存的起始地址。这对于DMA讑֤ؓ有用。在q里׃详细介绍了,读者可以参?/span>Windows CE的联机文?/span> q程如图Q?/p>
从图中可以看出,整个~译都是通过调用Blddemo.bat来实现的Q实际上也是q样Q编译WinCE是用Blddemo.batQ只不过后面可以跟不同的参数。编译阶D和pȝ产生阶段由Cebuild.bat完成Q文件拷贝阶D는Buildrel.bat完成Q最后的生成映像阶段由Makeimg.exe完成。下面介l一下各个阶Dc?/p>
1. ~译阶段(Compile phase) q个q程指对WinCE路径下的Private和Public目录q行~译Q将里面的源代码~译成库文g{,q个q程可能会花费几个小时。除非改动了Private或Public目录下的源码Q否则是不需要编译的。一般我们编译自q工程的时候,不需要这个步骤?/p>
2. pȝ产生阶段(Sysgen phase) 在这个过E中Q系l会Ҏ你在PB中Catalog中的选项Q删除和讄相应的SYSGEN环境变量Q链接相应的静态库Q过滤头文gQؓSDK创徏所需的导入库Q同时还会ؓWinCEpȝ创徏一些配|文件。最后会~译当前的BSP和应用程序?/p>
3. 文g拯阶段(Release Copy phase) 拯所有所需的文件到工程的release目录下面Q主要是前面pȝ产生阶段所产生的所有文件?/p>
4. 生成映像阶段(Make run-time image phase) 该过E首先调用cenlscmp.exeҎ国际语系与地定生成wince.nls文g。然后调用Fmerge.exe合ƈ一些配|文Ӟ合ƈ所有bib文g为ce.bibQ合q所有的reg文g为Reginit.iniQ合q所有的dat文g为Initobj.datQ合q所有的db文g为Initdb.ini。之后调用Regcomp.exe压羃reginit.ini为default.fdf。根据LOCAL环境变量的设|调用Res2exe.exe更新所有的dllQexe和cpl文g中的资源文gQ主要是更新其中的语a部分。再调用Txt2ucde.exe转换所有ASCII码字W串为UNICODE。最后调用Romimage.exe合ƈ所有文件ؓNK.bin?/p>
在整个编译过E中有时会遇到编译错误,q个可以通过察看Build.logQBuild.err和Build.wrn文g来分析问题,q三个文件应该是位于”\WINCE600”目录下面。编译错误可能在不同的编译阶D生,我们也可以根据这一Ҏ分析问题?/p>
一般在pȝ产生阶段(Sysgen phase)出现错误很可能是׃丢失lg或文仉成的,q时候Build.log会提供更多信息帮助分析问题。在q个阶段产生错误Q很可能是由于在当前工程中添加或者删除WinCElg造成的,其中有些lg的更Ҏ需要进?#8221;clean sysgen”的,而不能只使用”sysgen”命o。所以我的徏议是每次d或删除组仉?#8221;clean sysgen”?/p>
如上面介l系l生阶D?Sysgen phase)也会~译BSP和部分应用。所以如果错误出现在pȝ产生阶段的编译过E中Q一般一U可能就是你的代码有语法错误Q当然这U错误很好查。还有一U可能出现的错误是连接错误,有可能是丢失了lib库文件或者链接错了库文gQ也有可能是调用了错误的API函数Q还有就是设|了错误的环境变量,q些查v来相对困难一炏V?/p>
如果错误产生在文件拷贝阶D?Release copy phase)Q一U常见的问题是和硬盘驱动器有关Q检查一下release目录所在磁盘的盘I间?/p>
如果问题出现在生成映像阶D?Make run-time image phase)Q根据编译的输出H口的输Z息判断问题出在哪个子阶段。一U可能是你的bib文g或者reg文g中存在语法错误导致合q文件时出错Q还有就是注意你的image的大是否超q了config.bib文g中的讑֮Q还有就是可能丢׃某个或者某些文Ӟq些丢失的文件很可能是由于在前面的编译过E中出现错误D的?/p>
当然Q也有时候会遇到一些奇怪的问题Q这些问题可能是׃没有正确的安装WinCE造成的,比如在安装WinCE的时候,路径中不要有中文或者空格或者其他比较奇怪的字符。补丁要按照序来打Q要不也可能会出现问题?/p>
本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/FLandY1982/archive/2009/12/04/4941120.aspx
Code
;------------------------------------------------------------------------------
; Copy boot loader to memory
ands r9, pc, #0xFF000000 ; see if we are in flash or in ram
bne %f20 ; go ahead if we are already in ram
; This is the loop that perform copying.
ldr r0, = 0x21000 ; offset into the RAM
add r0, r0, #PHYBASE ; add physical base
mov r1, r0 ; (r1) copy destination
ldr r2, =0x0 ; (r2) flash started at physical address 0
ldr r3, =0x10000 ; counter (0x40000/4)
10 ldr r4, [r2], #4
str r4, [r1], #4
subs r3, r3, #1
bne %b10
; Restart from the RAM position after copying.
mov pc, r0
nop
nop
nop
; Shouldn't get here.
b .
]]>
2.VirtualCopy用来l定一块物理内存到当前q程虚拟地址I间。参数里的lpvSrc既可以是内核D늚虚拟地址也可以是物理地址(用page_physical来标?。同时要注意lpvSrc的右UM否?br>
3.使用VirtualAlloc要包含Winbase.h;使用VirtualCopy时要包含plfuncs.h.两者都要链接coredll.lib.
4.在CE5.0之前,使用VirtualAlloc获得的虚拟地址I间分ؓ两种情Ş:
(1)大小?MB以下?位于调用q程的虚拟空间中;
(2)大小大于2MB?位于用户态的׃n地址I间?0x42000000-0x7E000000 )
1. 如果copy的物理地址?12M范围内,那么׃静态映的存在QlpvSrc可以为静态映的虚拟地址Q也可以为物理地址。采用后者需要指定page_physicalQ同时lpvSrc右移8位?
2. 如果copy的物理地址?12M范围外,那么׃微Y的如下规?#8220;
VirtualCopy also supports the PAGE_PHYSICAL flag. You must set this flag when you are mapping physical memory that resides beyond 512 MB, that is, physical memory with an address above 0x1FFFFFFF.”
lpvSrc只能为物理地址Q同旉要右U?br>
只要讄了PAGE_PHYSICAL 为真Q那么就需要把lpvSrc右移8?/span>
嵌入式设备与桌面PC的一个显著不同是它的应用E序中通常需要直接访问某一D늉理内存,q在驱动E序中对物理内存的访问尤为重要,其是像ARM体系l构下,I/O端口也被映射成某一个物理内存地址。因此,与桌面版?/span>Windows相比Q?/span>Windows CE提供了相对简单的物理内存讉K方式。无论是驱动E序q是应用E序都可以通过API讉K某一D늉理内存?/span>
]]>
]]>
]]>
]]>
WinCE中的GWES模块负责加蝲和管理Touch Panel驱动QTouch Panel的MDD层向上提供DDI接口QPDD层是针对g的实玎ͼ对MDD层提供DDSI接口?/p>
WinCE中的GWES模块负责加蝲和管理Touch Panel驱动QTouch Panel的MDD层向上提供DDI接口QPDD层是针对g的实玎ͼ对MDD层提供DDSI接口?/p>
1 Touch Panel驱动中的数据l构
(1) TOUCH_PANEL_SAMPLE_FLAGS
用于描述一个采L的信息,q些信息被定义在一个枚丄构中Q?/p>
enum enumTouchPanelSampleFlags {
TouchSampleValidFlag = 0x01,
TouchSampleDownFlag = 0x02,
TouchSampleIsCalibratedFlag = 0x04,
TouchSamplePreviousDownFlag = 0x08,
TouchSampleIgnore = 0x10,
TouchSampleMouse = 0x40000000
};
TouchSampleValidFlagQ一个有效的采样?/p>
TouchSampleDownFlagQ第一ơ按触摸屏时Q返回该flag
TouchSampleIsCalibratedFlagQ采Lx和y坐标g需要再被校验了
TouchSamplePreviousDownFlagQ表CZ一ơ采L态是按在触摸屏上
TouchSampleIgnoreQ忽略这ơ采样?/p>
TouchSampleMouseQ预?/p>
(2) TPDC_CALIBRATION_POINT
用于描述一个校验点的相关信息,l构如下Q?/p>
struct TPDC_CALIBRATION_POINT {
INT PointNumber;
INT cDisplayWidth;
INT cDisplayHeight;
INT CalibrationX;
INT CalibrationY;
};
PointNumberQ校验点索引|用于描述校验点在LCD上的位置
0Q中?/p>
1Q左?/p>
2Q左?/p>
3Q右?/p>
4Q右?/p>
cDisplayWidthQ显C的宽度
cDisplayHeightQ显C的高度
CalibrationXQ校验点的x坐标?/p>
CalibrationYQ校验点的y坐标?/p>
(3) TPDC_CALIBRATION_POINT_COUNT
用于描述需要校验的点的个数Q结构如下:
struct TPDC_CALIBRATION_POINT_COUNT {
DDI_TOUCH_PANEL_CALIBRATION_FLAGS flags;
INT cCalibrationPoints;
};
flagsQ一般ؓ0
cCalibrationPointsQ需要校验的点的个数Q一般是5
(4) gIntrTouch和gIntrTouchChanged
q是两个被MDD层用到的中断Q需要在PDD层中定义Q如下:
DWORD gIntrTouch = SYSINTR_NOP;
DWORD gIntrTouchChanged = SYSINTR_NOP;
gIntrTouch用于描述触摸屏中断,要和g的触摸屏中断相关联?/p>
gIntrTouchChanged用于在触摸屏按下后,每隔一D|间进行一ơ采P应该和硬件的一个定时器中断相关联?/p>
q两个值应该在DdsiTouchPanelEnable(..)函数中和g中断兌Qƈ在函数DdsiTouchPanelGetPoint(..)中根据情冉|除相应的中断?/p>
2 MDD层API
MDDZ层导出所需的Touch Panel驱动接口函数Q上层通过q些函数可以完成对Touch Panel的操作,下面会介l这些函数的功能?/p>
(1) BOOL TouchPanelEnable(PFN_TOUCH_PANEL_CALLBACK pfnCallback):
使能Touch Panel讑֤Q用于初始化Touch Panel?/p>
pfnCallbackQ指向处理Touch Panel事g的回调函?/p>
(2) Void TouchPanelDisable(void):
用Touch Panel讑֤?/p>
(3) BOOL TouchPanelGetDeviceCaps(INT iIndex, LPVOID lpOutput )Q?/p>
获得Touch Panel讑֤的相关信息?/p>
iIndexQ烦引?/p>
TPDC_SAMPLE_RATE_IDQ采L信息
TPDC_CALIBRATION_POINT_COUNT_IDQ采L个数信息
TPDC_CALIBRATION_POINT_IDQ采L坐标信息
lpOutputQ指向一个内存区域,用于存放获得的相关信?/p>
(4) VOID TouchPanelCalibrateAPoint(INT32 UncalX, INT32 UncalY, INT32* pCalX, INT32* pCalY)Q?/p>
输入的未经q校验的坐标信息转换成校验后的坐标信息?/p>
UncalXQ输入的X坐标
UncalYQ输入的Y坐标
pCalXQ校验后的X坐标
pCalYQ?校验后的Y坐标
(5) VOID TouchPanelPowerHandler(BOOL bOff)Q?/p>
Touch Panel的电源控制函数?/p>
bOffQTRUE表示关闭甉|QFALSE表示打开甉|
(6) BOOL TouchPanelReadCalibrationPoint(INT* pRawX, INT* pRawY)Q?/p>
获得Touch Panel的坐标?/p>
pRawXQ触摸屏的X坐标
PRawYQ触摸屏的Y坐标
(7) VOID TouchPanelReadCalibrationAbort(void)Q?/p>
l止当前的校验?/p>
(8) VOID TouchPanelSetCalibration(INT32 cCalibrationPoints, INT32* pScreenXBuffer, INT32* pScreenYBuffer, INT32* pUncalXBuffer, INT32* pUncalYBuffer)Q?/p>
校验函数。通过一l实际的触摸屏上采集的点坐标和相应的屏幕坐标计算校验pL。具体公式如下:
Sx = A1*Tx + B1*Ty + C1
Sy = A2*Tx + B2*Ty + C2
q里是通过昄屏坐标和采样的触摸屏坐标计算A1,B1,C1,A2,B2,C2?/p>
cCalibrationPointsQ校验点的个?/p>
pScreenXBufferQ一l显C屏上的X坐标
pScreenYBufferQ一l显C屏上的Y坐标
pUncalXBufferQ一l触摸屏上采LX坐标
pUncalYBufferQ一l触摸屏上采LY坐标
(9) BOOL TouchPanelSetMode(INT iIndex, LPVOID lpInput)Q?/p>
讄Touch Panel的工作模式?/p>
iIndexQ烦引模?/p>
TPSM_SAMPLERATE_HIGHQ设|高采样?/p>
TPSM_SAMPLERATE_LOWQ设|低采样?/p>
TPSM_PRIORITY_HIGH_IDQ设|触摸屏的IST为高优先U?/p>
TPSM_PRIORITY_NORMAL_IDQ设|IST为正怼先
lpInputQ指向一块内存,其中包含相关信息?/p>
3 PDD层API
(1) LONG DdsiTouchPanelAttach(void)Q?/p>
该函数在Touch Panel驱动的Dll被加载的时候调?/p>
(2) LONG DdsiTouchPanelDettach(void)Q?/p>
该函数在Touch Panel驱动的Dll被卸载的时候调?/p>
(3) BOOL DdsiTouchPanelEnable(void)Q?/p>
打开Touch Panel甉|q做初始化。一般会在这里初始化一些信息,打开Touch Panel讑֤甉|q做初始化?/p>
(4) VOID DdsiTouchPanelDisable(void)Q?/p>
关闭Touch Panel讑֤。关闭Touch Panel甉|q放资源?/p>
(5) BOOL DdsiTouchPanelGetDeviceCaps(ULONG iIndex, LPVOID lpOutput)Q?/p>
查询Touch Panel讑֤的相关信?/p>
iIndexQ查询的索引?/p>
TPDC_SAMPLE_RATE_IDQ查询采L信息
TPDC_CALIBRATION_POINT_IDQ查询需要校验的点的坐标
TPDC_CALIBRATION_POINT_COUNT_IDQ查询用于校验的点的个数
lpOutputQ根据iIndex值分别指向相关的信息
(6) void DdsiTouchPanelGetPoint(TOUCH_PANEL_SAMPLE_FLAGS pTipState, PLONG pUnCalX, PLONG pUnCalY)Q?/p>
获得Touch Panel上被按下的点的状态和坐标?/p>
pTipStateQ当前触摸点的状态,比如无效点,有效点,被按下的点等?/p>
pUnCalXQ触摸点的X坐标
pUnCalYQ触摸点的Y坐标
(7) VOID DdsiTouchPanelPowerHandler(BOOL bOff)Q?/p>
讄Touch Panel的电源状态?/p>
bOffQTRUE表示关闭甉|QFALSE表示打开甉|
(8) BOOL DdsiTouchPanelSetMode(ULONG iIndex, LPVOID lpInput)Q?/p>
讄Touch Panel工作模式?/p>
iIndexQ模式烦?/p>
TPSM_SAMPLERATE_HIGH_IDQ高采样?/p>
TPSM_SAMPLERATE_LOW_IDQ低采样?/p>
lpInputQ指向包含相关信息的内存
4 注册表设|?/p>
对于Touch Panel驱动来说Q有些注册表Ҏ需要配|的。具体如下:
“InputConfig”QWinCEpȝ输入配置?/p>
Bit0表示键盘输入
Bit1表示Touch Panel输入
Bit2表示g按键输入
“DeviceName”QTouch Panel驱动的名字?/p>
“MaxCalError”QTouch Panel的精度配置?/p>
“CalibrationData”QTouch Panel的校验倹{第一ơ启动WinCE后,需要通过WinCE的触摸屏校验E序对Touch Panelq行校验。校验完成后Q校验g被写入注册表里面?/p>
下面是关于Touch Panel的注册表配置的例子:
[HKEY_LOCAL_MACHINE\ControlPanel]
"InputConfig"=dword:3 ;3 => keybd and touch screen
[HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\TOUCH]
"DriverName"="touch.dll"
"MaxCalError"=dword:8
"CalibrationData"="446,671 36,191 38,1179 856,1161 862,169 "
个h觉得Q要x好的理解Touch Panel驱动Q还是需要去读读代码Q基于WinCE6.0下,可以参?\WINCE600\PLATFORM\H4SAMPLE\SRC\DRIVERS\TOUCH"下面的驱动,Zq个驱动开发自q驱动会比较方ѝ?/p>
Windows CE下触摸屏驱动实现的分?br>Analysis of Touch Panel Driver Realization Based on Windows CE OS
刘林? ??
Liu,LinHui1 Zhang,Fen2
Q? 长沙理工大学能源与动力工E学院,湖南 长沙Q?10077Q? 华中U技大学机械U学与工E学院,湖北 武汉Q?30074Q?/p>
摘要Q本文介l了Windows CE操作pȝ的触摸屏驱动E序模型Q详l阐q嵌入式pȝ中电d触摸屏的Windows CE驱动E序的设计和实现Ҏ?/p>
关键词:触摸屏,Windows CE
中图分类PTP316
文献标识码:A
AbstractQThis article introduced touch panel driver model of Windows CE operating system, and elaborated the driver design and the realization method of resistance-type touch panel based on an embedded system which take Windows CE as operating system.
KeywordQTouch PanelQWindows CE
1. 前言
触摸屏是嵌入式设备中常用的计机输入讑֤Q它可操作单直观,Zh都会使用Q这一Ҏ论是键盘q是鼠标都无法与其相比。在手机、PDA{手持品及公共服务讑֤中大量采用触摸屏。触摸屏分ؓ电阻式、电容式、表面声波式{多U,电阻式触摸屏是目前应用比较广泛的一U,有四Uѝ五Uѝ七U等几种。本文将分析Windows CE操作pȝ下的触摸屏驱动程序模型及实现Ҏ?/p>
2. Windows CE触摸屏驱动程序模?br>在Windows CE操作pȝ中触摸屏驱动是一U分层驱动。其驱动模型如图1所C。上层是模型讑֤驱动E序QModel Device Driver, MDDQ,下层是依赖^台的驱动E序QPlatform Dependent Driver, PDDQ。MDD通常无需修改直接使用QMDD链接PDD层ƈ定义它希望调用的函数接口Q设备驱动程序提供器接口QDevice Driver Service Provider Interface, DDSIQ。同时MDD把不同的函数集提供给操作pȝQ这些函数叫做设备驱动程序接口(Device Driver Interface, DDIQ,q部分ؓ也就是我们通常驱动需要实现的部分?/p>
3 Windows CE的触摸屏驱动E序接口
Windows CE的触摸屏驱动链接了tch_cal.lib和tchmdd.lib两个静态链接库。触摸屏驱动由GWES加蝲QGWES通过DDI调用驱动E序获取讑֤状态,讄驱动功能{,而驱动本w通过DDSI直接获得g信息来确定当前触摸屏的状态?/p>
Windows CE触摸屏驱动要求的DDI接口包括QTouchPanelGetDeviceCaps、TouchPanelEnable、TouchPanelDisable、TouchPanelSetMode、TouchPanelReadCalibrationPoint、TouchPanelReadCalibrationAbort、TouchPanelSetCalibration、TouchPanelCalibrateAPoint、TouchPanelPowerHandler?/p>
Windows CE触摸屏驱动要求的DDSI接口包括QDdsiTouchPanelAttach、DdsiTouchPanelDetach、DdsiTouchPanelDisable、DdsiTouchPanelEnable、DdsiTouchPanelGetDeviceCaps、DdsiTouchPanelGetPoint、DdsiTouchPanelPowerHandler?/p>
4 Windows CE的触摸屏数据采集
Windows CE触摸屏驱动程序采用中断方式对触摸W的按下状态进行检,如果到触摸W按下将产生中断q触发一个事仉知一个工作线E开始采集数据。同Ӟ驱动打开一个硬件定时器Q只要检到触摸W仍然在按下状态将定时触发同一个事仉知工作U程采集数据Q直到触摸笔抬v后关闭该定时器,q新检按下状态。驱动中采用了触摸屏中断以及定时器中断两个中断源Q不仅可以监控触摸笔按下和抬L态,而且可以触摸笔按下时的拖动轨迹?/p>
触摸屏驱动在初始化过E调用TouchPanelEnable函数使能触摸屏。该函数调用的DDSI函数为:DdsiTouchPanelEnable和DdsiTouchPanelDisable。该函数实现如下 内容Q?/p>
1) 创徏事ghTouchPanelEvent和hCalibrationSampleAvailable。hTouchPanelEvent事g在正常状态下当有触摸W按下或者按下后需要定旉集数据时被触发。而hCalibrationSampleAvailable事g在校准状态下当有校准数据输入时被触发Q?/p>
2) 查ƈ初始化所需的中断gIntrTouchQ触摸屏中断Q和gIntrTouchChangedQ定时器中断Q,q将中断gIntrTouch、gIntrTouchChanged兌C件hTouchPanelEvent。当gIntrTouchQgIntrTouchChanged中断产生时将触发hTouchPanelEvent事gQ?/p>
3) 创徏一个ISRU程TouchPanelpISR。TouchPanelpISR用于{待和处理触摸屏事ghTouchPanelEventQ它是整个驱动程序中唯一的事件源?/p>
TouchPanelpISR函数是实现触摸屏数据采集关键函数Q它实现的内容ؓQ?/p>
1) {待循环Q用于接收hTouchPanelEvent事gQƈ构成函数的主体;
2) 通过调用DdsiTouchPanelGetPoint函数获取当前触摸屏位|和状态信息;
3) 在获取有效数据且在校准状态下Q收集ƈ提交按下的位|信息;
4) 在正常状态下Q校准数据,q检查校准后数据的有效性;
5) 最后调用由GWES传入的回调函敎ͼ提交位置信息和状态信息?/p>
因此Q在触摸屏驱动程序中DdsiTouchPanelEnable、DdsiTouchPanelDisable和DdsiTouchPanelGetPoint三个DDSI接口函数是驱动实现的关键所在?/p>
在DdsiTouchPanelEnable和DdsiTouchPanelDisable函数中分别打开和关闭触摸屏gQ这两个函数其实可以不真正操作硬Ӟ而只是实现Y件上的控Ӟ但是Z降低功耗最好在DdsiTouchPanelDisable中将触摸屏控制器甉|关闭q在DdsiTouchPanelEnable函数中打开?/p>
在DdsiTouchPanelGetPoint函数中实现对触摸屏数据的采样。从上面的分析得知MDD通过hTouchPanelEvent和hCalibrationSampleAvailable事g控制采样Q这两个事g被触发都调用该函数。而这两个事g触发条g有两个:
1) 触摸W按下时产生触摸屏中断gIntrTouch时触发;
2) 触摸W按下后Q定时器被打开Q定时器定时生中断gIntrTouchChangedQƈ触发事gQ直到触摸笔抬v为止?/p>
因此该函C仅需要对触摸屏数据采P而且需要对触发条gq行状态控Ӟ其流E如?所C。图中定义了三个变量Q它们分别ؓQ?/p>
1) TouchIrq为静态变量或全局变量Q且初始gؓTRUEQ该变量必须在触摸屏按下q生触摸屏中断时设|ؓFALSEQ?/p>
2) InterruptType为静态变量或全局变量Q且初始gؓSYSINTR_NOPQ当在处理触摸屏中断时设|ؓSYSINTR_TOUCHQ在处理定时器中断时讄为SYSINTR_TOUCH_CHANGEDQ其余设|ؓSYSINTR_NOPQ且在处理完毕后必须其作ؓ参数传入InterruptDone函数以清除中断;
3) g_NextExpectedInterrupt为静态变量或全局变量Q该变量表示下一个希望生的中断Q初始状态ؓPEN_DOWNQ也是触摸W在抬v状态,因此希望下一个生的中断为PEN_DOWN。当触摸屏中断生以及定时器中断产生时该变量为PEN_UP_OR_TIMERQ也是下一个可能生的状态ؓ触摸W抬L态或者触摸笔按下但定时器中断产生?/p>
DdsiTouchPanelGetPoint函数一开始从触摸W抬L态开始执行,此时TouchIrq{于TRUE。如果此时触摸笔按下Q将讄TouchIrq为FALSEQ表C本ơ采h׃触摸屏中断生ƈ讄下一ơ调用由定时器生。然后设|InterruptType状态ؓSYSINTR_TOUCHQ接着开始采集数据ƈ讄g_NextExpectedInterrupt变量为PEN_UP_OR_TIMERQ表CZ一ơ生的中断为定时器中断。接着判断在触摸笔按下状态(g_NextExpectedInterrupt{于PEN_UP_OR_TIMERQ下触摸W是否抬P如果抬v则设|g_NextExpectedInterrupt为PEN_DOWN恢复到抬L态。最后通过InterruptType作ؓ参数传入InterruptDone函数以清除中断。当触摸W按下,q生定时器中断ӞTouchIrq{于FALSEQ此时InterruptType被设|ؓSYSINTR_TOUCH_CHANGEDQ其余的动作基本和上面的程一致?/p>
5 Windows CE下的触摸屏校?br>电阻触摸屏需要校准。应用程序需要一些参考|以便接收到的触摸屏坐标数据转换成高层Y件所需的屏q坐标。理x况下校准E序只要在品初ơ加甉|试过E中q行一ơ就可以了,参考D存储在非易失性存储器中。在理想情况下只需两组原始数据Q即在屏q对角读取的最和最大倹{而在实际应用中,因ؓ许多电阻触摸屏存在显著的非线性,因此如果在最和最大g间简单的插入位置数gD驱动E序非常的不_?/p>
在Windows CE中通过在函数DdsiTouchPanelGetDeviceCaps 中设|校准点的个敎ͼ在TouchDriverCalibrationPointGet中获取每个校准点的屏q坐标。常用的校准Ҏ量ؓ5。校准UI在校准点坐标处相应昄一个十字叉Q用户需要精地在该十字叉位|按下触摸屏Q驱动通过TouchPanelReadCalibrationPoint函数d相应的触摸屏坐标|然后开始下一个校准点。@环设定的ơ数后,采集到的触摸屏坐标值和校准点屏q坐标送到TouchPanelSetCalibration函数中进行处理。该函数生校准基准参数?/p>
TouchPanelSetCalibration函数执行的动作是一套数学算法,具体内容?
在触摸屏数据与其位置偏移关系且屏q像素与其位|偏Udpd为线性关pd设情况下Q触摸屏q回的位|信息与像素位置信息之间?D坐标变换关系。则对于触摸屏按下点的触摸屏坐标(Tx,Ty)与其在显C备位|关pM匚w的点的屏q坐?Sx,Sy)之间的{换关p,可以通过下述坐标变换表示Q?/p>
Sx = A1*Tx + B1*Ty + C1
Sy = A2*Tx + B2*Ty + C2
TouchPanelSetCalibration的具体工作就是通过校准的动作获取的屏幕坐标和触摸屏坐标TouchCoordinate来确定A1QB1QC1和A2, B2, C2?/p>
6. l束?br>本文作者的创新点:从分析嵌入式Windows CE操作pȝ中触摸屏驱动E序的模型及实现Ҏ的角度深入剖析了Windows CE中触摸屏数据采集和校准的执行程Q对于类似系l的驱动开发具有一定的借鉴性?/p>
参考文献:
[1] Paul Kovitz. 电阻式触摸屏l构和实现原理,夏普公司Q?003
本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/zhongnanjun_3/archive/2008/11/11/3274020.aspx
转蝲h明出?/font> 作?马
前段旉UL 6.0 BSPQ目前已UL到触摸屏部分? ULq程中学C不少东西. 由其是关于触摸屏q部? 掌握了很多以前不会的东西. 觉得有必要把q些知识Ҏ理一?
一 g部分
g上的原理不是本文的重点,只讲一下大概的原理(主要是我也只知道大概的原? 毕竟׃是搞g? d!)
我移植用的这个屏?20*240 的TFT? 四线电阻式触? q种触屏的原理是׃个电dl成, 一个实现X位置的测量,一个用于Y位置上的量. 单来_是当用触笔按下屏幕Ӟ两个电阻层接? 电阻发生变化Q然后在X Y方向上生信? q个信号是电压信P 再经qCPU内部分AD转换为坐标? q个原理有点像高中物理课用的滑动电阻Q有一个最大上限,滑动C同的地方Q阻g? 2410本n集成了touch的控制器Q通过单的配置和读取相关的寄存器,可以实现触摸屏的操?
?驱动部分
Wince下的touch驱动跟很多其它的驱动一? 是分层的, 有MDD 和PDD两层. MDD层被pȝ隐藏h, 一般不用我们来修改. 而我们真正关心的是PDD ? 也就是要由开发者来修改的这一?
分析touch驱动Ӟ以我最q刚刚移植到一个基?410的板子上?.0的BSP包的触屏驱动Z.?span style="COLOR: red">C:\WINCE600\PLATFORM\DEVICEEMULATOR\SRC\DRIVERS\TOUCH? 扑ֈs3c2410x_touch.cpp文g. q里面正是PDD层的实现代码. Ҏ发现q里面的函数分ؓ两类Q一cL以TSP开头的函数Q一cL以DDSI开头的函数. TSP开头的函数为内部私有的函数Q是被DDSI调用? 而DDSI开头的函数则是对外的接? 也就是被MDD层的函数调用的接?
DdsiTouchPanelEnable是首先被调用的一个外部接? 它的实现可参见源E序, 它主
要做了下面几个事?
1 通过调用TSP_VirtualAlloc函数为驱动所用的IO,中断{硬件中断分配内存空?
2 通过调用KernelIoControl向系l申请两个中断,如果甌成功Q赋予相应的逻辑中断? KernelIoControl向底层是调用OEMIoControl函数, OEMIoControlҎKernelIoControl传进来的IOCTL代码Q做相应的操?比如q里, IOCTL是IOCTL_HAL_REQUEST_SYSINTRQ?它是向内核申请一个物理中断和逻辑中断的映?
3 通过调用TSP_PowerOn来初始化中断控制器,ADC寄存器,定时器等, 在TSP_PowerOn的实CQ有几点要说明一?
ADCDLY q个值在不同的模式下意义不同, 因ؓ前面通过ADCTSC已经配置为wait for interrupt mode, 所以这个值的意义和你的触W按下时, 从生中断信号到开始自动{换X,Y时的旉间隔是相关的Q它的单位是ms
v_pPWMregs->TCNTB3 = g_timer3_sampleticks
TCNTB3是timer3的count buffer, 当定时器启动? 0Q这个g一个设|好的频率递减Q直到减?Q?q时会生一个定时器中断. q个有什么用? 要理解它Q得知道触摸屏在中断模式下是如何工作?
当我们按下的触摸屏时Q会产生一个ADC的中? 同时我们的驱动还会启动一个定时器, q个定时器触发一个事件做数据采集, 在我们的手或触笔抬v来前Q这个定时器不断的触发采集事Ӟ直到它被关闭, 而它什么时候会被关闭呢Q就是在触笔的抬h? 下面截取的代码很好的说明的这个原?
if ( (v_pADCregs->ADCDAT0 & (1 << 15)) |(v_pADCregs->ADCDAT1 & (1 << 15)) )
{
bTSP_DownFlag = FALSE;
DEBUGMSG(ZONE_TIPSTATE, (TEXT("up\r\n")));
v_pADCregs->ADCTSC &= 0xff;
*pUncalX = x;
*pUncalY = y;
TSP_SampleStop();
……
}
上面的代码,if判断的正是是否抬?
而g_timer3_sampleticks的值是q样计算出来?
g_timer3_freq = (g_s3c2410_pclk / TIMER3_DIVIDER);
g_timer3_sampleticks = (g_timer3_freq / TSP_SAMPLE_RATE_LOW);
TIMER3_DIVIDER 的值是2, TSP_SAMPLE_RATE_LOW的值是100, ?/p>
v_pPWMregs->TCFG1 &= ~(0xf << 12);
v_pPWMregs->TCFG1 |= (0 << 12);
可知定时?/2分频, 所以,很容易计出Q所讄的定时器是每10ms产生一ơ定时器中断
而触摸屏中断是在你按下和抬v时生的.
DdsiTouchPanelGetPoint是采L主要实现函数Q当MDD到中断事g发生Ӟ该函C被调? 触摸屏的中断是SYSINTR_TOUCH, 而定时器的中断是SYSINTR_TOUCH_CHANGED
该函数用if else分别处理两种中断, 如下:
if (v_pINTregs->SUBSRCPND & (1<<IRQ_SUB_TC)) /* 触摸屏中?/
{
……
}
else /*定时器中?span style="WHITE-SPACE: pre"> */
{
}
DdsiTouchPanelGetPoint函数的实C码中Q调用了两个很重要的函数TSP_TransXY和TSP_GetXY
需要说明的是,q两个函数的实现跟LCD本n的分辨率是没有关pȝ.
TSP_GetXY用来获到AD采样|TSP_TransXY把它转化为屏上的坐标. 我移植touch驱动Ӟ遇到q点屏幕上面Q下面有反应Q或者点左上角,右上角有反应{类似的问题, 都是因ؓq两个函数没实现?
先来看TSP_GetXY函数.它的实现如下:
TSP_GetXY(INT *px, INT *py)
{
INT i;
INT xsum, ysum;
INT x, y;
INT dx, dy;
xsum = ysum = 0;
for (i = 0; i < TSP_SAMPLE_NUM; i++)
{
v_pADCregs->ADCTSC = (0 << 8) | /* UD_Sen*/
(1 << 7) | /* YMON 1 (YM = GND)*/
(1 << 6) | /* nYPON 1 (YP Connected AIN[n])*/
(0 << 5) | /* XMON 0 (XM = Z)*/
(1 << 4) | /* nXPON 1 (XP = AIN[7])*/
(1 << 3) | /* Pull Up Enable*/
(1 << 2) | /* Auto ADC Conversion Mode*/
(0 << 0); /* No Operation Mode*/
v_pADCregs->ADCCON |= (1 << 0); /* Start Auto conversion*/
while (v_pADCregs->ADCCON & 0x1); /* check if Enable_start is low*/
while (!(v_pADCregs->ADCCON & (1 << 15))); /* Check ECFLG*/
y = (0x3ff & v_pADCregs->ADCDAT1);
x = (0x3ff & v_pADCregs->ADCDAT0);
xsum += x;
ysum += y;
}
*px = xsum / TSP_SAMPLE_NUM;
*py = ysum / TSP_SAMPLE_NUM;
v_pADCregs->ADCTSC = (1 << 8) | /* UD_Sen*/
(1 << 7) | /* YMON 1 (YM = GND)*/
(1 << 6) | /* nYPON 1 (YP Connected AIN[n])*/
(0 << 5) | /* XMON 0 (XM = Z)*/
(1 << 4) | /* nXPON 1 (XP = AIN[7])*/
(0 << 3) | /* Pull Up Disable*/
(0 << 2) | /* Normal ADC Conversion Mode*/
(3 << 0); /* Waiting Interrupt*/
dx = (*px > x) ? (*px - x) : (x - *px);
dy = (*py > y) ? (*py - y) : (y - *py);
return((dx > TSP_INVALIDLIMIT || dy > TSP_INVALIDLIMIT) ? FALSE : TRUE);
}
关于q个函数有几点要说明.
Ҏ2410的手? ADCDAT0 保存是X方向上采Ll果, ADCDAT1 保存是Y方向上采Ll果, 所? 我们看到下面的两行代?/p>
y = (0x3ff & v_pADCregs->ADCDAT1);
x = (0x3ff & v_pADCregs->ADCDAT0);
与上0x3ff, 是因? ADCDAT寄存器只用了前面 10位来保存AD采样的结? 而这?410内部的AD模块只有10位精度是怸致的.所以,AD转换后的最大g会超q?024-1.
当然上在那种计算Ҏq不是绝对的 Q?Ҏg构造的不同, 比如有可能你x方向的坐标值和采样值成反比Q就要按下面的方式计?
x = 0x3ff - (0x3ff & v_pADCregs->ADCDAT0);
再看TSP_TransXY函数. 我移植的版本的实现如?
PRIVATE void
TSP_TransXY(INT *px, INT *py)
{
*px = (*px >= TSP_MAXX) ? (TSP_MAXX) : *px;
*py = (*py >= TSP_MAXY) ? (TSP_MAXY) : *py;
*px = (*px - TSP_MINX);
*py = (*py - TSP_MINY);
*px = (*px >= 0) ? *px : 0;
*py = (*py >= 0) ? *py : 0;
*px = *px * TSP_LCDY / (TSP_MAXX - TSP_MINX);
*py = *py * TSP_LCDX / (TSP_MAXY - TSP_MINY);
*px = (*px >= TSP_LCDY)? (TSP_LCDY - 1) : *px;
*py = (*py >= TSP_LCDX)? (TSP_LCDX - 1) : *py;
*px = TSP_LCDY - *px - 1;
*py = TSP_LCDX - *py - 1;
}
q个实现是我在模拟器的实C码基上修改的. q个函数计算X,Y的坐标用的是一个公式,至于q个公式是怎么来的Q我׃太清楚了. 只说明一?
#define TSP_MINX 88
#define TSP_MINY 84
#define TSP_MAXX 952
#define TSP_MAXY 996
上面四个值是定义X+, X-, Y+, Y-四个有效的采样? 理论上应该是0?023(10 bit ADC), 但实际肯定有偏差Q准来? 换了不同的硬件^収ͼq四个值应该是要重新测q的. 我就直接沿用原BSP中的g.
int x=1;
但你从没用到q?x。也许这一行是你以前?x 时留下来的,只删除了使用它的代码Q而忘了删除这个变量。Warning Level 4 能找到这些小ȝ。所以,Z么不让编译器帮助你完成可能是最高别的专业化呢Q用Level 4 ~译是展CZ工作态度的一U方式。如果你为公众用者编写库QLevel 4 则是C交CD上需要的。你不想你的开发h员用低U选项清洁地编译他们的代码?br> 问题是,Level 4 实在是太q于注意l节Q在 Level 4 上,~译器连未引用参数这h伤大雅的事情也要抱怨(当然Q除非你真的有意使用q个参数Q这时便相安无事Q。假设你有一个函数带来两个参敎ͼ但你只用其中一个:
int SomeFunction(int arg1, int arg2){ return arg1+5;}
使用 /W4Q编译器抱怨:
“warning C4100: ''arg2'' : unreferenced formal parameter.”
Z骗过~译器,你可以加?UNREFERENCED_PARAMETER(arg2)。现在编译器在编译你的引?arg2 的函数时便会住口。ƈ且由于语句:
arg2;
实际上不做Q何事情,~译器不会ؓ之生Q何代码,所以在I间和性能上不会有M损失?/p>
l心的h可能会问Q既然你不?arg2Q那当初Z要声明它呢?通常是因Z实现某个函数以满x些API固有的v名需要,例如QMFC?OnSize 处理例程的v名必要像下面这P
void OnSize(UINT nType, int cx, int cy);
q里 cx/cy 是窗口新的宽/高,nType 是一个类?SIZE_MAXIMIZED ?SIZE_RESTORED q样的编码,表示H口是否最大化或是常规大小。一般你不会在意 nTypeQ只会关?cx ?xy。所以如果你想用 /W4Q则必须使用 UNREFERENCED_PARAMETER(nType)。OnSize 只是上千?MFC ?Windows 函数之一。编写一个基?Windows 的程序,几乎不可能不到未引用参数?br> 说了q么多关?UNREFERENCED_PARAMETER 内容。Judy 在她的问题中q提C另一?C++ E序员常用的q且其作用与 UNREFERENCED_PARAMETER 相同的诀H,那就是注释函数v名中的参数名Q?/p>
void CMyWnd::OnSize(UINT /* nType */, int cx, int cy){}
现在 nType 是未命名参数Q其效果像你敲?OnSize(UINT, int cx, int cy)一栗那么现在的关键问题是:你应该用哪U方法——未命名参数Q还?UNREFERENCED_PARAMETERQ?br> 大多数情况下Q两者没什么区别,使用哪一个纯_Ҏ风格问题。(你喜Ƣ你?java 咖啡是黑色还是奶油的颜色Q)但我认ؓ臛_有一U情况必M?UNREFERENCED_PARAMETER。假设你军_H口不允许最大化。那么你便禁?Maximize 按钮Q从pȝ菜单中删除,同时L每一个用戯够最大化H口的操作。因Z是偏执狂Q大多数好的E序员都是偏执狂Q,你添加一?ASSERT Q断aQ以保代码按照你的意图q行Q?/p>
void CMyWnd::OnSize(UINT nType, int cx, int cy){ ASSERT(nType != SIZE_MAXIMIZE); ... // use cx, cy}
质检团队竭尽所能以各种方式q行你的E序QASSERT 从没有弹Q于是你认ؓ~译生成 Release 版本是安全的。但是此?_DEBUG 定义没有了,ASSERT(nType != SIZE_MAXIMIZE)展开?((void)0)Qƈ?nType 一下子成了一个未引用参数Q这栯入你q净的编译。你无法注释掉参数表中的 nTypeQ因Z要在 ASSERT 中用它。于是在q种情况下——你唯一使用参数的地Ҏ?ASSERT 中或其它 _DEBUG 条g代码中——只?UNREFERENCED_PARAMETER 会保持编译器?Debug ?Release 生成模式下都没有问题。知道了吗?
l束讨论之前Q我惌有一个问题我没有提及Q就是你可以象下面这L pragma 指o抑制单一的编译器警告Q?/p>
#pragma warning( disable : 4100 )
4100 是未引用参数的出错代码。pragma 抑制其余文g/模块的该警告。用下面Ҏ可以重新启用q个警告Q?/p>
#pragma warning( default : 4100 )
不管怎样Q较好的Ҏ是在用特定的警告之前保存所有的警告状态,然后Q等你做完之后再回到以前的配|。那P你便回到的以前的状态,q个状态不一定是~译器的默认状态?br> 所以你能象下面q样在代码的前后?pragma 指o抑制单个函数的未引用参数警告Q?/p>
#pragma warning( push ) #pragma warning( disable : 4100 )void SomeFunction(...){}#pragma warning( pop )
当然Q对于未引用参数而言Q这U方法未免冗长,但对于其它类型的警告来说可能׃是这样了。库生成者都是用 #pragma warning 来阻塞警告,q样他们的代码可以用 /W4 q行清洁~译。MFC 中充满了q样?pragmas 指o。还有好多的 #pragma warning 选项我没有在本文讨论。有兛_们的信息请参考相x档?/p>
本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/apunix/archive/2008/01/14/2043945.aspx
DBGPARAM dpCurSettings =
{
TEXT("PCIBUS"), {
TEXT("Errors"),TEXT("Warnings"),TEXT("Functions"),TEXT("Initialization"),
TEXT("Enumeration"),TEXT("Load Order"),TEXT("Resource"),TEXT("Undefined"),
TEXT("Undefined"),TEXT("Undefined"),TEXT("Undefined"),TEXT("Undefined"),
TEXT("Undefined"),TEXT("Undefined"),TEXT("Undefined"),TEXT("Undefined") },
0x20
};
先来解释一?/span>DBGPARAMl构Q该l构?/span>Dbgapi.h中定义,所以在定义dpCurSettings的时候还需要包含这个头文gQ该l构定义如下Q?/span>
typedef struct _DBGPARAM {
WCHAR lpszName[32]; //模块的名?/span>
WCHAR rglpszZones[16][32]; //调试域的名字
ULONG ulZoneMask; //调试域的掩码
}DBGPARAM, *LPDBGPARAM;
在上面的例子中可以看刎ͼW一个是模块的名字,?/span>PCIBUS。而后定义?/span>16个域的名字,其中只用C7个域Q剩下的都定义ؓUndefined了。最后一个数字ؓ域的掩码Q表C当前哪个域是被ȀzȝQ?/span>0x20表示只有W?/span>6个域是被Ȁzȝ。从上面的例子还可以看出Q前7个域是有意义的,而且按照序分别对应1?/span>7。下面针对这些域需要定义相?/span>Debug调试的宏定义Q?/span>
#define DBGZONE_ERROR 1
#define DBGZONE_WARNING 2
#define DBGZONE_FUNCTION 3
#define DBGZONE_INIT 4
#define DBGZONE_ENUM 5
#define DBGZONE_LOADORDER 6
#define DBGZONE_RESOURCE 7
上述宏定义对应在dpCurSettings中的7个域Q然后就可以在打C息的时候,通过q些宏定义来对应相应的调试域了。例如:
从这D代码可以看出,如果dpCurSettings中的掩码定义?/span>0x20Q那么在DEBUGMSG的打CQ只有条件ؓDBGZONE_LOADORDER才会被打华ͼ循环中的前两个打C息是不会被打印的。如果想让上面的代码中的所?/span>DEBUGMSG都能打印必须讄掩码如下Q?/span>
dpCurSettings.ulZoneMask = DBGZONE_ERROR | DBGZONE_WARNING | DBGZONE_LOADORDER;
在一个模块中定义了调试域Q如果想在系l中M用还必须注册该调试域Q需要用到的函数?/span>DEBUGREGISTER(..)Q其中要把该调试模块的句柄作为参Cl它。例如:
DllMain(..)
{
switch(op)
{
case DLL_PROCESS_ATTACH:
DEBUGREGISTER(hPCIBUS);
break;
….
}
}
完成了上q工作以后,可以重新编译调试的模块Q然后运行系l来调试了。调试域的一个好处就是在Debug的过E中Q不需要终止系l可以动态的改变调试域,方便我们分析问题。首先,我们可以ZPlatform. Builder中的CE Debug Zones来调试,?/span>VS2005的菜单中选择TargetQ然后选择CE Debug ZonesQ如图:
然后会出C?/span>Debug Zones的窗口,在窗口弹Z后,它可能会׃Ҏ间来攉当前支持Debug Zone的模块,如下图:
该图只是一个例子,左边昄了可调试的模块,选择serial_SMDK2410.dllq个模块Q就?/span>S3C2410的串口驱动模块。在右侧可以看到各个调试域及名字Q用户可以根据需要来选择打开和关闭相应的调试域,最后点?/span>Apply?/span>OK可以了?/span>
当然Q还有其他的Ҏ来修改调试域Q一U方法是使用Target Control中的zo命o来修改,Target Control在以后介绍。还有一U方法就是通过SetDbgZone(..)函数来修攏V定义如下:
BOOL SetDbgZone(DWORDdwProcid, LPVOIDlpvMod, LPVOIDbaseptr, DWORDzone, LPDBGPARAMlpdbgTgt)
dwProcidQ?/span> q程的句?/span>
lpvModQ?/span> 调试模块的句?/span>
baseptrQ?/span> 讄?/span>NULL
zoneQ?/span> 新的调试域掩?/span>
lpdbgTgtQ?/span> q回新的DBGPARAMl构
上面?/span>Debug Zone的定义,使用以及调试作了大致的介l,按照上面的步骤可以给一个模块添加调试域Q注册调试域q在pȝq行以后随时更改调试域,其根本目的无非是帮助我们来调试模块和分析问题。一般情况下Q调试域只在Debug模式下用,但是也可以在Release模式下用。但是有些地斚w要修改,首先前面已经介绍q?/span>Debug模式下的打印?/span>DEBUGMSGQ?/span>Release模式下的打印应该使用RETAILMSG函数。所以在Release模式下,打印函数应该改ؓRETAILMSG函数。还有在注册调试域的时候,不能再?/span>DEBUGREGISTER(..)函数Q而是应该改用RETAILREGISTERZONES(..)函数?/span>