﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-唐吉诃德-文章分类-wince</title><link>http://www.cppblog.com/tdweng/category/16105.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 10 Apr 2013 01:12:26 GMT</lastBuildDate><pubDate>Wed, 10 Apr 2013 01:12:26 GMT</pubDate><ttl>60</ttl><item><title>WinCE6.0的EBOOT概要 </title><link>http://www.cppblog.com/tdweng/articles/141765.html</link><dc:creator>心羽</dc:creator><author>心羽</author><pubDate>Mon, 14 Mar 2011 02:34:00 GMT</pubDate><guid>http://www.cppblog.com/tdweng/articles/141765.html</guid><wfw:comment>http://www.cppblog.com/tdweng/comments/141765.html</wfw:comment><comments>http://www.cppblog.com/tdweng/articles/141765.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tdweng/comments/commentRss/141765.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tdweng/services/trackbacks/141765.html</trackback:ping><description><![CDATA[<span style="COLOR: red">wince博客 </span><a href="http://www.cnblogs.com/we-hjb"><span style="COLOR: red">http://www.cnblogs.com/we-hjb</span></a>
<p><font face=宋体></font>&nbsp;</p>
<p><font face=宋体>为一个新的硬件设备定制</font>WinCE6.0<span style="FONT-FAMILY: 宋体">操作系统，一般需要完成以下几个主要步骤：</span> </p>
<p style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt">1.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: 宋体">针对特定的硬件设备创建板级支持包</span>(Board Support Package<span style="FONT-FAMILY: 宋体">缩写为</span>BSP)<span style="FONT-FAMILY: 宋体">，</span>BSP<span style="FONT-FAMILY: 宋体">必须包括</span>BOOTLOADER<span style="FONT-FAMILY: 宋体">、</span>OEM<span style="FONT-FAMILY: 宋体">适配层</span>(OEM Adaptation Layer<span style="FONT-FAMILY: 宋体">缩写为</span>OAL)<span style="FONT-FAMILY: 宋体">和一些必要的驱动。</span></p>
<p style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt">2.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: 宋体">利用创建的</span>BSP<span style="FONT-FAMILY: 宋体">，定制一个系统设计</span>(OS Design)<span style="FONT-FAMILY: 宋体">。即通过</span>VS2005<span style="FONT-FAMILY: 宋体">创建一个</span>Platform Builder<span style="FONT-FAMILY: 宋体">的工程。该工程可编译产生最终的运行时映像文件（</span>Rum-time Image<span style="FONT-FAMILY: 宋体">）。</span></p>
<p style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt">3.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: 宋体">针对板上的外围设备创建相关驱动，并添加到</span>BSP<span style="FONT-FAMILY: 宋体">中。</span></p>
<p style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt">4.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: 宋体">通过创建子工程和</span>Catalog Items<span style="FONT-FAMILY: 宋体">的方式，修改</span>OS Design<span style="FONT-FAMILY: 宋体">。</span></p>
<p style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt">5.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: 宋体">编译</span>OS Design<span style="FONT-FAMILY: 宋体">，下载编译得到的运行时映像文件到目标设备。此时，可通过远程调试工具进行调试。</span></p>
<p style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt">6.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: 宋体">在完成所有的调试工作之后，导出该运行时映像对应的</span>SDK(Software Development Kit)<span style="FONT-FAMILY: 宋体">，应用程序的开发人员可基于此</span>SDK<span style="FONT-FAMILY: 宋体">编写该设备的应用程序。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="FONT-FAMILY: 宋体">可以看出，在整个</span>WinCE<span style="FONT-FAMILY: 宋体">操作系统的移植过程中，</span>BSP<span style="FONT-FAMILY: 宋体">的移植是最基础也是最关键的一步。而创建</span>BSP<span style="FONT-FAMILY: 宋体">的过程主要包括以下几个内容：</span></p>
<p style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt">1.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: 宋体">创建</span>BOOTLOADER<span style="FONT-FAMILY: 宋体">。</span>BOOTLOADER<span style="FONT-FAMILY: 宋体">在开发的过程中用于下载操作系统映像文件。</span></p>
<p style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt">2.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: 宋体">创建</span>OAL<span style="FONT-FAMILY: 宋体">。</span>OAL<span style="FONT-FAMILY: 宋体">最终被链接到内核映像文件，它主要完成硬件的初始化和管理。</span></p>
<p style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt">3.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: 宋体">创建设备驱动。设备驱动是板上外围设备的软件支持。</span></p>
<p style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt">4.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: 宋体">修改运行时映像的配置文件。配置文件主要包括</span>BIB<span style="FONT-FAMILY: 宋体">、</span>REG<span style="FONT-FAMILY: 宋体">等文件。</span></p>
<p style="TEXT-INDENT: 21pt">BOOTLOADER<span style="FONT-FAMILY: 宋体">的主要作用是将操作系统运行时映像加载到内存，并跳转到</span>OS<span style="FONT-FAMILY: 宋体">的启动程序处。它的这一作用跟前一篇介绍的</span>NBOOT<span style="FONT-FAMILY: 宋体">的作用完全一致。</span>BOOTLOADER<span style="FONT-FAMILY: 宋体">获取运行时映像（一般对应的文件名为</span>NK<span style="FONT-FAMILY: 宋体">）一般有两种方法。它可以通过有线连接的方式象网络</span>(Ethernet)<span style="FONT-FAMILY: 宋体">、</span>USB<span style="FONT-FAMILY: 宋体">或串口从外部下载</span>NK<span style="FONT-FAMILY: 宋体">。它也可以从本地的存储器</span>(Flash<span style="FONT-FAMILY: 宋体">、</span>Hard Disk)<span style="FONT-FAMILY: 宋体">中加载</span>NK<span style="FONT-FAMILY: 宋体">。通常，</span>BOOTLOADER<span style="FONT-FAMILY: 宋体">通过</span>Ethernet<span style="FONT-FAMILY: 宋体">下载操作系统映像故将其称为</span>EBOOT<span style="FONT-FAMILY: 宋体">。在开发的过程中使用</span>EBOOT<span style="FONT-FAMILY: 宋体">，可以提高开发效率。通过使用</span>EBOOT<span style="FONT-FAMILY: 宋体">，你可以很快速的下载</span>NK<span style="FONT-FAMILY: 宋体">到目标设备中。而利用</span>Flash<span style="FONT-FAMILY: 宋体">编程工具或者是通过</span>JTAG<span style="FONT-FAMILY: 宋体">下载则很慢。在一些产品最终发布时，</span>EBOOT<span style="FONT-FAMILY: 宋体">是可以去掉的，但也有一些则必须包括</span>BOOTLOADER<span style="FONT-FAMILY: 宋体">，像</span>X86<span style="FONT-FAMILY: 宋体">的平台就是如此。</span></p>
<p style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体">至此，我们已经了解了</span>EBOOT<span style="FONT-FAMILY: 宋体">的主要功能，为了实现这些功能，</span>EBOOT<span style="FONT-FAMILY: 宋体">必须完成以下工作：</span></p>
<p style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt">1.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: 宋体">初始化</span>MCU<span style="FONT-FAMILY: 宋体">。包括初始化</span>MCU<span style="FONT-FAMILY: 宋体">的相关寄存器、中断、看门狗、系统时钟、内存和</span>MMU<span style="FONT-FAMILY: 宋体">。前面几项跟</span>NBOOT<span style="FONT-FAMILY: 宋体">基本一致，但这里增加了对</span>MMU<span style="FONT-FAMILY: 宋体">的初始化。</span></p>
<p style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt; TEXT-ALIGN: left" align=left>2.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: 宋体">在完成所有的初始化工作之后，调用</span>BootloaderMain()<span style="FONT-FAMILY: 宋体">。这个函数的定义在</span>WinCE6.0<span style="FONT-FAMILY: 宋体">中对应的文件是</span>C:"WINCE600"PLATFORM"COMMON"SRC"COMMON"BOOT"BLCOMMON"blcommon.c</p>
<p style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt; TEXT-ALIGN: left" align=left>3.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>BootloaderMain()<span style="FONT-FAMILY: 宋体">主要依次调用以下几个函数，</span>OEMDebugInit()<span style="FONT-FAMILY: 宋体">、</span>OEMPlatformInit<span style="FONT-FAMILY: 宋体">（）、</span>OEMPreDownload<span style="FONT-FAMILY: 宋体">（）、</span>OEMLaunch<span style="FONT-FAMILY: 宋体">（），而这些函数必须由</span>EBOOT<span style="FONT-FAMILY: 宋体">的代码来实现。</span></p>
<p style="MARGIN-LEFT: 39pt; TEXT-INDENT: -18pt">4.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: 宋体">最终跳转到</span>OAL.exe<span style="FONT-FAMILY: 宋体">的</span>StartUp<span style="FONT-FAMILY: 宋体">处，进而启动</span>WinCE<span style="FONT-FAMILY: 宋体">操作系统。</span></p>
<p style="MARGIN-LEFT: 21pt"><span style="FONT-FAMILY: 宋体">整个流程如下图所示：</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p style="MARGIN-LEFT: 21pt"></p>
<p style="MARGIN-LEFT: 21pt"><img height=412 alt="" src="http://images.cnblogs.com/cnblogs_com/we-hjb/EBOOT_CF.png" width=631 border=0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EBOOT的代码可参考C:"WINCE600"PLATFORM"DEVICEEMULATOR"SRC"BOOTLOADER"EBOOT目录。这里针对S3C2410的EBOOT做几点说明。前一篇介绍NBOOT加载EBOOT的方法时提到，NBOOT必须将EBOOT放在内存中指定的位置，这个位置是由EBOOT的来决定的。具体的，在EBOOT中的体现是boot.bib里的内存配置，如下图所示。 </p>
<p style="MARGIN-LEFT: 21pt" align=left><img height=246 alt="" src="http://images.cnblogs.com/cnblogs_com/we-hjb/EBOOT_BIB.png" width=416 border=0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NBOOT<span style="FONT-FAMILY: 宋体">加载</span>EBOOT<span style="FONT-FAMILY: 宋体">到内存的地址必须与此地址对应。由于在</span>NBOOT<span style="FONT-FAMILY: 宋体">中没有使用</span>MMU<span style="FONT-FAMILY: 宋体">，所以</span>NBOOT<span style="FONT-FAMILY: 宋体">使用的实际地址应该为</span>0x30021000<span style="FONT-FAMILY: 宋体">，否则系统将不能正常启动。第二点，如果没有采用</span>NBOOT<span style="FONT-FAMILY: 宋体">加载</span>EBOOT<span style="FONT-FAMILY: 宋体">的方法，而是将</span>EBOOT<span style="FONT-FAMILY: 宋体">直接存储在</span>NOR Flash<span style="FONT-FAMILY: 宋体">中，此时必须在</span>EBOOT<span style="FONT-FAMILY: 宋体">的代码中实现自加载的过程，即将</span>NOR Flash<span style="FONT-FAMILY: 宋体">中的</span>EBOOT<span style="FONT-FAMILY: 宋体">全部加载到</span>RAM<span style="FONT-FAMILY: 宋体">中，并执行，实现代码如下:</span></p>
<div class=cnblogs_code><img id=Code_Closed_Image_224744 style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('Code_Closed_Text_224744').style.display='none'; document.getElementById('Code_Open_Image_224744').style.display='inline'; document.getElementById('Code_Open_Text_224744').style.display='inline';" height=16 src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" width=11 align=top><img id=Code_Open_Image_224744 style="DISPLAY: inline" onclick="this.style.display='none'; document.getElementById('Code_Open_Text_224744').style.display='none'; getElementById('Code_Closed_Image_224744').style.display='inline'; getElementById('Code_Closed_Text_224744').style.display='inline';" height=16 src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" width=11 align=top><span class=cnblogs_code_Collapse id=Code_Closed_Text_224744 style="DISPLAY: none">Code</span><span id=Code_Open_Text_224744 style="DISPLAY: inline"><br><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="COLOR: #000000">;</span><span style="COLOR: #000000">------------------------------------------------------------------------------</span><span style="COLOR: #000000"><br>;&nbsp;&nbsp;&nbsp;Copy&nbsp;boot&nbsp;loader&nbsp;to&nbsp;memory<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ands&nbsp;&nbsp;&nbsp;&nbsp;r9,&nbsp;pc,&nbsp;#</span><span style="COLOR: #800080">0xFF000000</span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;&nbsp;see&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;we&nbsp;are&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;flash&nbsp;or&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;ram<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bne&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">f20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;&nbsp;go&nbsp;ahead&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;we&nbsp;are&nbsp;already&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;ram<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;&nbsp;This&nbsp;</span><span style="COLOR: #0000ff">is</span><span style="COLOR: #000000">&nbsp;the&nbsp;loop&nbsp;that&nbsp;perform&nbsp;copying.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r0,&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #800080">0x21000</span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;&nbsp;offset&nbsp;into&nbsp;the&nbsp;RAM&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r0,&nbsp;r0,&nbsp;#PHYBASE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;&nbsp;add&nbsp;physical&nbsp;</span><span style="COLOR: #0000ff">base</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r1,&nbsp;r0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;&nbsp;(r1)&nbsp;copy&nbsp;destination<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r2,&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #800080">0x0</span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;&nbsp;(r2)&nbsp;flash&nbsp;started&nbsp;at&nbsp;physical&nbsp;address&nbsp;</span><span style="COLOR: #800080">0</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r3,&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #800080">0x10000</span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;&nbsp;counter&nbsp;(</span><span style="COLOR: #800080">0x40000</span><span style="COLOR: #000000">/</span><span style="COLOR: #800080">4</span><span style="COLOR: #000000">)<br></span><span style="COLOR: #800080">10</span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r4,&nbsp;[r2],&nbsp;#</span><span style="COLOR: #800080">4</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r4,&nbsp;[r1],&nbsp;#</span><span style="COLOR: #800080">4</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;subs&nbsp;&nbsp;&nbsp;&nbsp;r3,&nbsp;r3,&nbsp;#</span><span style="COLOR: #800080">1</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bne&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">b10<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;&nbsp;Restart&nbsp;from&nbsp;the&nbsp;RAM&nbsp;position&nbsp;after&nbsp;copying.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;pc,&nbsp;r0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nop<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nop<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nop<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;&nbsp;Shouldn</span><span style="COLOR: #800000">'</span><span style="COLOR: #800000">t&nbsp;get&nbsp;here.</span><span style="COLOR: #800000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.</span></span></div>
<div align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EBOOT<span style="FONT-FAMILY: 宋体">在实现必备功能的前提下，我们还可以扩展其功能，譬如说初始化</span>LCD<span style="FONT-FAMILY: 宋体">，并显示特定的启动画面，显示加载映像的进度等。</span></div>
<div align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="FONT-FAMILY: 宋体">本文粗略的介绍了</span>WinCE6.0<span style="FONT-FAMILY: 宋体">下</span>EBOOT<span style="FONT-FAMILY: 宋体">的内容，但没有涉及具体的代码实现，相关代码可以参考目录</span>C:"WINCE600"PLATFORM"DEVICEEMULATOR"SRC"BOOTLOADER"EBOOT<span style="FONT-FAMILY: 宋体">。总的来说，</span>EBOOT<span style="FONT-FAMILY: 宋体">的核心功能就是引导操作系统映像。</span></div>
<script type=text/javascript>
if ($ != jQuery) {
$ = jQuery.noConflict();
}
var isLogined = false;
var cb_blogId = 40622;
var cb_entryId = 1305926;
var cb_blogApp = "we-hjb";
var cb_blogUserGuid = "0248420b-63cf-dd11-9e4d-001cf0cd104b";
var cb_entryCreatedDate = '2008/10/7 23:05:00';
</script>
<img src ="http://www.cppblog.com/tdweng/aggbug/141765.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tdweng/" target="_blank">心羽</a> 2011-03-14 10:34 <a href="http://www.cppblog.com/tdweng/articles/141765.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转）Windows CE下访问物理内存的一些方法！！ </title><link>http://www.cppblog.com/tdweng/articles/140929.html</link><dc:creator>心羽</dc:creator><author>心羽</author><pubDate>Tue, 01 Mar 2011 09:37:00 GMT</pubDate><guid>http://www.cppblog.com/tdweng/articles/140929.html</guid><wfw:comment>http://www.cppblog.com/tdweng/comments/140929.html</wfw:comment><comments>http://www.cppblog.com/tdweng/articles/140929.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tdweng/comments/commentRss/140929.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tdweng/services/trackbacks/140929.html</trackback:ping><description><![CDATA[<div class=postbody>1.VirtualAlloc用来在进程的虚拟地址空间中保留（reserve)或者提交(commit)页。在保留时以64KB为粒度，即保留空间以64K为单位。而提交虚拟地址时，则以页(典型大小为4KB)为单位。<br><br>2.VirtualCopy用来绑定一块物理内存到当前进程虚拟地址空间。参数里的lpvSrc既可以是内核段的虚拟地址也可以是物理地址(用page_physical来标记)。同时要注意lpvSrc的右移与否。<br><br>3.使用VirtualAlloc要包含Winbase.h;使用VirtualCopy时要包含plfuncs.h.两者都要链接coredll.lib. <br><br>4.在CE5.0之前,使用VirtualAlloc获得的虚拟地址空间分为两种情形:<br>(1)大小在2MB以下时,位于调用进程的虚拟空间中;<br>(2)大小大于2MB时,位于用户态的共享地址空间内(0x42000000-0x7E000000 )
<p><font size=2><span><br>1. 如果copy的物理地址在512M范围内，那么由于静态映射的存在，lpvSrc可以为静态映射的虚拟地址，也可以为物理地址。采用后者需要指定page_physical，同时lpvSrc右移8位。 <br>2. 如果copy的物理地址在512M范围外，那么由于微软的如下规定&#8220; <br>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.&#8221; <br>lpvSrc只能为物理地址，同时需要右移<br><br><span style="COLOR: red">只要设置了PAGE_PHYSICAL 为真，那么就需要把lpvSrc右移8位</span><br><br>嵌入式设备与桌面</span><span>PC</span><span>的一个显著不同是它的应用程序中通常需要直接访问某一段物理内存，这在驱动程序中对物理内存的访问尤为重要，尤其是像</span><span>ARM</span><span>体系结构下，</span><span>I/O</span><span>端口也被映射成某一个物理内存地址。因此，与桌面版本</span><span>Windows</span><span>相比，</span><span>Windows CE</span><span>提供了相对简单的物理内存访问方式。无论是驱动程序还是应用程序都可以通过</span><span>API</span><span>访问某一段物理内存。</span></font></p>
<p><font size=2><span>Windows CE</span><span>的有些函数中需要用到物理内存结构体</span><span>PHYSICAL_ADDRESS</span><span>，</span> <span>Windows CE</span><span>在</span><span>ceddk.h</span><span>中定义了</span><span>PHYSICAL_ADDRESS</span><span>，它其实是</span><span>LARGE_INTEGER</span><span>类型，其定义如下：</span></font></p>
<p><span><font size=2><font size=+0><font face="Courier New">// in ceddk.h</font></font></font></span></p>
<p><span><font face="Courier New" size=2>typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;</font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">// in winnt.h</font></font></font></span></p>
<p><span><font face="Courier New" size=2>typedef union _LARGE_INTEGER{</font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">&nbsp;struct{</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>DWORD LowPart;</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>LONG HighPart;</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">&nbsp;};</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">&nbsp;LONGLONG QuadPart;</font></font></font></span></p>
<p><span><font face="Courier New" size=2>} LARGE_INTEGER;</font></span></p>
<p><font size=2><span>可见，</span><span>Windows CE</span><span>中用</span><span>64</span><span>个</span><span>Bit</span><span>来代表物理地址，对于大多数</span><span>32</span><span>位的</span><span>CPU</span><span>而言，只需要把它的</span><span>HighPart</span><span>设置为</span><span>0</span><span>就可以了。</span></font></p>
<p><font size=2><span>如果要直接访问某一个地址的物理内存，</span><span>Windows CE</span><span>提供了</span><span>VirtualAlloc()</span><span>和</span><span>VirtualCopy()</span><span>函数，</span><span>VirtualAlloc</span><span>负责在虚拟内存空间内保留一段虚拟内存，而</span><span>VirtualCopy</span><span>负责把一段物理内存和虚拟内存绑定，这样，最终对物理内存的访问还是通过虚拟地址进行。它们的声明如下：</span></font></p>
<p><font size=2><font size=+0><span><font face="Courier New">// </font></span><span>申请虚拟内存</span></font></font></p>
<p><span><font face="Courier New" size=2>LPVOID VirtualAlloc(</font></span></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;LPVOID lpAddress, </span><span><span>&nbsp;&nbsp;&nbsp; </span>// </span></font><span>希望的虚拟内存起始地址</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;DWORD dwSize, </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>以字节为单位的大小</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;DWORD flAllocationType, </span><span>&nbsp;// </span></font><span>申请类型，分为</span><span><font face="Courier New">Reserve</font></span><span>和</span><span><font face="Courier New">Commit</font></span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;DWORD flProtect </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>访问权限</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>);</span></font></font></font></p>
<p><font size=2><font size=+0><span><font face="Courier New">// </font></span><span>把物理内存绑定到虚拟地址空间</span></font></font></p>
<p><span><font face="Courier New" size=2>BOOL VirtualCopy( </font></span></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;LPVOID lpvDest, </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>虚拟内存的目标地址</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;LPVOID lpvSrc, </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>物理内存地址</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;DWORD cbSize, </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>要绑定的大小</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;DWORD fdwProtect </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>访问权限</span></font></font></p>
<p><span><font face="Courier New" size=2>);</font></span></p>
<p><font size=2><span>VirtualAlloc</span><span>对虚拟内存的申请分为两步，保留</span><span>MEM_RESERVE</span><span>和提交</span><span>MEM_COMMIT</span><span>。其中</span><span>MEM_RESERVE</span><span>只是在进程的虚拟地址空间内保留一段，并不分配实际的物理内存，因此保留的虚拟内存并不能被应用程序直接使用。</span><span>MEM_COMMIT</span><span>阶段才真正的为虚拟内存分配物理内存。</span></font></p>
<p><font size=2><span>下面的代码显示了如何使用</span><span>VirtualAlloc</span><span>和</span><span>VirtualCopy</span><span>来访问物理内存。因为</span><span>VirtualCopy</span><span>负责把一段物理内存和虚拟内存绑定，所以</span><span>VirtualAlloc</span><span>的时候只需要对内存保留，没有必要提交。</span></font></p>
<p><span><font size=2><font size=+0><font face="Courier New">FpDriverGlobals = </font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">(PDRIVER_GLOBALS) VirtualAlloc(</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>0, </font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE, </font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>MEM_RESERVE, </font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>PAGE_NOACCESS);</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">&nbsp;if (FpDriverGlobals == NULL) {</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>ERRORMSG(DRIVER_ERROR_MSG, (TEXT(" VirtualAlloc failed!\r\n")));</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>return;</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">&nbsp;}</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">&nbsp;else {</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>if (!VirtualCopy(</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>(PVOID)FpDriverGlobals, </font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>(PVOID)(DRIVER_GLOBALS_PHYSICAL_MEMORY_START), </font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE, </font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>(PAGE_READWRITE | PAGE_NOCACHE))) {</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ERRORMSG(DRIVER_ERROR_MSG, (TEXT("VirtualCopy failed!\r\n")));</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;</span>return;</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>}</font></font></font></span></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;}</span></font></font></font></p>
<p><font size=2><span>CEDDK</span><span>还提供了函数</span><span>MmMapIoSpace</span><span>用来把一段物理内存直接映射到虚拟内存。此函数的原形如下：</span></font></p>
<p><span><font size=2><font size=+0><font face="Courier New">PVOID MmMapIoSpace( </font></font></font></span></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;PHYSICAL_ADDRESS PhysicalAddress, </span><span>// </span></font><span>起始物理地址</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;ULONG NumberOfBytes, </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>要映射的字节数</span></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>&nbsp;BOOLEAN CacheEnable </span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span></font><span>是否缓存</span></font></font></p>
<p><span><font face="Courier New" size=2>);</font></span></p>
<p><font size=2><span>其实，</span><span>MmMapIoSpace</span><span>函数内部也是调用</span><span>VirtualAlloc</span><span>和</span><span>VirtualCopy</span><span>函数来实现物理地址到虚拟地址的映射的。</span><span>MmMapIoSpace</span><span>函数的原代码是公开的，我们可以从</span><span>%_WINCEROOT%\PUBLIC\COMMON\OAK\DRIVERS\CEDDK\DDK_MAP\ddk_map.c</span><span>得到。从</span><span>MmMapIoSpace</span><span>的实现我们也可以看出</span><span>VirtualAlloc</span><span>和</span><span>VirtualCopy</span><span>的用法：</span></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>PVOID</span><span> </span><span>MmMapIoSpace (</span></font></font></font></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>IN PHYSICAL_ADDRESS PhysicalAddress,</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>IN ULONG NumberOfBytes,</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>IN BOOLEAN CacheEnable</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>)</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New">{</font></font></font></span></p>
<p><font size=2><font size=+0><font face="Courier New"><span>PVOID pVirtualAddress;</span><span> </span><span>ULONGLONG SourcePhys;</span><span> </span></font></font></font></p>
<p><font size=2><font size=+0><font face="Courier New"><span>ULONG SourceSize;</span><span> </span><span>BOOL bSuccess;</span></font></font></font></p>
<p><span><font face="Courier New" size=2>&nbsp;</font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>SourcePhys = PhysicalAddress.QuadPart &amp; ~(PAGE_SIZE - 1);</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>SourceSize = NumberOfBytes + (PhysicalAddress.LowPart &amp; (PAGE_SIZE - 1));</font></font></font></span></p>
<p><span><font face="Courier New" size=2>&nbsp;</font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>pVirtualAddress = VirtualAlloc(0, SourceSize, MEM_RESERVE, PAGE_NOACCESS);</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>if (pVirtualAddress != NULL)</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>{</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>bSuccess = VirtualCopy(</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pVirtualAddress, (PVOID)(SourcePhys &gt;&gt; 8), SourceSize,</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>PAGE_PHYSICAL | PAGE_READWRITE | (CacheEnable ? 0 : PAGE_NOCACHE));</font></font></font></span></p>
<p><span><font face="Courier New" size=2>&nbsp;</font></span></p>
<p><font size=2><font size=+0><font face="Courier New"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (bSuccess)</span><span> </span><span>{</span></font></font></font></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>(ULONG)pVirtualAddress += PhysicalAddress.LowPart &amp; (PAGE_SIZE - 1);</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>else {</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>VirtualFree(pVirtualAddress, 0, MEM_RELEASE);</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pVirtualAddress = NULL;</font></font></font></span></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}</font></font></font></span></p>
<p><font size=2><font size=+0><font face="Courier New"><span><span>&nbsp;&nbsp;&nbsp; </span>}</span></font></font></font></p>
<p><span><font size=2><font size=+0><font face="Courier New"><span>&nbsp;&nbsp;&nbsp; </span>return pVirtualAddress;</font></font></font></span></p>
<p><span><font face="Courier New" size=2>}</font></span></p>
<p><font size=2><span>此外，</span><span>Windows CE</span><span>还供了</span><span>AllocPhysMem</span><span>函数和</span><span>FreePhysMem</span><span>函数，用来申请和释放一段连续的物理内存。函数可以保证申请的物理内存是连续的，如果函数成功，会返回虚拟内存的句柄和物理内存的起始地址。这对于</span><span>DMA</span><span>设备尤为有用。在这里就不详细介绍了，读者可以参考</span><span>Windows CE</span><span>的联机文档。</span></font></p>
</div>
<img src ="http://www.cppblog.com/tdweng/aggbug/140929.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tdweng/" target="_blank">心羽</a> 2011-03-01 17:37 <a href="http://www.cppblog.com/tdweng/articles/140929.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>wince编译过程</title><link>http://www.cppblog.com/tdweng/articles/140919.html</link><dc:creator>心羽</dc:creator><author>心羽</author><pubDate>Tue, 01 Mar 2011 08:53:00 GMT</pubDate><guid>http://www.cppblog.com/tdweng/articles/140919.html</guid><wfw:comment>http://www.cppblog.com/tdweng/comments/140919.html</wfw:comment><comments>http://www.cppblog.com/tdweng/articles/140919.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tdweng/comments/commentRss/140919.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tdweng/services/trackbacks/140919.html</trackback:ping><description><![CDATA[<p>在WinCE系统中，当我们完成了相关的开发和系统定制工作以后，会编译WinCE系统，最后生成NK.bin和NK.nb0。我现在用WinCE6.0在自己的PC上面编译一次用时19分16秒(有一天无聊，就测了一下)。下面介绍一下WinCE系统的编译过程，大致分为4个阶段：编译阶段(Compile phase)，系统产生阶段(Sysgen phase)，文件拷贝阶段(Release copy phase)和生成映像阶段(Make Run-time image phase)。</p>
<p>过程如图：</p>
<p><img height=353 alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nanjianhui/EntryImages/20080904/buildphase_1.JPG" width=561></p>
<p>从图中可以看出，整个编译都是通过调用Blddemo.bat来实现的，实际上也是这样，编译WinCE就是用Blddemo.bat，只不过后面可以跟不同的参数。编译阶段和系统产生阶段由Cebuild.bat完成，文件拷贝阶段由Buildrel.bat完成，最后的生成映像阶段由Makeimg.exe完成。下面介绍一下各个阶段。</p>
<p>1. 编译阶段(Compile phase)</p>
<p>这个过程指对WinCE路径下的Private和Public目录进行编译，将里面的源代码编译成库文件等，这个过程可能会花费几个小时。除非改动了Private或Public目录下的源码，否则是不需要编译的。一般我们编译自己的工程的时候，不需要这个步骤。</p>
<p>2. 系统产生阶段(Sysgen phase)</p>
<p>在这个过程中，系统会根据你在PB中Catalog中的选项，删除和设置相应的SYSGEN环境变量，链接相应的静态库，过滤头文件，为SDK创建所需的导入库，同时还会为WinCE系统创建一些配置文件。最后会编译当前的BSP和应用程序。</p>
<p>3. 文件拷贝阶段(Release Copy phase)</p>
<p>拷贝所有所需的文件到工程的release目录下面，主要是前面系统产生阶段所产生的所有文件。</p>
<p>4. 生成映像阶段(Make run-time image phase)</p>
<p>该过程首先调用cenlscmp.exe根据国际语系与地区设定生成wince.nls文件。然后调用Fmerge.exe合并一些配置文件，合并所有bib文件为ce.bib，合并所有的reg文件为Reginit.ini，合并所有的dat文件为Initobj.dat，合并所有的db文件为Initdb.ini。之后调用Regcomp.exe压缩reginit.ini为default.fdf。根据LOCAL环境变量的设置调用Res2exe.exe更新所有的dll，exe和cpl文件中的资源文件，主要是更新其中的语言部分。再调用Txt2ucde.exe转换所有ASCII码字符串为UNICODE。最后调用Romimage.exe合并所有文件为NK.bin。</p>
<p>在整个编译过程中有时会遇到编译错误，这个可以通过察看Build.log，Build.err和Build.wrn文件来分析问题，这三个文件应该是位于&#8221;\WINCE600&#8221;目录下面。编译错误可能在不同的编译阶段产生，我们也可以根据这一点来分析问题。</p>
<p>一般在系统产生阶段(Sysgen phase)出现错误很可能是由于丢失组件或文件造成的，这时候Build.log会提供更多信息帮助分析问题。在这个阶段产生错误，很可能是由于在当前工程中添加或者删除WinCE组件造成的，其中有些组件的更改是需要进行&#8221;clean sysgen&#8221;的，而不能只使用&#8221;sysgen&#8221;命令。所以我的建议是每次添加或删除组件都做&#8221;clean sysgen&#8221;。</p>
<p>如上面介绍系统产生阶段(Sysgen phase)也会编译BSP和部分应用。所以如果错误出现在系统产生阶段的编译过程中，一般一种可能就是你的代码有语法错误，当然这种错误很好查。还有一种可能出现的错误是连接错误，有可能是丢失了lib库文件或者链接错了库文件，也有可能是调用了错误的API函数，还有就是设置了错误的环境变量，这些查起来相对困难一点。</p>
<p>如果错误产生在文件拷贝阶段(Release copy phase)，一种常见的问题就是和硬盘驱动器有关，检查一下release目录所在磁盘的磁盘空间。</p>
<p>如果问题出现在生成映像阶段(Make run-time image phase)，根据编译的输出窗口的输出信息判断问题出在哪个子阶段。一种可能是你的bib文件或者reg文件中存在语法错误导致合并文件时出错，还有就是注意你的image的大小是否超过了config.bib文件中的设定，还有就是可能丢失了某个或者某些文件，这些丢失的文件很可能是由于在前面的编译过程中出现错误导致的。</p>
<p>当然，也有时候会遇到一些奇怪的问题，这些问题可能是由于没有正确的安装WinCE造成的，比如在安装WinCE的时候，路径中不要有中文或者空格或者其他比较奇怪的字符。补丁要按照顺序来打，要不也可能会出现问题。</p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/FLandY1982/archive/2009/12/04/4941120.aspx">http://blog.csdn.net/FLandY1982/archive/2009/12/04/4941120.aspx</a></p>
<img src ="http://www.cppblog.com/tdweng/aggbug/140919.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tdweng/" target="_blank">心羽</a> 2011-03-01 16:53 <a href="http://www.cppblog.com/tdweng/articles/140919.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WINCE串口驱动MDD层代码简单分析 </title><link>http://www.cppblog.com/tdweng/articles/140916.html</link><dc:creator>心羽</dc:creator><author>心羽</author><pubDate>Tue, 01 Mar 2011 08:16:00 GMT</pubDate><guid>http://www.cppblog.com/tdweng/articles/140916.html</guid><wfw:comment>http://www.cppblog.com/tdweng/comments/140916.html</wfw:comment><comments>http://www.cppblog.com/tdweng/articles/140916.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tdweng/comments/commentRss/140916.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tdweng/services/trackbacks/140916.html</trackback:ping><description><![CDATA[<a href="http://blog.csdn.net/FLandY1982/archive/2009/12/24/5070059.aspx">http://blog.csdn.net/FLandY1982/archive/2009/12/24/5070059.aspx</a>
<img src ="http://www.cppblog.com/tdweng/aggbug/140916.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tdweng/" target="_blank">心羽</a> 2011-03-01 16:16 <a href="http://www.cppblog.com/tdweng/articles/140916.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WINCE串口驱动PDD层代码简单分析 </title><link>http://www.cppblog.com/tdweng/articles/140915.html</link><dc:creator>心羽</dc:creator><author>心羽</author><pubDate>Tue, 01 Mar 2011 08:13:00 GMT</pubDate><guid>http://www.cppblog.com/tdweng/articles/140915.html</guid><wfw:comment>http://www.cppblog.com/tdweng/comments/140915.html</wfw:comment><comments>http://www.cppblog.com/tdweng/articles/140915.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tdweng/comments/commentRss/140915.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tdweng/services/trackbacks/140915.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1. PDD层代码简单分析PDD层的主要包含了以下2个类：CSerialPDDPowerUpCallback，&nbsp;CSerialPDD,&nbsp;下面简单的分析这2个类的作用。1.1 CSerialPDDPowerUpCallbackCSerialPDDPowerUpCallback&nbsp;类用于串口电源上电时的处理。在调用CSerialPDD::Init()后会创建...&nbsp;&nbsp;<a href='http://www.cppblog.com/tdweng/articles/140915.html'>阅读全文</a><img src ="http://www.cppblog.com/tdweng/aggbug/140915.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tdweng/" target="_blank">心羽</a> 2011-03-01 16:13 <a href="http://www.cppblog.com/tdweng/articles/140915.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WinCE下Touch Panel驱动介绍</title><link>http://www.cppblog.com/tdweng/articles/140894.html</link><dc:creator>心羽</dc:creator><author>心羽</author><pubDate>Tue, 01 Mar 2011 03:51:00 GMT</pubDate><guid>http://www.cppblog.com/tdweng/articles/140894.html</guid><wfw:comment>http://www.cppblog.com/tdweng/comments/140894.html</wfw:comment><comments>http://www.cppblog.com/tdweng/articles/140894.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tdweng/comments/commentRss/140894.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tdweng/services/trackbacks/140894.html</trackback:ping><description><![CDATA[<p>WinCE中的Touch Panel驱动是由GWES模块来管理的，Touch Panel驱动接收用户的触摸信息，并将其转换为屏幕上的坐标信息，传给GWES模块。在WinCE中，Touch Panel驱动是分层的，分为MDD层和PDD层，这和其他WinCE设备驱动是一样的。MDD层由微软提供，用户只需要实现MDD和PDD层间的DDSI函数就可以了。如图<span style="FONT-SIZE: small"><span lang=ZH-CN style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span lang=ZH-CN style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nanjianhui/EntryImages/20080804/未命名.JPG"></span></span></span></p>
<p>&nbsp;</p>
<p><br>&nbsp;&nbsp;&nbsp; WinCE中的GWES模块负责加载和管理Touch Panel驱动，Touch Panel的MDD层向上提供DDI接口，PDD层是针对硬件的实现，对MDD层提供DDSI接口。</p>
<p>&nbsp;</p>
<p>&nbsp; WinCE中的GWES模块负责加载和管理Touch Panel驱动，Touch Panel的MDD层向上提供DDI接口，PDD层是针对硬件的实现，对MDD层提供DDSI接口。</p>
<p>&nbsp;</p>
<p>1 Touch Panel驱动中的数据结构</p>
<p>(1) TOUCH_PANEL_SAMPLE_FLAGS</p>
<p>用于描述一个采样点的信息，这些信息被定义在一个枚举结构中：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; enum enumTouchPanelSampleFlags {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TouchSampleValidFlag = 0x01,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TouchSampleDownFlag = 0x02,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TouchSampleIsCalibratedFlag = 0x04,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TouchSamplePreviousDownFlag = 0x08,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TouchSampleIgnore = 0x10,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TouchSampleMouse = 0x40000000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TouchSampleValidFlag：一个有效的采样值</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TouchSampleDownFlag：第一次按触摸屏时，返回该flag</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TouchSampleIsCalibratedFlag：采样的x和y坐标值不需要再被校验了</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TouchSamplePreviousDownFlag：表示上一次采样状态是按在触摸屏上</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TouchSampleIgnore：忽略这次采样值</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TouchSampleMouse：预留</p>
<p>&nbsp;</p>
<p>(2) TPDC_CALIBRATION_POINT</p>
<p>用于描述一个校验点的相关信息，结构如下：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct TPDC_CALIBRATION_POINT {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT PointNumber;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT cDisplayWidth;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT cDisplayHeight;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT CalibrationX;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT CalibrationY;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PointNumber：校验点索引值，用于描述校验点在LCD上的位置</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0：中间</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1：左上</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2：左下</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3：右下</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4：右上</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cDisplayWidth：显示的宽度</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cDisplayHeight：显示的高度</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CalibrationX：校验点的x坐标值</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CalibrationY：校验点的y坐标值</p>
<p>&nbsp;</p>
<p>(3) TPDC_CALIBRATION_POINT_COUNT</p>
<p>用于描述需要校验的点的个数，结构如下：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct TPDC_CALIBRATION_POINT_COUNT {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DDI_TOUCH_PANEL_CALIBRATION_FLAGS flags;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT cCalibrationPoints;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; flags：一般为0</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cCalibrationPoints：需要校验的点的个数，一般是5</p>
<p>&nbsp;</p>
<p>(4) gIntrTouch和gIntrTouchChanged</p>
<p>这是两个被MDD层用到的中断，需要在PDD层中定义，如下：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD gIntrTouch&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = SYSINTR_NOP;</p>
<p>DWORD gIntrTouchChanged = SYSINTR_NOP;</p>
<p>gIntrTouch用于描述触摸屏中断，要和硬件的触摸屏中断相关联。</p>
<p>gIntrTouchChanged用于在触摸屏按下后，每隔一段时间进行一次采样，应该和硬件的一个定时器中断相关联。</p>
<p>这两个值应该在DdsiTouchPanelEnable(..)函数中和硬件中断关联，并在函数DdsiTouchPanelGetPoint(..)中根据情况清除相应的中断。</p>
<p>&nbsp;</p>
<p>2 MDD层API</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MDD为上层导出所需的Touch Panel驱动接口函数，上层通过这些函数可以完成对Touch Panel的操作，下面会介绍这些函数的功能。</p>
<p>(1) BOOL TouchPanelEnable(PFN_TOUCH_PANEL_CALLBACK pfnCallback):</p>
<p>使能Touch Panel设备，用于初始化Touch Panel。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pfnCallback：指向处理Touch Panel事件的回调函数</p>
<p>&nbsp;</p>
<p>(2) Void TouchPanelDisable(void):</p>
<p>禁用Touch Panel设备。</p>
<p>&nbsp;</p>
<p>(3) BOOL TouchPanelGetDeviceCaps(INT iIndex, LPVOID lpOutput )：</p>
<p>获得Touch Panel设备的相关信息。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iIndex：索引值</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TPDC_SAMPLE_RATE_ID：采样率信息</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TPDC_CALIBRATION_POINT_COUNT_ID：采样点个数信息</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TPDC_CALIBRATION_POINT_ID：采样点坐标信息</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpOutput：指向一个内存区域，用于存放获得的相关信息</p>
<p>&nbsp;</p>
<p>(4) VOID TouchPanelCalibrateAPoint(INT32 UncalX, INT32 UncalY, INT32* pCalX, INT32* pCalY)：</p>
<p>将输入的未经过校验的坐标信息转换成校验后的坐标信息。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UncalX：输入的X坐标</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UncalY：输入的Y坐标</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pCalX：校验后的X坐标</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pCalY： 校验后的Y坐标</p>
<p>&nbsp;</p>
<p>(5) VOID TouchPanelPowerHandler(BOOL bOff)：</p>
<p>Touch Panel的电源控制函数。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bOff：TRUE表示关闭电源，FALSE表示打开电源</p>
<p>&nbsp;</p>
<p>(6) BOOL TouchPanelReadCalibrationPoint(INT* pRawX, INT* pRawY)：</p>
<p>获得Touch Panel的坐标。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pRawX：触摸屏的X坐标</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PRawY：触摸屏的Y坐标</p>
<p>&nbsp;</p>
<p>(7) VOID TouchPanelReadCalibrationAbort(void)：</p>
<p>终止当前的校验。</p>
<p>&nbsp;</p>
<p>(8) VOID TouchPanelSetCalibration(INT32 cCalibrationPoints, INT32* pScreenXBuffer, INT32* pScreenYBuffer, INT32* pUncalXBuffer, INT32* pUncalYBuffer)：</p>
<p>校验函数。通过一组实际的触摸屏上采集的点坐标和相应的屏幕坐标计算校验系数。具体公式如下：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sx =&nbsp; A1*Tx + B1*Ty + C1</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sy =&nbsp; A2*Tx + B2*Ty + C2</p>
<p>&nbsp;&nbsp; 这里就是通过显示屏坐标和采样的触摸屏坐标计算A1,B1,C1,A2,B2,C2。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cCalibrationPoints：校验点的个数</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pScreenXBuffer：一组显示屏上的X坐标</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pScreenYBuffer：一组显示屏上的Y坐标</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pUncalXBuffer：一组触摸屏上采样的X坐标</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pUncalYBuffer：一组触摸屏上采样的Y坐标</p>
<p>&nbsp;</p>
<p>(9) BOOL TouchPanelSetMode(INT iIndex, LPVOID lpInput)：</p>
<p>设置Touch Panel的工作模式。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iIndex：索引模式</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TPSM_SAMPLERATE_HIGH：设置高采样率</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TPSM_SAMPLERATE_LOW：设置低采样率</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TPSM_PRIORITY_HIGH_ID：设置触摸屏的IST为高优先级</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TPSM_PRIORITY_NORMAL_ID：设置IST为正常优先级</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpInput：指向一块内存，其中包含相关信息。</p>
<p>&nbsp;</p>
<p>3 PDD层API</p>
<p>(1) LONG DdsiTouchPanelAttach(void)：</p>
<p>该函数在Touch Panel驱动的Dll被加载的时候调用</p>
<p>&nbsp;</p>
<p>(2) LONG DdsiTouchPanelDettach(void)：</p>
<p>该函数在Touch Panel驱动的Dll被卸载的时候调用</p>
<p>&nbsp;</p>
<p>(3) BOOL DdsiTouchPanelEnable(void)：</p>
<p>打开Touch Panel电源并做初始化。一般会在这里初始化一些信息，打开Touch Panel设备电源并做初始化。</p>
<p>&nbsp;</p>
<p>(4) VOID DdsiTouchPanelDisable(void)：</p>
<p>关闭Touch Panel设备。关闭Touch Panel电源并释放资源。</p>
<p>&nbsp;</p>
<p>(5) BOOL DdsiTouchPanelGetDeviceCaps(ULONG iIndex, LPVOID lpOutput)：</p>
<p>查询Touch Panel设备的相关信息</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iIndex：查询的索引值</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TPDC_SAMPLE_RATE_ID：查询采样率信息</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TPDC_CALIBRATION_POINT_ID：查询需要校验的点的坐标</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TPDC_CALIBRATION_POINT_COUNT_ID：查询用于校验的点的个数</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpOutput：根据iIndex值分别指向相关的信息</p>
<p>&nbsp;</p>
<p>(6) void DdsiTouchPanelGetPoint(TOUCH_PANEL_SAMPLE_FLAGS pTipState, PLONG pUnCalX, PLONG pUnCalY)：</p>
<p>获得Touch Panel上被按下的点的状态和坐标。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pTipState：当前触摸点的状态，比如无效点，有效点，被按下的点等。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pUnCalX：触摸点的X坐标</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pUnCalY：触摸点的Y坐标</p>
<p>&nbsp;</p>
<p>(7) VOID DdsiTouchPanelPowerHandler(BOOL bOff)：</p>
<p>设置Touch Panel的电源状态。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bOff：TRUE表示关闭电源，FALSE表示打开电源</p>
<p>&nbsp;</p>
<p>(8) BOOL DdsiTouchPanelSetMode(ULONG iIndex, LPVOID lpInput)：</p>
<p>设置Touch Panel工作模式。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iIndex：模式索引</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TPSM_SAMPLERATE_HIGH_ID：高采样率</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TPSM_SAMPLERATE_LOW_ID：低采样率</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpInput：指向包含相关信息的内存</p>
<p>&nbsp;</p>
<p>4 注册表设置</p>
<p>对于Touch Panel驱动来说，有些注册表项是需要配置的。具体如下：</p>
<p>&#8220;InputConfig&#8221;：WinCE系统输入配置。</p>
<p>Bit0表示键盘输入</p>
<p>Bit1表示Touch Panel输入</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bit2表示硬件按键输入</p>
<p>&#8220;DeviceName&#8221;：Touch Panel驱动的名字。</p>
<p>&#8220;MaxCalError&#8221;：Touch Panel的精确度配置。</p>
<p>&#8220;CalibrationData&#8221;：Touch Panel的校验值。第一次启动WinCE后，需要通过WinCE的触摸屏校验程序对Touch Panel进行校验。校验完成后，校验值会被写入注册表里面。</p>
<p>&nbsp;</p>
<p>下面是关于Touch Panel的注册表配置的例子：</p>
<p>[HKEY_LOCAL_MACHINE\ControlPanel]</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "InputConfig"=dword:3&nbsp;&nbsp;&nbsp; ;3 =&gt; keybd and touch screen</p>
<p>[HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\TOUCH]</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "DriverName"="touch.dll"</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "MaxCalError"=dword:8</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "CalibrationData"="446,671 36,191 38,1179 856,1161 862,169 "</p>
<p>&nbsp;</p>
<p>个人觉得，要想更好的理解Touch Panel驱动，还是需要去读读代码，基于WinCE6.0下，可以参考"\WINCE600\PLATFORM\H4SAMPLE\SRC\DRIVERS\TOUCH"下面的驱动，基于这个驱动开发自己的驱动会比较方便。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Windows CE下触摸屏驱动实现的分析<br>Analysis of Touch Panel Driver Realization Based on Windows CE OS</p>
<p>刘林辉1&nbsp;&nbsp;&nbsp; 张 芬2</p>
<p>Liu,LinHui1&nbsp;&nbsp;&nbsp; Zhang,Fen2</p>
<p>（1 长沙理工大学能源与动力工程学院，湖南 长沙，410077；2 华中科技大学机械科学与工程学院，湖北 武汉，430074）</p>
<p>摘要：本文介绍了Windows CE操作系统的触摸屏驱动程序模型，详细阐述嵌入式系统中电阻式触摸屏的Windows CE驱动程序的设计和实现方法。</p>
<p>关键词：触摸屏，Windows CE</p>
<p>中图分类号：TP316</p>
<p>文献标识码：A</p>
<p>Abstract：This 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.</p>
<p>Keyword：Touch Panel，Windows CE</p>
<p>1. 前言<br>触摸屏是嵌入式设备中常用的计算机输入设备，它可使操作简单直观，人人都会使用，这一点无论是键盘还是鼠标都无法与其相比。在手机、PDA等手持产品及公共服务设备中大量采用触摸屏。触摸屏分为电阻式、电容式、表面声波式等多种，电阻式触摸屏是目前应用比较广泛的一种，有四线、五线、七线等几种。本文将分析Windows CE操作系统下的触摸屏驱动程序模型及实现方法。</p>
<p>2. Windows CE触摸屏驱动程序模型<br>在Windows CE操作系统中触摸屏驱动是一种分层驱动。其驱动模型如图1所示。上层是模型设备驱动程序（Model Device Driver, MDD），下层是依赖平台的驱动程序（Platform Dependent Driver, PDD）。MDD通常无需修改直接使用，MDD链接PDD层并定义它希望调用的函数接口：设备驱动程序提供器接口（Device Driver Service Provider Interface, DDSI）。同时MDD把不同的函数集提供给操作系统，这些函数叫做设备驱动程序接口（Device Driver Interface, DDI），这部分为也就是我们通常驱动需要实现的部分。</p>
<p>3 Windows CE的触摸屏驱动程序接口</p>
<p>Windows CE的触摸屏驱动链接了tch_cal.lib和tchmdd.lib两个静态链接库。触摸屏驱动由GWES加载，GWES通过DDI调用驱动程序获取设备状态，设置驱动功能等，而驱动本身通过DDSI直接获得硬件信息来确定当前触摸屏的状态。</p>
<p>Windows CE触摸屏驱动要求的DDI接口包括：TouchPanelGetDeviceCaps、TouchPanelEnable、TouchPanelDisable、TouchPanelSetMode、TouchPanelReadCalibrationPoint、TouchPanelReadCalibrationAbort、TouchPanelSetCalibration、TouchPanelCalibrateAPoint、TouchPanelPowerHandler。</p>
<p>Windows CE触摸屏驱动要求的DDSI接口包括：DdsiTouchPanelAttach、DdsiTouchPanelDetach、DdsiTouchPanelDisable、DdsiTouchPanelEnable、DdsiTouchPanelGetDeviceCaps、DdsiTouchPanelGetPoint、DdsiTouchPanelPowerHandler。</p>
<p>4 Windows CE的触摸屏数据采集<br>Windows CE触摸屏驱动程序采用中断方式对触摸笔的按下状态进行检测，如果检测到触摸笔按下将产生中断并触发一个事件通知一个工作线程开始采集数据。同时，驱动将打开一个硬件定时器，只要检测到触摸笔仍然在按下状态将定时触发同一个事件通知工作线程采集数据，直到触摸笔抬起后关闭该定时器，并重新检测按下状态。驱动中采用了触摸屏中断以及定时器中断两个中断源，不仅可以监控触摸笔按下和抬起状态，而且可以检测触摸笔按下时的拖动轨迹。</p>
<p>触摸屏驱动在初始化过程调用TouchPanelEnable函数使能触摸屏。该函数调用的DDSI函数为：DdsiTouchPanelEnable和DdsiTouchPanelDisable。该函数实现如下 内容：</p>
<p>1) 创建事件hTouchPanelEvent和hCalibrationSampleAvailable。hTouchPanelEvent事件在正常状态下当有触摸笔按下或者按下后需要定时采集数据时被触发。而hCalibrationSampleAvailable事件在校准状态下当有校准数据输入时被触发；</p>
<p>2) 检查并初始化所需的中断gIntrTouch（触摸屏中断）和gIntrTouchChanged（定时器中断），并将中断gIntrTouch、gIntrTouchChanged关联到事件hTouchPanelEvent。当gIntrTouch，gIntrTouchChanged中断产生时将触发hTouchPanelEvent事件；</p>
<p>3) 创建一个ISR线程TouchPanelpISR。TouchPanelpISR用于等待和处理触摸屏事件hTouchPanelEvent，它是整个驱动程序中唯一的事件源。</p>
<p>TouchPanelpISR函数是实现触摸屏数据采集关键函数，它实现的内容为：</p>
<p>1) 等待循环，用于接收hTouchPanelEvent事件，并构成函数的主体；</p>
<p>2) 通过调用DdsiTouchPanelGetPoint函数获取当前触摸屏位置和状态信息；</p>
<p>3) 在获取有效数据且在校准状态下，收集并提交按下的位置信息；</p>
<p>4) 在正常状态下，校准数据，并检查校准后数据的有效性；</p>
<p>5) 最后调用由GWES传入的回调函数，提交位置信息和状态信息。</p>
<p>因此，在触摸屏驱动程序中DdsiTouchPanelEnable、DdsiTouchPanelDisable和DdsiTouchPanelGetPoint三个DDSI接口函数是驱动实现的关键所在。</p>
<p>在DdsiTouchPanelEnable和DdsiTouchPanelDisable函数中分别打开和关闭触摸屏硬件，这两个函数其实可以不真正操作硬件，而只是实现软件上的控制，但是为了降低功耗最好在DdsiTouchPanelDisable中将触摸屏控制器电源关闭并在DdsiTouchPanelEnable函数中打开。</p>
<p>在DdsiTouchPanelGetPoint函数中实现对触摸屏数据的采样。从上面的分析得知MDD通过检测hTouchPanelEvent和hCalibrationSampleAvailable事件控制采样，这两个事件被触发都将调用该函数。而这两个事件触发条件有两个：</p>
<p>1) 触摸笔按下时产生触摸屏中断gIntrTouch时触发；</p>
<p>2) 触摸笔按下后，定时器被打开，定时器将定时产生中断gIntrTouchChanged，并触发事件，直到触摸笔抬起为止。</p>
<p>因此该函数不仅需要对触摸屏数据采样，而且需要对触发条件进行状态控制，其流程如图2所示。图中定义了三个变量，它们分别为：</p>
<p>1) TouchIrq为静态变量或全局变量，且初始值为TRUE，该变量必须在触摸屏按下并产生触摸屏中断时设置为FALSE；</p>
<p>2) InterruptType为静态变量或全局变量，且初始值为SYSINTR_NOP，当在处理触摸屏中断时设置为SYSINTR_TOUCH，在处理定时器中断时设置为SYSINTR_TOUCH_CHANGED，其余设置为SYSINTR_NOP，且在处理完毕后必须将其作为参数传入InterruptDone函数以清除中断；</p>
<p>3) g_NextExpectedInterrupt为静态变量或全局变量，该变量表示下一个希望产生的中断，初始状态为PEN_DOWN，也就是触摸笔在抬起状态，因此希望下一个产生的中断为PEN_DOWN。当触摸屏中断产生以及定时器中断产生时该变量为PEN_UP_OR_TIMER，也就是下一个可能产生的状态为触摸笔抬起状态或者触摸笔按下但定时器中断产生。</p>
<p>DdsiTouchPanelGetPoint函数一开始从触摸笔抬起状态开始执行，此时TouchIrq等于TRUE。如果此时触摸笔按下，将设置TouchIrq为FALSE，表示本次采样是由于触摸屏中断产生并设置下一次调用由定时器产生。然后设置InterruptType状态为SYSINTR_TOUCH，接着开始采集数据并设置g_NextExpectedInterrupt变量为PEN_UP_OR_TIMER，表示下一次产生的中断为定时器中断。接着判断在触摸笔按下状态（g_NextExpectedInterrupt等于PEN_UP_OR_TIMER）下触摸笔是否抬起，如果抬起则设置g_NextExpectedInterrupt为PEN_DOWN恢复到抬起状态。最后通过将InterruptType作为参数传入InterruptDone函数以清除中断。当触摸笔按下，并产生定时器中断时，TouchIrq等于FALSE，此时InterruptType被设置为SYSINTR_TOUCH_CHANGED，其余的动作基本和上面的流程一致。</p>
<p><br>5 Windows CE下的触摸屏校准<br>电阻触摸屏需要校准。应用程序需要一些参考值，以便将接收到的触摸屏坐标数据转换成高层软件所需的屏幕坐标。理想情况下校准程序只要在产品初次加电测试过程中运行一次就可以了，参考值被存储在非易失性存储器中。在理想情况下只需两组原始数据，即在屏幕对角读取的最小和最大值。而在实际应用中，因为许多电阻触摸屏存在显著的非线性，因此如果在最小和最大值之间简单的插入位置数值会导致驱动程序非常的不精确。</p>
<p>在Windows CE中通过在函数DdsiTouchPanelGetDeviceCaps 中设置校准点的个数，在TouchDriverCalibrationPointGet中获取每个校准点的屏幕坐标。常用的校准点数量为5。校准UI将在校准点坐标处相应显示一个十字叉，用户需要精确地在该十字叉位置按下触摸屏，驱动通过TouchPanelReadCalibrationPoint函数读取相应的触摸屏坐标值，然后开始下一个校准点。循环设定的次数后，将采集到的触摸屏坐标值和校准点屏幕坐标送到TouchPanelSetCalibration函数中进行处理。该函数将产生校准基准参数。</p>
<p>TouchPanelSetCalibration函数执行的动作是一套数学算法，具体内容为:</p>
<p>在触摸屏数据与其位置偏移关系且屏幕像素与其位置偏移关系同为线性关系假设情况下，触摸屏返回的位置信息与像素位置信息之间成2D坐标变换关系。则对于触摸屏按下点的触摸屏坐标(Tx,Ty)与其在显示设备位置关系上匹配的点的屏幕坐标(Sx,Sy)之间的转换关系，可以通过下述坐标变换表示：</p>
<p>Sx =&nbsp; A1*Tx + B1*Ty + C1</p>
<p>Sy =&nbsp; A2*Tx + B2*Ty + C2</p>
<p>TouchPanelSetCalibration的具体工作就是通过校准的动作获取的屏幕坐标和触摸屏坐标TouchCoordinate来确定A1，B1，C1和A2, B2, C2。</p>
<p>6. 结束语<br>本文作者的创新点：从分析嵌入式Windows CE操作系统中触摸屏驱动程序的模型及实现方法的角度深入剖析了Windows CE中触摸屏数据采集和校准的执行流程，对于类似系统的驱动开发具有一定的借鉴性。</p>
<p>参考文献：</p>
<p>[1] Paul Kovitz. 电阻式触摸屏结构和实现原理，夏普公司，2003</p>
<p><br>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/zhongnanjun_3/archive/2008/11/11/3274020.aspx">http://blog.csdn.net/zhongnanjun_3/archive/2008/11/11/3274020.aspx</a></p>
<img src ="http://www.cppblog.com/tdweng/aggbug/140894.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tdweng/" target="_blank">心羽</a> 2011-03-01 11:51 <a href="http://www.cppblog.com/tdweng/articles/140894.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>wince下的触摸屏驱动分析(基于2410)  </title><link>http://www.cppblog.com/tdweng/articles/140893.html</link><dc:creator>心羽</dc:creator><author>心羽</author><pubDate>Tue, 01 Mar 2011 03:50:00 GMT</pubDate><guid>http://www.cppblog.com/tdweng/articles/140893.html</guid><wfw:comment>http://www.cppblog.com/tdweng/comments/140893.html</wfw:comment><comments>http://www.cppblog.com/tdweng/articles/140893.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tdweng/comments/commentRss/140893.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tdweng/services/trackbacks/140893.html</trackback:ping><description><![CDATA[<div style="FONT-SIZE: 14px; LINE-HEIGHT: 160%">
<p><font face="Verdana, Arial, Helvetica, 宋体, sans-serif" color=#25435b><span style="LINE-HEIGHT: 25px"><span style="LINE-HEIGHT: 22px">
<p><span style="LINE-HEIGHT: 25px; FONT-FAMILY: Verdana, Arial, Helvetica, 宋体, sans-serif"><font style="LINE-HEIGHT: 25px" color=#ff0000>转载请注明出处</font></span></p>
<p><span style="LINE-HEIGHT: 25px; FONT-FAMILY: Verdana, Arial, Helvetica, 宋体, sans-serif"><font style="LINE-HEIGHT: 25px" color=#ff0000>作者:小马</font></span></p>
<p><span style="LINE-HEIGHT: 25px; FONT-FAMILY: Verdana, Arial, Helvetica, 宋体, sans-serif"><font style="LINE-HEIGHT: 25px" color=#ff0000><br></font></span></p>
</span></span></font>
<p>&nbsp;</p>
<p>前段时间移植 6.0 BSP，目前已移植到触摸屏部分了. 移植过程中学到了不少东西. 由其是关于触摸屏这部分, 掌握了很多以前不会的东西. 觉得有必要把这些知识点整理一下.&nbsp;</p>
<p><br></p>
<p><span style="FONT-SIZE: large"><strong><font size=4>一 硬件部分</font></strong></span></p>
<p><font size=4><br></font></p>
<p>硬件上的原理不是本文的重点，只讲一下大概的原理(主要是我也只知道大概的原理, 毕竟咱不是搞硬件的. 嘻嘻!)</p>
<p><br></p>
<p>我移植用的这个屏是320*240 的TFT屏, 四线电阻式触屏. 这种触屏的原理是由两个电阻层组成, 一个实现X位置的测量，一个用于Y位置上的测量. 简单来说，就是当用触笔按下屏幕时，两个电阻层接触, 电阻发生变化，然后在X Y方向上产生信号, 这个信号是电压信号， 再经过CPU内部分AD转换为坐标值. 这个原理有点像高中物理课用的滑动电阻，有一个最大上限，滑动到不同的地方，阻值不同. 2410本身集成了touch的控制器，通过简单的配置和读取相关的寄存器，就可以实现触摸屏的操作.&nbsp;</p>
<p><br></p>
<p><span style="FONT-SIZE: large; LINE-HEIGHT: 28px"><strong><font size=4>二 驱动部分</font></strong></span></p>
<p>Wince下的touch驱动跟很多其它的驱动一样, 是分层的, 有MDD 和PDD两层. MDD层被系统隐藏起来, 一般不用我们来修改. 而我们真正关心的是PDD 层. 也就是要由开发者来修改的这一层.&nbsp;</p>
<p><br></p>
<p>分析touch驱动时，以我最近刚刚移植到一个基于2410的板子上的6.0的BSP包的触屏驱动为例.到<span style="COLOR: red">C:\WINCE600\PLATFORM\DEVICEEMULATOR\SRC\DRIVERS\TOUCH</span>下. 找到s3c2410x_touch.cpp文件. 这里面正是PDD层的实现代码. 容易发现这里面的函数分为两类，一类是以TSP开头的函数，一类是以DDSI开头的函数. TSP开头的函数为内部私有的函数，是被DDSI调用的, 而DDSI开头的函数则是对外的接口, 也就是被MDD层的函数调用的接口.&nbsp;</p>
<p><br></p>
<p>DdsiTouchPanelEnable是首先被调用的一个外部接口, 它的实现可参见源程序, &nbsp;它主</p>
<p>要做了下面几个事情:</p>
<p>1 通过调用TSP_VirtualAlloc函数为驱动所用的IO,中断等硬件中断分配内存空间.&nbsp;</p>
<p><br></p>
<p>2 通过调用KernelIoControl向系统申请两个中断，如果申请成功，赋予相应的逻辑中断号. KernelIoControl向底层是调用OEMIoControl函数, OEMIoControl根据KernelIoControl传进来的IOCTL代码，做相应的操作,比如这里, IOCTL是IOCTL_HAL_REQUEST_SYSINTR， 它是向内核申请一个物理中断和逻辑中断的映射.&nbsp;</p>
<p><br></p>
<p>3 通过调用TSP_PowerOn来初始化中断控制器，ADC寄存器，定时器等, 在TSP_PowerOn的实现中，有几点要说明一下:</p>
<p>ADCDLY 这个值在不同的模式下意义不同, 因为前面通过ADCTSC已经配置为wait for interrupt mode, 所以这个值的意义和你的触笔按下时, &nbsp;从产生中断信号到开始自动转换X,Y时的时间间隔是相关的，它的单位是ms</p>
<p><br></p>
<p>v_pPWMregs-&gt;TCNTB3 &nbsp;= g_timer3_sampleticks</p>
<p>TCNTB3是timer3的count buffer, 当定时器启动时, 0，这个值以一个设置好的频率递减，直到减到0， 这时会产生一个定时器中断. 这个有什么用呢. 要理解它，得知道触摸屏在中断模式下是如何工作的.&nbsp;</p>
<p><br></p>
<p>当我们按下的触摸屏时，会产生一个ADC的中断, 同时我们的驱动还会启动一个定时器, 这个定时器触发一个事件做数据采集, 在我们的手或触笔抬起来前，这个定时器不断的触发采集事件，直到它被关闭, 而它什么时候会被关闭呢，就是在触笔的抬起来时. 下面截取的代码很好的说明的这个原理:</p>
<p>if ( (v_pADCregs-&gt;ADCDAT0 &amp; (1 &lt;&lt; 15)) |(v_pADCregs-&gt;ADCDAT1 &amp; (1 &lt;&lt; 15)) )</p>
<p>{</p>
<p><span style="WHITE-SPACE: pre"></span>bTSP_DownFlag = FALSE;</p>
<p><span style="WHITE-SPACE: pre"></span>DEBUGMSG(ZONE_TIPSTATE, (TEXT("up\r\n")));</p>
<p><span style="WHITE-SPACE: pre"></span>v_pADCregs-&gt;ADCTSC &amp;= 0xff;</p>
<p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;*pUncalX = x;</p>
<p><span style="WHITE-SPACE: pre"></span>*pUncalY = y;</p>
<p><span style="WHITE-SPACE: pre"></span>TSP_SampleStop();</p>
<p>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&#8230;&#8230;</p>
<p>}</p>
<p>上面的代码，if判断的正是是否抬起.&nbsp;</p>
<p>而g_timer3_sampleticks的值是这样计算出来的.&nbsp;</p>
<p>g_timer3_freq &nbsp; &nbsp; &nbsp; &nbsp;= (g_s3c2410_pclk / TIMER3_DIVIDER);</p>
<p>g_timer3_sampleticks = (g_timer3_freq / TSP_SAMPLE_RATE_LOW);&nbsp;</p>
<p>TIMER3_DIVIDER 的值是2, TSP_SAMPLE_RATE_LOW的值是100, 由</p>
<p>v_pPWMregs-&gt;TCFG1 &nbsp;&amp;= ~(0xf &lt;&lt; 12); &nbsp;</p>
<p>v_pPWMregs-&gt;TCFG1 &nbsp;|= &nbsp;(0 &nbsp; &lt;&lt; 12);&nbsp;</p>
<p>可知定时器1/2分频, 所以，很容易计算出，所设置的定时器是每10ms产生一次定时器中断</p>
<p>而触摸屏中断是在你按下和抬起时产生的.&nbsp;</p>
<p><br></p>
<p>DdsiTouchPanelGetPoint是采样的主要实现函数，当MDD检测到中断事件发生时，该函数会被调用. 触摸屏的中断是SYSINTR_TOUCH, 而定时器的中断是SYSINTR_TOUCH_CHANGED&nbsp;</p>
<p>该函数用if else分别处理两种中断, 如下:</p>
<p>if (v_pINTregs-&gt;SUBSRCPND &amp; (1&lt;&lt;IRQ_SUB_TC)) &nbsp; &nbsp; &nbsp;/* 触摸屏中断*/</p>
<p>{</p>
<p>&nbsp;&nbsp; &nbsp;&#8230;&#8230;</p>
<p>}</p>
<p><br></p>
<p>else &nbsp; &nbsp; &nbsp; &nbsp;/*定时器中断<span style="WHITE-SPACE: pre"> </span>*/</p>
<p>{</p>
<p>}</p>
<p>DdsiTouchPanelGetPoint函数的实现代码中，调用了两个很重要的函数TSP_TransXY和TSP_GetXY</p>
<p>需要说明的是，这两个函数的实现跟LCD本身的分辨率是没有关系的.</p>
<p>TSP_GetXY用来获到AD采样值，TSP_TransXY把它转化为屏上的坐标. 我移植touch驱动时，遇到过点屏幕上面，下面有反应，或者点左上角，右上角有反应等类似的问题, 都是因为这两个函数没实现好.</p>
<p><br></p>
<p>先来看TSP_GetXY函数.它的实现如下:</p>
<p>TSP_GetXY(INT *px, INT *py)</p>
<p>{</p>
<p><span style="WHITE-SPACE: pre"></span>INT i;</p>
<p><span style="WHITE-SPACE: pre"></span>INT xsum, ysum;</p>
<p><span style="WHITE-SPACE: pre"></span>INT x, y;</p>
<p><span style="WHITE-SPACE: pre"></span>INT dx, dy;</p>
<p><span style="WHITE-SPACE: pre"></span>xsum = ysum = 0;</p>
<p><span style="WHITE-SPACE: pre"></span>for (i = 0; i &lt; TSP_SAMPLE_NUM; i++)</p>
<p><span style="WHITE-SPACE: pre"></span>{</p>
<p><span style="WHITE-SPACE: pre"></span>v_pADCregs-&gt;ADCTSC = &nbsp; (0 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;8) | &nbsp; &nbsp; &nbsp; &nbsp;/* UD_Sen*/</p>
<p><span style="WHITE-SPACE: pre"></span>(1 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;7) | &nbsp; &nbsp; &nbsp; &nbsp;/* YMON &nbsp;1 (YM = GND)*/</p>
<p><span style="WHITE-SPACE: pre"></span>(1 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;6) | &nbsp; &nbsp; &nbsp; &nbsp;/* nYPON 1 (YP Connected AIN[n])*/</p>
<p><span style="WHITE-SPACE: pre"></span>(0 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;5) | &nbsp; &nbsp; &nbsp; &nbsp;/* XMON &nbsp;0 (XM = Z)*/</p>
<p><span style="WHITE-SPACE: pre"></span>(1 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;4) | &nbsp; &nbsp; &nbsp; &nbsp;/* nXPON 1 (XP = AIN[7])*/</p>
<p><span style="WHITE-SPACE: pre"></span>(1 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;3) | &nbsp; &nbsp; &nbsp; &nbsp;/* Pull Up Enable*/</p>
<p><span style="WHITE-SPACE: pre"></span>(1 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;2) | &nbsp; &nbsp; &nbsp; &nbsp;/* Auto ADC Conversion Mode*/</p>
<p><span style="WHITE-SPACE: pre"></span>(0 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;0); &nbsp; &nbsp; &nbsp; &nbsp; /* No Operation Mode*/</p>
<p><br></p>
<p><span style="WHITE-SPACE: pre"></span>v_pADCregs-&gt;ADCCON |= (1 &lt;&lt; 0); &nbsp;/* Start Auto conversion*/</p>
<p><br></p>
<p><span style="WHITE-SPACE: pre"></span>while (v_pADCregs-&gt;ADCCON &amp; 0x1); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* check if Enable_start is low*/</p>
<p><span style="WHITE-SPACE: pre"></span>while (!(v_pADCregs-&gt;ADCCON &amp; (1 &lt;&lt; 15))); &nbsp; &nbsp; &nbsp;/* Check ECFLG*/</p>
<p><br></p>
<p><span style="WHITE-SPACE: pre"></span>y = (0x3ff &amp; v_pADCregs-&gt;ADCDAT1);</p>
<p><span style="WHITE-SPACE: pre"></span>x = (0x3ff &amp; v_pADCregs-&gt;ADCDAT0);</p>
<p><span style="WHITE-SPACE: pre"></span>xsum += x;</p>
<p><span style="WHITE-SPACE: pre"></span>ysum += y;</p>
<p><span style="WHITE-SPACE: pre"></span>}</p>
<p><span style="WHITE-SPACE: pre"></span>*px = xsum / TSP_SAMPLE_NUM;</p>
<p><span style="WHITE-SPACE: pre"></span>*py = ysum / TSP_SAMPLE_NUM;</p>
<p><br></p>
<p><span style="WHITE-SPACE: pre"></span>v_pADCregs-&gt;ADCTSC = &nbsp; &nbsp;(1 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;8) | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* UD_Sen*/</p>
<p><span style="WHITE-SPACE: pre"></span>(1 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;7) | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* YMON &nbsp;1 (YM = GND)*/</p>
<p><span style="WHITE-SPACE: pre"></span>(1 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;6) | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* nYPON 1 (YP Connected AIN[n])*/</p>
<p><span style="WHITE-SPACE: pre"></span>(0 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;5) | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* XMON &nbsp;0 (XM = Z)*/</p>
<p><span style="WHITE-SPACE: pre"></span>(1 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;4) | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* nXPON 1 (XP = AIN[7])*/</p>
<p><span style="WHITE-SPACE: pre"></span>(0 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;3) | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* Pull Up Disable*/</p>
<p><span style="WHITE-SPACE: pre"></span>(0 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;2) | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* Normal ADC Conversion Mode*/</p>
<p><span style="WHITE-SPACE: pre"></span>(3 &nbsp; &nbsp; &nbsp;&lt;&lt; &nbsp;0); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* Waiting Interrupt*/</p>
<p><br></p>
<p><span style="WHITE-SPACE: pre"></span>dx = (*px &gt; x) ? (*px - x) : (x - *px);</p>
<p><span style="WHITE-SPACE: pre"></span>dy = (*py &gt; y) ? (*py - y) : (y - *py);</p>
<p><br></p>
<p><span style="WHITE-SPACE: pre"></span>return((dx &gt; TSP_INVALIDLIMIT || dy &gt; TSP_INVALIDLIMIT) ? FALSE : TRUE);</p>
<p>}</p>
<p><br></p>
<p>关于这个函数有几点要说明.&nbsp;</p>
<p>根据2410的手册, ADCDAT0 保存是X方向上采样的结果, &nbsp;ADCDAT1 保存是Y方向上采样的结果, &nbsp;所以, 我们看到下面的两行代码</p>
<p>y = (0x3ff &amp; v_pADCregs-&gt;ADCDAT1);</p>
<p>x = (0x3ff &amp; v_pADCregs-&gt;ADCDAT0);</p>
<p>与上0x3ff, 是因为, ADCDAT寄存器只用了前面 10位来保存AD采样的结果, 而这和2410内部的AD模块只有10位精度是相一致的.所以，AD转换后的最大值不会超过1024-1.&nbsp;</p>
<p>当然上在那种计算方法并不是绝对的 ， 根据硬件构造的不同, 比如有可能你x方向的坐标值和采样值成反比，就要按下面的方式计算:</p>
<p>x = 0x3ff - (0x3ff &amp; v_pADCregs-&gt;ADCDAT0);</p>
<p><br></p>
<p>再看TSP_TransXY函数. 我移植的版本的实现如下:</p>
<p>PRIVATE void</p>
<p>TSP_TransXY(INT *px, INT *py)</p>
<p>{</p>
<p><span style="WHITE-SPACE: pre"></span>*px = (*px &gt;= TSP_MAXX) ? (TSP_MAXX) : *px;</p>
<p><span style="WHITE-SPACE: pre"></span>*py = (*py &gt;= TSP_MAXY) ? (TSP_MAXY) : *py;</p>
<p><br></p>
<p><span style="WHITE-SPACE: pre"></span>*px = (*px - TSP_MINX);</p>
<p><span style="WHITE-SPACE: pre"></span>*py = (*py - TSP_MINY);</p>
<p><br></p>
<p><span style="WHITE-SPACE: pre"></span>*px = (*px &gt;= 0) ? *px : 0;</p>
<p><span style="WHITE-SPACE: pre"></span>*py = (*py &gt;= 0) ? *py : 0;</p>
<p><br></p>
<p><span style="WHITE-SPACE: pre"></span>*px = *px * TSP_LCDY / (TSP_MAXX - TSP_MINX);</p>
<p><span style="WHITE-SPACE: pre"></span>*py = *py * TSP_LCDX / (TSP_MAXY - TSP_MINY);</p>
<p><br></p>
<p><span style="WHITE-SPACE: pre"></span>*px = (*px &gt;= TSP_LCDY)? (TSP_LCDY - 1) : *px;</p>
<p><span style="WHITE-SPACE: pre"></span>*py = (*py &gt;= TSP_LCDX)? (TSP_LCDX - 1) : *py;</p>
<p><br></p>
<p><span style="WHITE-SPACE: pre"></span>*px = TSP_LCDY - *px - 1;</p>
<p><span style="WHITE-SPACE: pre"></span>*py = TSP_LCDX - *py - 1;</p>
<p><br></p>
<p>}</p>
<p>这个实现是我在模拟器的实现代码基础上修改的. 这个函数计算X,Y的坐标用的是一个公式，至于这个公式是怎么来的，我就不太清楚了. 只说明一点.</p>
<p>#define TSP_MINX<span style="WHITE-SPACE: pre"> </span>88</p>
<p>#define TSP_MINY<span style="WHITE-SPACE: pre"> </span>84</p>
<p><br></p>
<p>#define TSP_MAXX<span style="WHITE-SPACE: pre"> </span>952</p>
<p>#define TSP_MAXY<span style="WHITE-SPACE: pre"> </span>996</p>
<p>上面四个值是定义X+, X-, Y+, Y-四个有效的采样值, 理论上应该是0和1023(10 bit ADC), 但实际肯定有偏差，准确来讲, 换了不同的硬件平台，这四个值应该是要重新测过的. 我就直接沿用原BSP中的值了.&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;</p>
</div>
<div id=miniAd style="DISPLAY: none; PADDING-TOP: 20px"></div>
<script>
function showMiniAd(){
var vn = "tinfo";
var dataURL = "http://t.sohu.com/third/user.jsp?passport="+window._xpt+"&vn="+vn;
new LinkFile(dataURL, {
type: 'script',
noCache: false,
callBack: {
variable: vn,
onLoad: function(){
var data = eval("(" + vn + ")")
if(data != null && data.status == 1){
var userLink = data.url;
//var userLink = data.icon;
$('miniAd').innerHTML = "<font style='font-size:14px;font-weight:bold;color:#e30000;'>我正在玩搜狐微博，快来&#8220;关注&#8221;我，了解我的最新动态吧。</font><br/>
<a href='"+userLink+"' href_cetemp='"+userLink+"' target='_blank' onmousedown=\"CA.q('tmini_entry_promotion');\">"+userLink+"</a>";
$('miniAd').show();
}
},
onFailure: function(){}
}});
}
showMiniAd();
</script>
<img src ="http://www.cppblog.com/tdweng/aggbug/140893.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tdweng/" target="_blank">心羽</a> 2011-03-01 11:50 <a href="http://www.cppblog.com/tdweng/articles/140893.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WinCE 6.0中断驱动程序分析（电源例子）</title><link>http://www.cppblog.com/tdweng/articles/140884.html</link><dc:creator>心羽</dc:creator><author>心羽</author><pubDate>Tue, 01 Mar 2011 02:55:00 GMT</pubDate><guid>http://www.cppblog.com/tdweng/articles/140884.html</guid><wfw:comment>http://www.cppblog.com/tdweng/comments/140884.html</wfw:comment><comments>http://www.cppblog.com/tdweng/articles/140884.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tdweng/comments/commentRss/140884.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tdweng/services/trackbacks/140884.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Windows Embedded CE 6.0的中断处理过程主要分为两部分：    &nbsp;中断服务例程（ISR）：处于内核中的低级处理程序，中断发生时首先被调用。    中断服务线程（IST）：处于驱动或者应用中的中断处理线程，由系统调度，完成大部分的中断处理工作。 ISR的实现在OAL（OEM适配层）中，它只处理最低级的中断响应，通常是获取IRQ和SYSINTR并设置M...&nbsp;&nbsp;<a href='http://www.cppblog.com/tdweng/articles/140884.html'>阅读全文</a><img src ="http://www.cppblog.com/tdweng/aggbug/140884.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tdweng/" target="_blank">心羽</a> 2011-03-01 10:55 <a href="http://www.cppblog.com/tdweng/articles/140884.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>常用宏</title><link>http://www.cppblog.com/tdweng/articles/140818.html</link><dc:creator>心羽</dc:creator><author>心羽</author><pubDate>Mon, 28 Feb 2011 08:26:00 GMT</pubDate><guid>http://www.cppblog.com/tdweng/articles/140818.html</guid><wfw:comment>http://www.cppblog.com/tdweng/comments/140818.html</wfw:comment><comments>http://www.cppblog.com/tdweng/articles/140818.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tdweng/comments/commentRss/140818.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tdweng/services/trackbacks/140818.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 14pt"><span style="COLOR: red"><strong>UNREFERENCED_PARAMETER的作用</strong></span><br>我们从 UNREFERENCED_PARAMETER 开始吧。这个宏在 winnt.h 中定义如下： <br>#define UNREFERENCED_PARAMETER(P) (P)<br>　　换句话说 UNREFERENCED_PARAMETER 展开传递的参数或表达式。其目的是避免编译器关于未引用参数的警告。许多程序员，包括我在内，喜欢用最高级别的警告 Level 4（/W4）进行编译。Level 4 属于&#8220;能被安全忽略的事件&#8221;的范畴。虽然它们可能使你难堪，但很少破坏你的代码。例如，在你的程序中可能会有这样一些代码行：</p>
<p style="FONT-SIZE: 14pt">int x=1;<br>　　但你从没用到过 x。也许这一行是你以前使用 x 时留下来的，只删除了使用它的代码，而忘了删除这个变量。Warning Level 4 能找到这些小麻烦。所以，为什么不让编译器帮助你完成可能是最高级别的专业化呢？用Level 4 编译是展示你工作态度的一种方式。如果你为公众使用者编写库，Level 4 则是社交礼节上需要的。你不想强迫你的开发人员使用低级选项清洁地编译他们的代码。<br>　　问题是，Level 4 实在是太过于注意细节，在 Level 4 上，编译器连未引用参数这样无伤大雅的事情也要抱怨（当然，除非你真的有意使用这个参数，这时便相安无事）。假设你有一个函数带来两个参数，但你只使用其中一个：</p>
<p style="FONT-SIZE: 14pt">int SomeFunction(int arg1, int arg2){&nbsp;&nbsp;&nbsp;&nbsp; return arg1+5;}<br>使用 /W4，编译器抱怨：</p>
<p style="FONT-SIZE: 14pt">&#8220;warning C4100: ''arg2'' : unreferenced formal parameter.&#8221;<br>为了骗过编译器，你可以加上 UNREFERENCED_PARAMETER(arg2)。现在编译器在编译你的引用 arg2 的函数时便会住口。并且由于语句：</p>
<p style="FONT-SIZE: 14pt">arg2;<br>实际上不做任何事情，编译器不会为之产生任何代码，所以在空间和性能上不会有任何损失。</p>
<p style="FONT-SIZE: 14pt">　　细心的人可能会问：既然你不使用 arg2，那当初为何要声明它呢？通常是因为你实现某个函数以满足某些API固有的署名需要，例如，MFC的 OnSize 处理例程的署名必须要像下面这样：</p>
<p style="FONT-SIZE: 14pt">void OnSize(UINT nType, int cx, int cy);<br>　　这里 cx/cy 是窗口新的宽/高，nType 是一个类似 SIZE_MAXIMIZED 或 SIZE_RESTORED 这样的编码，表示窗口是否最大化或是常规大小。一般你不会在意 nType，只会关注 cx 和 xy。所以如果你想用 /W4，则必须使用 UNREFERENCED_PARAMETER(nType)。OnSize 只是上千个 MFC 和 Windows 函数之一。编写一个基于 Windows 的程序，几乎不可能不碰到未引用参数。<br>　　说了这么多关于 UNREFERENCED_PARAMETER 内容。Judy 在她的问题中还提到了另一个 C++ 程序员常用的并且其作用与 UNREFERENCED_PARAMETER 相同的诀窍，那就是注释函数署名中的参数名：</p>
<p style="FONT-SIZE: 14pt">void CMyWnd::OnSize(UINT /* nType */, int cx, int cy){}<br>　　现在 nType 是未命名参数，其效果就像你敲入 OnSize(UINT, int cx, int cy)一样。那么现在的关键问题是：你应该使用哪种方法——未命名参数，还是 UNREFERENCED_PARAMETER？<br>　　大多数情况下，两者没什么区别，使用哪一个纯粹是风格问题。（你喜欢你的 java 咖啡是黑色还是奶油的颜色？）但我认为至少有一种情况必须使用 UNREFERENCED_PARAMETER。假设你决定窗口不允许最大化。那么你便禁用 Maximize 按钮，从系统菜单中删除，同时阻止每一个用户能够最大化窗口的操作。因为你是偏执狂（大多数好的程序员都是偏执狂），你添加一个 ASSERT （断言）以确保代码按照你的意图运行：</p>
<p style="FONT-SIZE: 14pt">void CMyWnd::OnSize(UINT nType, int cx, int cy){&nbsp;&nbsp;&nbsp;&nbsp; ASSERT(nType != SIZE_MAXIMIZE);&nbsp;&nbsp;&nbsp;&nbsp; ... // use cx, cy}<br>　　质检团队竭尽所能以各种方式运行你的程序，ASSERT 从没有弹出过，于是你认为编译生成 Release 版本是安全的。但是此时 _DEBUG 定义没有了，ASSERT(nType != SIZE_MAXIMIZE)展开为 ((void)0)，并且 nType 一下子成了一个未引用参数！这样进入你干净的编译。你无法注释掉参数表中的 nType，因为你要在 ASSERT 中使用它。于是在这种情况下——你唯一使用参数的地方是在 ASSERT 中或其它 _DEBUG 条件代码中——只有 UNREFERENCED_PARAMETER 会保持编译器在 Debug 和 Release 生成模式下都没有问题。知道了吗？<br>　　结束讨论之前，我想还有一个问题我没有提及，就是你可以象下面这样用 pragma 指令抑制单一的编译器警告：</p>
<p style="FONT-SIZE: 14pt">#pragma warning( disable : 4100 )<br>4100 是未引用参数的出错代码。pragma 抑制其余文件/模块的该警告。用下面方法可以重新启用这个警告：</p>
<p style="FONT-SIZE: 14pt">#pragma warning( default : 4100 )<br>　　不管怎样，较好的方法是在禁用特定的警告之前保存所有的警告状态，然后，等你做完之后再回到以前的配置。那样，你便回到的以前的状态，这个状态不一定是编译器的默认状态。<br>　　所以你能象下面这样在代码的前后用 pragma 指令抑制单个函数的未引用参数警告：</p>
<p style="FONT-SIZE: 14pt">#pragma warning( push ) #pragma warning( disable : 4100 )void SomeFunction(...){}#pragma warning( pop )<br>　　当然，对于未引用参数而言，这种方法未免冗长，但对于其它类型的警告来说可能就不是这样了。库生成者都是用 #pragma warning 来阻塞警告，这样他们的代码可以用 /W4 进行清洁编译。MFC 中充满了这样的 pragmas 指令。还有好多的 #pragma warning 选项我没有在本文讨论。有关它们的信息请参考相关文档。</p>
<p style="FONT-SIZE: 14pt">&nbsp;</p>
<p style="FONT-SIZE: 14pt">本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/apunix/archive/2008/01/14/2043945.aspx">http://blog.csdn.net/apunix/archive/2008/01/14/2043945.aspx</a></p>
<img src ="http://www.cppblog.com/tdweng/aggbug/140818.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tdweng/" target="_blank">心羽</a> 2011-02-28 16:26 <a href="http://www.cppblog.com/tdweng/articles/140818.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WinCE中的Debug Zone调试</title><link>http://www.cppblog.com/tdweng/articles/140815.html</link><dc:creator>心羽</dc:creator><author>心羽</author><pubDate>Mon, 28 Feb 2011 08:18:00 GMT</pubDate><guid>http://www.cppblog.com/tdweng/articles/140815.html</guid><wfw:comment>http://www.cppblog.com/tdweng/comments/140815.html</wfw:comment><comments>http://www.cppblog.com/tdweng/articles/140815.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tdweng/comments/commentRss/140815.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tdweng/services/trackbacks/140815.html</trackback:ping><description><![CDATA[<p><font color=#000000><span>在</span><span>WinCE</span><span>的开发环境中支持</span><span>Debug Zones</span><span>功能，通常也被称为调试域，通过它可以控制打印信息。当某个调试域被打开以后，在这个域中的打印信息就会被打印出来，如果某个调试域被关闭了，那么这个域中的打印信息就会被关闭。调试域是基于模块的，也就是说一个模块，可能是在一个驱动或者一个应用中都可以定义一个调试域，用来调试该模块。一个调试域最多可以包括</span><span>16</span><span>个域，一般在每一个模块中都会有一个全局变量</span><span>dpCurSettings</span><span>，该变量用于描述调试域的相关信息，它由一个模块名字，</span><span>16</span><span>个域的名字和一个掩码组成。下面具个例子：</span></font></p>
<p><font color=#000000></font><span><font color=#000000><strong>DBGPARAM dpCurSettings =</strong></font></span></p>
<p><span><font color=#000000><strong>{<br>&nbsp;&nbsp;&nbsp;&nbsp;TEXT("PCIBUS"), {<br>&nbsp;&nbsp;&nbsp;&nbsp;TEXT("Errors"),TEXT("Warnings"),TEXT("Functions"),TEXT("Initialization"),<br>&nbsp;&nbsp;&nbsp;&nbsp;TEXT("Enumeration"),TEXT("Load Order"),TEXT("Resource"),TEXT("Undefined"),<br>&nbsp;&nbsp;&nbsp;&nbsp;TEXT("Undefined"),TEXT("Undefined"),TEXT("Undefined"),TEXT("Undefined"),<br>&nbsp;&nbsp;&nbsp;&nbsp;TEXT("Undefined"),TEXT("Undefined"),TEXT("Undefined"),TEXT("Undefined") },<br>&nbsp;&nbsp;&nbsp;&nbsp;0x20</strong></font></span></p>
<p><span><font color=#000000><strong>};</strong></font></span></p>
<p><font color=#000000><span>先来解释一下</span><span>DBGPARAM</span><span>结构，该结构在</span><span>Dbgapi.h</span><span>中定义，所以在定义</span><span>dpCurSettings</span><span>的时候还需要包含这个头文件，该结构定义如下：</span></font></p>
<p><font color=#000000></font><span><font color=#000000>typedef struct _DBGPARAM {</font></span></p>
<p><font color=#000000><span>&nbsp;&nbsp;&nbsp;&nbsp;WCHAR<span>&nbsp;&nbsp;</span>lpszName[32];<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>//</span><span>模块的名字</span></font></p>
<p><font color=#000000><span>&nbsp;&nbsp;&nbsp;&nbsp;WCHAR<span>&nbsp;&nbsp;</span>rglpszZones[16][32];<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>//</span><span>调试域的名字</span></font></p>
<p><font color=#000000><span>&nbsp;&nbsp;&nbsp;&nbsp;ULONG<span>&nbsp;&nbsp;</span>ulZoneMask;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>//</span><span>调试域的掩码</span></font></p>
<p><font color=#000000></font><span><font color=#000000>}DBGPARAM, *LPDBGPARAM;</font></span></p>
<p>&nbsp;</p>
<p><font color=#000000><span>在上面的例子中可以看到，第一个是模块的名字，叫</span><span>PCIBUS</span><span>。而后定义了</span><span>16</span><span>个域的名字，其中只用到了</span><span>7</span><span>个域，剩下的都定义为</span><span>Undefined</span><span>了。最后一个数字为域的掩码，表示当前哪个域是被激活的，</span><span>0x20</span><span>表示只有第</span><span>6</span><span>个域是被激活的。从上面的例子还可以看出，前</span><span>7</span><span>个域是有意义的，而且按照顺序分别对应</span><span>1</span><span>到</span><span>7</span><span>。下面针对这些域需要定义相应</span><span>Debug</span><span>调试的宏定义：</span></font></p>
<p><font color=#000000></font><span><font color=#000000>#define DBGZONE_ERROR<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>1</font></span></p>
<p><span><font color=#000000>#define DBGZONE_WARNING<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>2</font></span></p>
<p><span><font color=#000000>#define DBGZONE_FUNCTION<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>3</font></span></p>
<p><span><font color=#000000>#define DBGZONE_INIT<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>4</font></span></p>
<p><span><font color=#000000>#define DBGZONE_ENUM<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>5</font></span></p>
<p><span><font color=#000000>#define DBGZONE_LOADORDER<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>6</font></span></p>
<p><span><font color=#000000>#define DBGZONE_RESOURCE<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>7</font></span></p>
<p>&nbsp;</p>
<p><font color=#000000><span>上述宏定义对应在</span><span>dpCurSettings</span><span>中的</span><span>7</span><span>个域，然后就可以在打印信息的时候，通过这些宏定义来对应相应的调试域了。例如：</span></font><span><font color=#000000></p>
<div>
<ol>
    <li><span><strong><font color=#0000ff>while</font></strong></span><span>(1)</span>
    <li><span>{</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span><strong><font color=#0000ff>if</font></strong></span><span>&nbsp;(dwFlag)</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEBUGMSG(DBGZONE_ERROR,&nbsp;(L</span><span><font color=#a31515>"Error&nbsp;found:&nbsp;%d\r\n"</font></span><span>,&nbsp;NumDevKeys));</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span><strong><font color=#0000ff>break</font></strong></span><span>;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><strong><font color=#0000ff><span>else</span></font></strong>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEBUGMSG(DBGZONE_WARNING,&nbsp;(L</span><span><font color=#a31515>"Warning&nbsp;found\r\n"</font></span><span>));</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span>
    <li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEBUGMSG(DBGZONE_LOADORDER,&nbsp;(L</span><span><font color=#a31515>"load&nbsp;in&nbsp;a&nbsp;while&nbsp;loop\r\n"</font></span><span>));</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sleep(100);</span>
    <li><span>}</span>
    <li></font></span>&nbsp; </li>
</ol>
</div>
<font color=#000000><span>
<p><font color=#000000><span>从这段代码可以看出，如果</span><span>dpCurSettings</span><span>中的掩码定义为</span><span>0x20</span><span>，那么在</span><span>DEBUGMSG</span><span>的打印中，只有条件为</span><span>DBGZONE_LOADORDER</span><span>才会被打印，循环中的前两个打印信息是不会被打印的。如果想让上面的代码中的所有</span><span>DEBUGMSG</span><span>都能打印必须设置掩码如下：</span></font></p>
<p><span><font color=#000000>dpCurSettings.ulZoneMask = DBGZONE_ERROR | DBGZONE_WARNING | DBGZONE_LOADORDER;</font></span></p>
<p></span></font><font color=#000000></font>&nbsp;</p>
<p><font color=#000000><span>在一个模块中定义了调试域，如果想在系统中去使用还必须注册该调试域，需要用到的函数叫</span><span>DEBUGREGISTER(..)</span><span>，其中要把该调试模块的句柄作为参数传给它。例如：</span></font></p>
<p><font color=#000000></font><span><font color=#000000><strong>DllMain(..)</strong></font></span></p>
<p><span><font color=#000000><strong>{</strong></font></span></p>
<p><span><font color=#000000><strong>&nbsp;&nbsp;&nbsp;&nbsp;switch(op)</strong></font></span></p>
<p><span><font color=#000000><strong>&nbsp;&nbsp;&nbsp;&nbsp;{</strong></font></span></p>
<p><span><font color=#000000><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case DLL_PROCESS_ATTACH:</strong></font></span></p>
<p><span><font color=#000000><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DEBUGREGISTER(hPCIBUS);</strong></font></span></p>
<p><span><font color=#000000><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;</strong></font></span></p>
<p><span><font color=#000000><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;.</strong></font></span></p>
<p><span><font color=#000000><strong>&nbsp;&nbsp;&nbsp;&nbsp;}</strong></font></span></p>
<p><span><font color=#000000><strong>}</strong></font></span></p>
<p>&nbsp;</p>
<p><font color=#000000><span>完成了上述工作以后，就可以重新编译调试的模块，然后运行系统来调试了。调试域的一个好处就是在</span><span>Debug</span><span>的过程中，不需要终止系统可以动态的改变调试域，方便我们分析问题。首先，我们可以基于</span><span>Platform. Builder</span><span>中的</span><span>CE Debug Zones</span><span>来调试，在</span><span>VS2005</span><span>的菜单中选择</span><span>Target</span><span>，然后选择</span><span>CE Debug Zones</span><span>，如图：</span></font></p>
<p><font color=#000000><span><img title=点击图片可在新窗口打开 alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nanjianhui/EntryImages/20081210/debugzone_1.JPG"></span></font></p>
<p><font color=#000000><span></span></font>&nbsp;</p>
<p><font color=#000000><span><span>然后会出现一个</span><span><font face="Times New Roman">Debug Zones</font></span><span>的窗口，在窗口弹出以后，它可能会花一点时间来收集当前支持</span><span><font face="Times New Roman">Debug Zone</font></span><span>的模块，如下图：</span></p>
</span></font>
<p><font color=#000000><span><img title=点击图片可在新窗口打开 alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/nanjianhui/EntryImages/20081210/debugzone_2.JPG"></span></font></p>
<p><font color=#000000><span></span></font>&nbsp;</p>
<p><font color=#000000><span><span>该图只是一个例子，左边显示了可调试的模块，选择</span><span><font face="Times New Roman">serial_SMDK2410.dll</font></span><span>这个模块，就是</span><span><font face="Times New Roman">S3C2410</font></span><span>的串口驱动模块。在右侧可以看到各个调试域及名字，用户可以根据需要来选择打开和关闭相应的调试域，最后点击</span><span><font face="Times New Roman">Apply</font></span><span>和</span><span><font face="Times New Roman">OK</font></span><span>就可以了。</span></span></font></p>
<p><font color=#000000><span></span></font>&nbsp;</p>
<p><font color=#000000><span><span>当然，还有其他的方法来修改调试域，一种方法是使用</span><span><font face="Times New Roman">Target Control</font></span><span>中的</span><span><font face="Times New Roman">zo</font></span><span>命令来修改，</span><span><font face="Times New Roman">Target Control</font></span><span>将在以后介绍。还有一种方法就是通过</span><span><font face="Times New Roman">SetDbgZone(..)</font></span><span>函数来修改。定义如下：</span></span></font></p>
<p><font color=#000000><span><span><font face="Times New Roman"><strong>BOOL SetDbgZone(DWORD<span>dwProcid</span>,&nbsp;LPVOID<span>lpvMod</span>,&nbsp;LPVOID<span>baseptr</span>,&nbsp;DWORD<span>zone</span>, LPDBGPARAM<span>lpdbgTgt</span>)</strong></font></span></p>
<p><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;dwProcid</font></span><span>：</span><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span><span>进程的句柄</span></p>
<p><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span>lpvMod</font></span><span>：</span><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span><span>调试模块的句柄</span></p>
<p><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span>baseptr</font></span><span>：</span><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span><span>设置为</span><span><font face="Times New Roman">NULL</font></span></p>
<p><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span>zone</font></span><span>：</span><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span><span>新的调试域掩码</span></p>
<p><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span>lpdbgTgt</font></span><span>：</span><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span><span>返回新的</span><span><font face="Times New Roman">DBGPARAM</font></span><span>结构</span></p>
<p>&nbsp;</p>
<p><span>上面对</span><span><font face="Times New Roman">Debug Zone</font></span><span>的定义，使用以及调试作了大致的介绍，按照上面的步骤可以给一个模块添加调试域，注册调试域并在系统运行以后随时更改调试域，其根本目的无非是帮助我们来调试模块和分析问题。一般情况下，调试域只在</span><span><font face="Times New Roman">Debug</font></span><span>模式下使用，但是也可以在</span><span><font face="Times New Roman">Release</font></span><span>模式下使用。但是有些地方需要修改，首先前面已经介绍过</span><span><font face="Times New Roman">Debug</font></span><span>模式下的打印用</span><span><font face="Times New Roman">DEBUGMSG</font></span><span>，而</span><span><font face="Times New Roman">Release</font></span><span>模式下的打印应该使用</span><span><font face="Times New Roman">RETAILMSG</font></span><span>函数。所以在</span><span><font face="Times New Roman">Release</font></span><span>模式下，打印函数应该改为</span><span><font face="Times New Roman">RETAILMSG</font></span><span>函数。还有在注册调试域的时候，不能再使用</span><span><font face="Times New Roman">DEBUGREGISTER(..)</font></span><span>函数，而是应该改用</span><span><font face="Times New Roman">RETAILREGISTERZONES(..)</font></span><span>函数。</span></p>
<p></span></font></p>
<br>
<img src ="http://www.cppblog.com/tdweng/aggbug/140815.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tdweng/" target="_blank">心羽</a> 2011-02-28 16:18 <a href="http://www.cppblog.com/tdweng/articles/140815.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>