WinCE简介与教程

爆料WinCE简介与教程


第一楼
目录



第二楼
winCE边做边学



第三楼
Windows CE 5.0实时系统


第四楼
Windows CE内核启动分析


第五楼
wince5.0中断的详细解释

wince边做边学

    从微软公司寄来了我定购的免费光盘,让我很兴奋,里面我要了一份WINCE5的评估版,还要了一份Windows XP Embedded的评估版,于是马上着手准备测试环境,开始我了对新版WinCE的学习之旅。
    既然都是新学,我就想到要把我的学习过程记录下来,这样就可以让更多还没有使用WINCE5的朋友先睹为快了,好,有想法就要有行动,就不如起个名字叫WinCE5边做边学吧,怎么样,准备好了吗?Ready? GO!
    我们先来安装WinCE5的开发环境。120天的评估版,可要珍惜时间哦。安装的过程不用多说,只要注意,以下图的界面中别选错了就行了。

    花了半个小时左右的时间,安装过程结束,赶快运行起来看看这个新东西吧!
    我们看到,这个Platform Builder与WinCE4的基本相同,所以不用多解释,看到屏幕中间的那个"New Platform"了吗?点击它我们来新建一个平台。
    废话少说,我们来看几个关键的步骤。在新建平台向导的第三步需要选择板支持包(BSP)的类型,这里要根据你的硬件平台进行选择,比如你使用AMD GEODE的板子,就可以选择这个,如果使用PC机做为目标平台就选用CEPC,当然也可以选择使用模拟器Emulator,使用模拟器的意思就是你的电脑既是开发机也是目标机。
    在第四步中可以选择预定义的模板平台,比如你想做个企业终端就可以选择它,想做个网关就选网关模板,想做个机顶盒就选机顶盒模析,还有IP电话,手持式移动设备,瘦客户端等等,我就选了一个Internet Appliance来做一个基于Internet的应用。
    后续的向导步骤都取默认,Finish。
    好,向导为我们选取了一些组件组成了新的Hello平台,我们在工具栏的"Select Active Configuration"下拉列表框中选择"Emulator:x86_Debug"以便使用模拟器配置,然后点击该列表框后的"Sysgen"按钮以编译并生成平台。这需要根据你的机器配置情况来决定用时了,在我这里大概要20-30分钟左右。慢慢等吧。
    终于编译完了,别急,现在还不能运行,我们还需要为目标设备做些连接方面的配置。在Target菜单找到Connectivity Options菜单项,打开对话框后在"Download"列表框中选择"Emulator",如下图,完成后关闭对话框,然后再次通过Target菜单下的Attach Device来连接设备,花几钟后您就会看到一个模拟器被运行了,上面的CE操作系统就是你刚才生成的哦。

    今天就弄这些吧,主要原因是走了一点弯路。我上午在笔记本电脑上装了这个环境,后来编译也没有问题。但在运行模拟器的时候提示我需要使用Professional版本且模拟器无显示,因此下午又找了另一台台式机来装了个XP的专业版。又不得不重装了CE5,呵呵,因此耽误了不少时间哦!
继续我们的边做边学,上次我们第一次用PlatformBuilder这个环境做了一个Internet Appliance的平台,我们也在模拟器上看到了这个平台的运行结果,这次,我们在此基础上做些修改,以实现我自己的需求。

    现在假设我们的目标系统不再需要MediaPlayer以及多媒体如MP3,WMA,MPG,AVI等功能,这样我们就要在自己的平台的把这一部分组件去掉,否则这些无用的功能会浪费你很大一部分存储空间。下面我们就来做这一部分。
    我们先来看"OSDesignView"这个工具窗口。它里面以树的形式列出了你现有平台中的组件,我们可以从中发现各种设备驱动程序,核心服务组件,文件系统和数据存储组件等等。如果我们想要对某一部分组件进行操作,那么就可以选中它以通过右键菜单进行。比如你想看某一个组件与其他组件的依赖关系,只要右击该组件即可。
    说到依赖关系,由于操作系统是很庞大复杂的系统软件,因此很多组件都与其他组件之件形成了依赖关系,即它必须以某些组件的存在为前提,或它的存在是某些组件存在的基础。如果我们想自己来明晰这些关系将会是非常困难的事情。幸好PlatformBuilder为我们提供了反应组件之间依赖关系的功能,你只要在欲了解的组件上右击鼠标,选择"Dependencies for Selected Item"即可,弹出的窗口如下图所示:


继续实现我们前面的需求,因为此前我们实现的平台已经包括了多媒体组件,现在不要了,那么我们只要在现有的平台组件中将其删除即可。因此在"OSDesignView"中展开当前"Graphics and Multimedia Technologies"文件夹中的多媒体部分,将媒体播放器等内容全部Delete,如图:


然后重新编译平台,重新下载到模拟器上运行。你注意到桌面的上次运行时的差异了吗?没错,媒体播放器不见。

    你学会如何删除无关的组件了吗?好,下面再让我们试试添加组件,假设我们的目标平台需要纸牌和空当接龙的游戏,这时如何呢?
    我们来看Platform Builder中的右侧面板即Catalog,在其Core OS文件夹下有系统提供的全部可用组件,你只需要把你想要的找到即可,我们按下图所示


找到这两个游戏,分别右击每一个然后在菜单中选择"Add to OS Design",这样它们就会被添加到左侧的"OSDesignView"中,也就是添加到了你的平台之上,再次编译、下载,看看模拟器的开始菜单的"Programs"中是否多了两个游戏呢?不如自己试试玩玩吧!

经过前两次的边做边学,我们已经创建并可以自由修改自己的平台了,这次我平来补充一点理论知识,以便加深对CE生成过程的了解。在此之前,需要说明的是CE的目录结构是很庞大而复杂的,在以后的工作中很多事情会取决于你对目录结构的了解程度,在此,CE5的文件夹结构未发生大的改变,因此想了解此部分内容的朋友可直接参阅本人的《WinCE实验教程》相关部分。与CE4稍有不同的是在CE5中你自己的平台不在存放于Public文件夹中了,而是专门有一个PBWorkspaces的文件夹,这样更加方便管理且结构清晰了。
    我们重点要说的还是CE的生成过程,即你在编译平台的时候Platform Builder到底为你做了些什么?
    实际上,IDE在生成CE的过程当中共经历了四个阶段,分别是Sysgen、Build、Copy、Make,即组件生成阶段,编译阶段,Release文件夹复制阶段和镜像打包阶段。下面我们就分阶段来说明一下。
    在Public文件夹下是CE为我们提供的可用组件,我们定制自己的平台是通过Platform Builder做的选择就是从这里面选择了一部分需要的组件。在这些文件夹中,包含了所有可用的库文件,头文件,DEF文件,当然也包括了所有可导出的函数说明。在Sysgen阶段要做的就是从中选择我们自己的平台需要的头文件,库文件和导出函数以创建我们自己平台需要的组件。它所完成的头文件会以C++注释的方式加以标记,这样它就可以选择需要的部分来导出,最后把所有需要的库进行链接就得到了我们自己平台所需要的东西了,它就放在Wince500\pbworkspaces\%ProjectName%\WINCE500\%CPU_TYPE%_x86\cesysgen文件夹下。
    经过上一阶段后,需要的头文件库文件就准备好了,但这只是系统组件部分,我们的平台还有各种设备驱动程序等其他的东西,这些就要在Build阶段来完成了。
    整个Build阶段都是在围绕着DIRS文件和SOURCES文件来进行,前者决定了哪些文件夹要被编译,后者决定了哪些文件要被如何编译,也就是说,此阶段要完成的就是各种源程序的编译过程。下面我们就重点看一下这两种文件的内容。
    DIRS文件可以在很多文件夹中找到,它列出了要参与编译的子文件夹,内容大体如下:

DIRS_CE=\
ceddk \
regenum \
pcibus \

    这样,build.exe在编译的时候就可以通过它来逐层找到要参与编译文件夹了。至于其中的OPTIONAL_DIRS、DIRS_NTANSI等项目的含义可以通过帮助查到。
    同样,SOURCES文件对参与编译的源程序的编译方式做了规定,例如通过TARGETNAME规定编译后的名称,通过TARGETTYPE规定编译的类型是EXE,DLL还是LIB,通过DLLENTRY规定DLL文件的入口点,通过INCLUDES规定编译过程中需要的头文件,通过SOURCES规定参与编译的源文件等等。这些宏的用法在帮助文档里有更加明细的说明,在这里只要理解这种编译机制就可以了。
    接下来,所有的编译都有完成了,DLL也好,EXE也好,都是目标平台自有的了,下一步就是一个Copy过程,也就是将你的项目文件夹下的WINCE500文件夹下的内容复制到RELEASE文件夹下,这步就没什么可说的了。
    最后一步就是打包了,即将已经准备好的目标平台的文件打包成NK.bin这样的操作系统镜像文件。在这个过程当中,一个完成了文件合并、注册表压缩、资源文件替换和打包四个子过程。
    文件合并阶段重点对以下文件进行合并:
    --所有的.bib文件合并成CE.BIB
    --所有的.reg文件合并成
REGINIT.INI
    --所有的.dat文件合并成
INITOBJ.DAT
    --所有的.db文件合并成
INITDB.INI
    知道这几个合并以后便于你在RELEASE下查找你做的修改是否确实被置入了目标平台。

    注册表压缩会将REGINIT.INT文件压缩成DEFAULT.FDF文件。
    资源替换就是将EXE或DLL中的资源替换成本地语言如简体中文。
    接下来才是把这些CE.BIB等二进制文件制作NK.BIN文件,这才是我们需要的文件,不过在CE5中好像可以在项目设置中改变这个文件的名称,读者可以自己试试。
    以上说的这些编译过程,在Platform Builder中的Build OS菜单下分得比较明确,这一点比CE4要好得多,你可以自己掌握让它单独执行某一步骤。如果你修改了某个组件的源程序,也可以右击这一组件然后单独编译此组件,最后再生成Image,总之,灵活性是比较不错的了。
    同时,经过以上的分析也可以看出来,Platform Builder这个IDE在帮助生成CE时基本上是依赖于文件夹结构和那些文本文件以及批处理文件的,因此,对于文件夹结构还是要尽量的熟悉。同时也证明那些总说批处理文件没用的人的无知,在微软的操作系统和开发工具中,批处理文件是至关重要的,说它无用的人只是由于自己的水平不够而已。
    好了,这次我们学得多做得少,不过这些知识还是有必要了解的,别忘了对照你的文件夹结构来学习哦!
经过以前的学习,我们已经掌握了生成CE的过程及编译的几个阶段,这次我们再来了解一下Platform Builder中为我们提供的组件包的管理方式以及如何管理自己的组件。

在Platform Builder(以下简称PB)中的右侧有一个"Catalog"窗口,其中列出了所有可供使用的组件,我们可以看到其内容是非常之多的,现在的疑问就产生了,PB是如何管理这些组的呢?如果我有一个新设备的驱动组件要如何才能放到这个组件包窗口中呢?如果我开发了一个设备驱动以供其他人使用那我要如何才能发布我的驱动呢?下面,我们就来解决这些问题。

    在PB中,这些组件的管理都是能过一种组件文件(.cec文件)来实现的。在CE4中,系统自带的CEC文件都位于PB的安装文件夹下的CEC文件夹,我们可以在那里很容易的找到它们,但是在CE5中,它的位置变了,你可以在WINCE500\PUBLIC\COMMON\OAK\CATALOG\CEC下找到他们。
    如果你开发了OAL,设备驱动或其他组件,你就可以能过CEC文件来把它们加入到PB中。通过在PB环境中导入CEC文件,其他的平台开发人员就可以使用这些组件了。CEC文件是用来描述组件信息的文本文件,它包括了一些块的列表,主要包括以下四种信息块:
    --CECInfo块,用来描述此CEC文件的信息,每个CEC文件只能含有一个此信息块,从中你可以看到该CEC的名称、GUID、版本、供应商和简要描述。
    --ComponentType块,它描述了最高级别的组件类型,在一个CEC文件中可以有多个此信息块,它通过Group,RequiredCEModules,ExcludeWhenSet,MaxResolvedImplsAllowed等条目来描述此组件所在的组,需要的CE组件,排除的组件及允许在一个CE平台中存在的数量等信息。
    --Implementation块,用来描述此组件在编译时需要的各种信息,每个CEC文件中也可以有多个此信息块,它是 ComponentType块的一部分,其中的BSPPlatformDir,Children,OptionalChildren,DefaultDrivers,ExcludeWhenSet,FeatureVariable,ImplSize等条目的具体含义可以在PB的帮助文档中找到,在此不一一细述。
    --BuildMethod块,描述了编译的方法,也是ComponentType块的一部分,为了加深对上一次编译阶段的理解,我们详细说一下此块中Step和Action两个条目。
    Step用来说明编译此组件将要在哪一步中进行,对照上一次的内容,它的取值为:CESYSGEN,BSP,BUILDREL,MAKEIMG。所以如果你自己开发了OAL或驱动之类的组件,你就需要自己决定让PB在什么阶段来编译它,就要在CEC中通过Step来描述。其实在具体的编译过程中,上述的每一阶段又有PRE和POST两个子阶段,也就是说对于每个阶段其实都有三步,即PRECESYSGEN,CESYSGEN,POSTCESYSGEN,PREBSP,BSP,POSTBSP……一般不用具体到这样详细的程度。
    Action则描述了组件编译的行为,有效的行为与编译阶段的对应关系如下表所示:
Action CESYSGEN BSP BUILDREL MAKEIMG
#BUILD(DIR,...) No Yes No No
#BUILD(SOURCES) No Yes No No
#BUILD(MAK,...) No Yes No No
#CUSTOM(...,...) No Yes Yes No
#COPY(...,...) Yes No Yes No
#ENV(...,...) No Yes No Yes
#SRCCODE(...) No No No No

    这些Action的意义如下:
    --#BUILD():使用DIRS文件或SOURCES文件或MAKE文件来调用build.exe,在完全编译平台的时候被使用;
    --#CUSTOM():在编译的时候运行批处理文件或可执行文件;
    --#COPY():从一个位置复制文件到另一个位置;
    --#ENV():设置环境变量;
    --#SRCCODE():指定了包含源程序文件的文件夹,只在编译所选组件时应用。
    对于以上每个条目的具体用法可以参阅PB的帮助文档。为了获得感性认识,我们最好用记事本打开几个CEC文件来看一下。比如打开serial.cec看看串口驱动组件的实现方法等。
    以上我们了解了CEC文件,我们再来解决后面的问题,即假设我从别的供应商处拿到了一个设备的驱动程序,它带了CEC文件,那么我要如何把这个组件加入到PB的组件包中呢?这个问题其实很简单,只要在PB的File菜单下使用"Manage Catalog Items"命令即可。在这个弹出的对话框中的内容和使用方法就不用我说了吧。
    再来解决下一个问题,我要如何为我开发的组件编写CEC文件呢?这个也好办,在Tools菜单下使用"CEC Editor"命令即可,如果不会写,可以先打开一个现有的CEC文件看看是如何组织的,然后再仿照它来写自己的就可以了,别忘了写完后要保存哦,同时也要注意GUID的问题。
    此次内容的最后,我们再来看一下BSP。什么是BSP呢,其实就是为某一种开发板实现了设备驱动的软件包,它包含了源程序文件,二进制文件等,还有OAL适配层,Bootloader和其他有关的配置文件。比如CE5的评估版就带了x86的BSP,AMD的BSP和Emulator的BSP。
    通常对BSP的操作都在BSP向导中进行,它位于Platform菜单中,利用它可以新建自己的BSP,复制现存的BSP,修改现存的BSP或创建全局的驱动程序。如果你准备好了BSP所需要的那些东西就可以用它来生成你的BSP了,它会选择必须的CE内核组件。
    好了,已经写了不少了,唉,写这东西真是费时啊,不说了,希望我们能一起获得更多的知识。
Windows服务是独立于登录用户而工作的Windows应用程序,它通常在计算机启动时开始执行,且常常连续执行,直到计算机关闭为止。像Exchange Server,IIS和杀毒软件等都使用这种方式,这样就可以独立于某一用户而且可以在任何用户登录前来运行,同时也可以服务于所有的进程,从而以一种服务的形式存在。

    正因为Windows服务有着这么多的特性,因此,当需要一些特殊功能的时候就可以考虑使用Windows服务来解决问题。比如下面我们要做的这个例子。对于我们这些程序设计人员,计算机是在一起工作时间最长的伙伴,每天都会对着它的屏幕八个小时以上,还不包括下班后在家打游戏的时间,因此,保护眼睛是最重要的了。问题的起因来源于本人周六去眼科对激光手术的复查,大夫一再向我强调眼睛的自我调节能力,就是说只要你能保证你每隔一个小时左右就闭眼休息或向远处眺望,离开电脑屏幕,那么已经治好的近视就不会反弹。本人虽是自律性比较强的人,但在计算机屏幕面前就不再如此了,往往几个小时也不抬头一次,为了眼睛的健康,我决定把这个艰巨的任务交由计算机来完成,让它在一小时左右自动提醒我休息五分钟。如此一来,岂不是再也不用顾虑这件事了。
    功能虽然简单,但要写个程序放在启动组里每天自动运行也不是一个好的办法,正巧以前也没做过Windows服务,不如索性来试一试,同进也看看.NET为我们提供了多么先进的功能吧,于是决定,就用C#来做一个提醒我保护眼睛的Windows服务,取名就叫CareEye吧。
    运行Visual Studio.NET 2003,建立一个C#的Windows服务项目,在CareEye.cs的设计视图提示可以把需要的控件和组件拖动到这上面,假如想要做系统日志的话当然就可以把EventLog组件拖过来了,不过这个程序好像不需要这些东西,还是算了吧。那么计时要不要采用计时器控件呢?想了一下,这个控件虽然好用,但太常用了,本着学习新知识的原则,最恰当的恐怕就是线程了,而且在以后做其他Windows服务的时候线程肯定是必需的,所以还是用线程好,这样我只要在线程中完成对时间的监测,把线程的启动和停止交给服务的启动和停止,呵,很方便啊。
    再来看CareEye.cs的源程序,一大堆没见过的东西,不过仔细分析一下也就没什么了。CareEye类派生于ServiceBase类,因此继承了基本服务类的特性,显然Main()方法会由SCM(服务控制管理程序)调用,在这个方法中Run一个新的CareEye实例,这样就运行了一个Windows服务,OnStart()和OnStop()明显是用于启动和停止服务的响应函数了。
    注意在Main()方法中有一个ServiceBase[]的数组,它是为那些一个服务进程包含多个服务准备的,对于这个程序来说,它只有一个CareEye服务,因此完全可以把这个数组删除,而只是使用System.ServiceProcess.ServiceBase.Run(new CareEye());一句就够了。
    接下来为了使用线程,需要引入System.Threading命名空间,为了使用对话框,还需要引入System.Windows.Forms命名空间,这是为了将来提示用户时显示对话框而准备的。
    下面为类CareEye添加一个成员字段private Thread MainThread;同时在构造函数中对其进行初始化:
      MainThread=new Thread(new ThreadStart(ThreadFunc));
      MainThread.Priority=ThreadPriority.Lowest;
   
这里把线程的优先级设到最低,这样不会耗用过多的系统性能。这个线程对象使用ThreadFunc作为线程函数,因此将这个线程函数补充完整:
      public static void ThreadFunc()
      {
         int LastHour=DateTime.Now.Hour;
         while (true)
         {
            System.Threading.Thread.Sleep(60000);
            if (DateTime.Now.Hour-1==LastHour)
            {
               MessageBox.Show("
为了爱护您的眼睛,请您暂时休息5分钟并向远处眺望!","警告",MessageBoxButtons.OK,MessageBoxIcon.Warning,MessageBoxDefaultButton.Button1,
MessageBoxOptions.DefaultDesktopOnly);
               LastHour=DateTime.Now.Hour;
             }
          }
       }
    余下的代码就简单了,只要在OnStart中启动线程,在OnStop中停止线程就行了。

    以上的服务程序虽然很简单,线程的处理上也不很恰当,也违背了很多服务程序的原则比如界面显示等,但对于本人的需求而言是足够了,因此就如此制作了。如果你有需要,当然可以把对话框改为其他的提醒方式如响铃等,线程也可以使用内核对象同时使用更好的处理方法。
    Windows服务就做完了,余下的就是要测试了,但发现这个EXE无法运行,它会提示你该EXE需要使用安装程序来安装服务,看来不可能写一个程序就算是Windows服务了,还要把它注册到Windows才行。
    接下来,右击CareEye.cs的设计视图,添加安装程序,(VS.NET想得就是挺周到的),这下又出来一批代码,不过好在不用改代码了,只要把Account的账户类型设成LocalSystem,把StartType设成手动启动就行了,这里用手动是为了方便调试,以后可以改成自动类型。
    编译完后,还是无法运行,此处还需要一步,就是运行installutil来安装这个服务,其安装和卸载的用法为:
      installutil CareEye.exe
      installutil /u CareEye.exe
   
安装完后能过系统的服务管理器你就可以看到你的服务了,只要点击启动就可以把它启动,把时间向前改一个小时它就会提醒你需要休息了,呵呵,够简单了吧。
    如果你想制作成安装包分发给自己的朋友,只需要再添加个部署项目就行了,不过为了完成自注册,要在自定义操作编辑器中的安装阶段添加一个自定义的安装操作,把InstallerClass属性设成TRUE即可。
    余下的事情,就是自己动手试试吧,这回不用担心用眼超时了!
    以下是careeye.cs的源程序:
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;
using System.Windows.Forms;

namespace CareEye
{
public class CareEye : System.ServiceProcess.ServiceBase
{
private Thread MainThread;
/// <summary>
///
必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null;

public CareEye()
{
   //
该调用是 Windows.Forms 组件设计器所必需的。
   InitializeComponent();

   // TODO: 在 InitComponent 调用后添加任何初始化
   MainThread=new Thread(new ThreadStart(ThreadFunc));
   MainThread.Priority=ThreadPriority.Lowest;
}

// 进程的主入口点
static void Main()
{
   //System.ServiceProcess.ServiceBase[] ServicesToRun;

   //
同一进程中可以运行多个用户服务。若要将
   //另一个服务添加到此进程,请更改下行
   // 以创建另一个服务对象。例如,
   //
   //   ServicesToRun = New System.ServiceProcess.ServiceBase[] {new CareEye(), new MySecondUserService()};
   //
   //ServicesToRun = new System.ServiceProcess.ServiceBase[] { new CareEye() };

   System.ServiceProcess.ServiceBase.Run(new CareEye());
}

/// <summary>
///
设计器支持所需的方法 - 不要使用代码编辑器
/// 修改此方法的内容。

/// </summary>
private void InitializeComponent()
{
   //
   // CareEye
   //
   this.ServiceName = "CareEye";

}

/// <summary>
///
清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
   if( disposing )
   {
    if (components != null)
    {
     components.Dispose();
    }
   }
   base.Dispose( disposing );
}

/// <summary>
///
设置具体的操作,以便服务可以执行它的工作。
/// </summary>
protected override void OnStart(string[] args)
{
   // TODO:
在此处添加代码以启动服务。
   MainThread.Start();
}

/// <summary>
///
停止此服务。
/// </summary>
protected override void OnStop()
{
   // TODO:
在此处添加代码以执行停止服务所需的关闭操作。
   MainThread.Abort();
}
public static void ThreadFunc()
{
   int LastHour=DateTime.Now.Hour;
   while (true)
   {
    System.Threading.Thread.Sleep(60000);
    if (DateTime.Now.Hour-1==LastHour)
    {
     MessageBox.Show("
为了爱护您的眼睛,请您暂时休息5分钟并向远处眺望!","警告",MessageBoxButtons.OK,MessageBoxIcon.Warning,MessageBoxDefaultButton.Button1,MessageBoxOptions.DefaultDesktopOnly);
     LastHour=DateTime.Now.Hour;
    }
   }
}
}
}
Windows CE 5.0实时系统

       目前,Windows CE已从一款基本操作系统升级到体积小巧、组件化的硬实时嵌入式操作系统。尽管Windows CE具有与Win32相同的应用编程接口(API),而且微软台式机和服务器操作系统也配备了此类接口,但Windows CE的底层操作系统架构和台式机的操作系统完全不同。Windows CE既支持包括Win32、MFC、ATL等在内的台式机应用开发结构,也支持使用.NET Compact Framework的管理应用开发,还支持当前实时嵌入式系统设计,提供操作系统必要之需的实时内核。本文中,我们主要讨论用于验证Windows CE 5.0实时功效的操作系统工具。
众所周知,实时系统并不通过单一分析进行测试,即使单一分析可以证明实时系统的正确性。实时系统的测试是详尽讨论此问题的依据。您的工作就是建立起用户对解决方案的信任感。下文介绍的工具可以完整、实时地解释应用程序和操作系统之间的交互作用,它们有助于您加深对实时系统的了解。

尽管关于实时的定义还存在诸多争议,我们还是来了解一下对它的定义。这里,我引用comp.realtime FAQ的定义。实时系统的权威定义(Donald Gillies)如下:

实时系统是这样一种系统,即其计算正确与否,不仅取决于计算逻辑是否正确,还取决于计算结果所花费的时间。如果不能满足系统的时间限制,就会出现系统失败的情况。”

因为集高速I/O、机器人技术和机械控制于一身的工业自动化应用对时间的要求最为苛刻。微软开始了解实时嵌入式操作系统的特殊社会要求。自1986年以来,通用汽车动力公司(GMPTG)在制造应用中实施OMAC技术方面一直处于领先地位,并且在后来促成了OMAC用户群的形成。他们一起对数百种应用进行评估后发现,大多数系统(95%)需要一毫秒或稍长的周期。一毫秒周期允许的变化幅度为10%,或是100微秒(μs)。这是基于200 MHz X86系统的Windows CE的设计目标,其在该平台上的平均响应时间为50 μs。Windows CE达到或超过了95%的被评估的硬实时应用OMAC的要求。

大部分满足要求的工业自动化应用是由从一台机器发出的外部信号驱动的。此信号以中断形式发送给硬实时应用。微软鼓励Windows CE的开发人员,尽可能在中断服务线程(IST)中置入更多的应用代码。这使OMAC抖动定义变为针对不超过100 μs的IST延迟的时间限制。其余被评估的应用使用计时器创建其周期。这就需要一台延迟或抖动不超过100 μs的1毫秒计时器。总之,OMAC定义提出以下设计和测试要求:

·Interrupt Service Thread (IST) latencies of no more than 100 μs latency.

·1 millisecond timers with maximum of 100 μs latency.

·中断服务线程(IST)延迟不超过100 μs。

·1毫秒计时器的延迟最长为100 μs。

在了解了OMAC的设计和测试要求后,接下来让我们看看Windows CE中安装的工具。这些工具的用途是确定中断定时、应用执行动作、操作系统功能定时和时序安排定时。

区分实时系统和实时操作系统也很重要。实时系统包含硬件、操作系统和应用等所有元素。实时操作系统仅仅是构成实时系统的其中一个元素。如需了解更多信息,请参阅微软Windows CE实时性能设计和优化。

我们将介绍诸多工具和用途:

·ILTiming。该工具用于确定平台的中断服务例程(ISR)和中断服务线程(IST)延迟。ISR延迟是指从硬件中断到第一次中断服务例程指令之间的时间间隔。而IST延迟是指从现有ISR到中断服务线程开始之间的时间间隔。

内核实时性能最重要的特性之一,就是可以在指定的时间内实施中断。中断延迟主要指软件中断处理延迟,即从外部中断到达处理器直到中断处理开始之间的时间间隔。

如果不发生分页操作,Windows CE中断延迟时间被限制于内存中锁定的线程。这样就可以计算最糟糕情况下的延迟时间 — 到ISR的启动和到IST的启动的总用时。通过计算ISR和IST所需时间,可以确定中断处理以前的总用时。

ISR延迟

ISR延迟是指从IRQ在CUP中被设置到ISR开始运行时的时间。以下三个与时间相关的变量会影响ISR的启动:

A = 中断在内核中关闭的最长时间。内核很少关闭中断,但如果将它们关闭,则关闭的时间长度会受到限制。

B = 在内核调度中断和ISR被实际调用之间的时间。内核使用该时间确定要运行什么ISR,并保存在继续之前必须保存的任何寄存器。

C = 在 ISR 返回到内核和内核实际停止处理中断之间的时间。这是内核通过还原在ISR被调用之前被保存的任何状态(例如寄存器)来完成ISR操作的时间。

正在测量的ISR的启动时间可以基于系统中其他中断的当前状态进行计算。如果中断正在进行,则计算要测量的新 ISR 的启动时间必须考虑到两个因素:所关注的中断已经发生之后将发生的较高优先级中断的数量,以及执行ISR所占用的时间。

Windows CE和原始设备制造商(OEM)都会影响执行ISR的时间。Windows CE的控制变量A、B和C都受到限制。

IST延迟

IST延迟是指从完成执行ISR即(通知线程)到IST开始执行的时间。以下四个与时间相关的变量会影响IST的启动:

B = 内核调度中断和真正调用ISR的时间间隔。内核利用这一时间决定将要运行什么ISR,并保存在继续之前必须保存的任何寄存器。

C = 在ISR返回到内核和内核实际停止处理中断之间的时间。这是内核通过还原在ISR被调用之前保存的任何状态(例如寄存器)来完成ISR操作的时间。

L = Kcall(内核调用)中的最长时间。

M = 调度线程的时间。

在ISR返回到内核并且内核执行某些工作来开始执行IST之后最高优先级IST开始的启动时间。在ISR返回并通知IST开始运行之后,IST启动时间受所有ISR的总计时间的影响。下面的示例说明了所得到的启动时间:

最高优先级IST启动时间 =

Windows CE和OEM都会影响执行IST所需的时间。Windows CE控制变量B、C、L和M都是受限制的。OEM控制NISR和TISR(N),它们可以影响IST延迟。

Windows CE还对IST添加了以下限制:链接ISR和IST的事件处理只能用在WaitForSingleObject函数中。Windows CE防止ISR-IST事件处理被用在WaitForMultipleObjects函数中,这意味着内核可以担保触发事件的时间和释放IST的时间有一个上限。

·计划程序计时分析(OSBench):该工具允许您收集计时样本,通过执行调度性能定时测试,测量内核的性能。

·内核跟踪程序(Kernel Tracker):此工具可以直观显示Windows CE .NET操作系统在目标设备上的执行状况。该工具可用于在实时环境下查看线程交互、内部关联以及系统状态信息。本文目的是检验线程和进程间的交互作用。

·调用评测程序(Call Profiler):此工具可用于确定代码的算法瓶颈。

设备中存在许多影响实时性能的因素,如硬件、驱动程序和应用。在本例中,我们从应用级开始。运行于实时环境中的应用启动时就应该分配所有资源。所有内核对象(进程、线程、互斥锁、临界段、信号和事件)都按照需要分配到虚拟内存中。按需分配内存是不确定的,因此,不能对操作系统完成操作的时间进行限制,所以它不能用于应用的实时执行中。

远程调用评测程序

实时系统不仅包括硬件和操作系统,日益增多的应用逻辑也运行于相同的硬件之上。因此,嵌入式设计中的应用代码可能存在失败风险。Windows CE不会强行命令IST在设备驱动程序环境中运行,IST仅是一个特殊的线程,因此在应用环境中可以运行IST线程。既然如此,该如何检验应用代码的瓶颈呢?当然,这可能会影响设备的整体性能。答案是:这正是Windows CE安装的工具 - 远程调用评测程序的功能。该工具可解答下列问题:何时执行何种代码?何谓软件组件的交互?应用程序运行时,CPU在做什么?

为了证明这一点,我采用构建、运行在Windows CE上的“哲学家就餐问题”应用。以下是解决过程:现在,五位哲学家(线程)围坐在圆桌前。每人面前放着一碗食物。哲学家们用一支筷子开始吃饭。哲学家就餐的前提是他必须有两支筷子(因此,五位哲学家中必须有一人奉献出一支筷子)。这时,哲学家就必须找到一种能够共享筷子的方法,以保证大家都能吃到碗中的食物。

同样地,当多线程程序中有一个以上的线程(哲学家)竞争资源(食物)时,就有可能发生死锁或争执,当然这要取决于哲学家的饥饿程度!如果多个线程都在等待使用稀缺资源,就会造成等待时间的不确定性,进而冻结所有应用。对实时应用而言,这并不是个难题,您可以选择远程调用评测程序运行应用就可以解决该问题。

远程调用评测程序可以在不同视图中显示调用信息,包括直观的调用图表。它会显示应用运行每个函数时花费的时间。显而易见,这是处理视频/音频流的实时压缩/解压缩问题的最为有效的工具。下表显示的是远程调用评测程序应用中的视图。

表1. 远程调用评测程序中的视图

下图显示的是哲学家应用的调用图表视图。此图显示,35%的应用时间花费在函数Eat( ) 上。也许应该了解一下函数的内容!

图1. 远程调用评测程序

您也许会问,要运行远程调用评测程序,需要向应用代码中添加什么。实际上,您根本无需更改所有代码,而仅需要用其它标志函数(WINCECALLCAP=1)进行编译。

调用评测库为应用开发人员提供了一幅独特的应用逻辑执行过程细节图。将该工具用于低速测试过程,以培养客户对应用代码的信任感。

内核跟踪程序(Kernel Tracker):

远程内核跟踪程序可用于检测运行设备上的进程、线程和中断之间的交互作用关系。下面是一些内核跟踪程序中集成的样本代码。实例中的应用运行的是Windows CE设备的文件系统,其中一个文件夹在台式机放置释放文件,此应用为驻留在台式机中的每个文件生成了一个KITL(内核独立传输层)中断。因此,我们可以在运行的操作系统镜像中清晰地观察应用与中断间的交互作用,也可以确定应用线程运行与KITL中断处理间的时间增量。

作为一个用户界面,内核跟踪程序被划分为三个区域,左窗格显示中断和进程,中窗格显示线程/进程间的交互作用,右窗格(未显示字)中的内容是对中窗格使用的符号的解释。我们可以在镜像底部清楚地看到WalkTree应用正在运行,但看不到在应用和内核环境中花费的时间。

图2. 远程内核跟踪程序用户界面

内核跟踪程序可以在事件间设置时间标记,并能在状态栏上显示不同的时间。内核跟踪程序有一些预先定义的事件,如同步事件、混合事件和用户定义事件等。此外,它还能显示线程状态(如运行、锁定、休眠和移植等)。在下图中,当从内核返回到线程执行时,我们设置了第一个时间标记,而当从线程环境切换到内核时,我们设置了第二个时间标记。

图3. 远程内核跟踪程序 — 时间增量

内核跟踪程序工具可用于定位和检测死锁情况,还可以检测花费在应用和驱动程序线程上的时间。运行内核跟踪程序也许将使系统用时增加2%-3%,但不会影响操作系统的整体定时。

计划程序计时分析

该程序为操作系统环境的扩展集提供了测试标准。该扩展集来自超出Protected Server Library (PSL)的内部调用,而这种调用则来自集成到操作系统其中一个进程的应用(如FileSys.exe、Device.exe等)。该测试分为以下7个基本组:

1.临界段

2.事件设置-唤醒

3.信号发出-接收

4.互斥锁

5.自动放弃率

6.PSL API调用开销

7.互锁API(递减、递增、测试交换、交换)

我们来看一下几项测试结果:

===================================================================

| 0.01 | IP = NO | CS = NO | 1 IPS

-------------------------------------------------------------------

EnterCriticalSection traditional (blocking) without priority inversion :

Time from a higher priority thread calling EnterCS (blocked) to a lower

priority runnable thread getting run

-------------------------------------------------------------------

| Max Time = 13.409 μs

| Min Time = 7.543 μs

| Avg Time = 8.389 μs

====================================================================

===================================================================

| 0.02 | IP = NO | CS = NO | 1000 IPS

-------------------------------------------------------------------

EnterCriticalSection fastpath (uncontested)

-------------------------------------------------------------------

| Subtracting out base result of 12 ticks

| Max Time = 0.064 μs

| Min Time = 0.061 μs

| Avg Time = 0.061 μs

===================================================================

将这些测试结果与花费在处理EnterCrticalSection()函数调用上的时间进行比较。调用此函数的途径有两种。第一种方法较快捷,就是通过使用临界段,实施向内核转移,来解决资源争用问题。第二种方法贯穿整个调用进程,其时因为不存在临界段争用问题,因而速度明显提升。(此例可以解释为什么临界段是同步的首要考虑因素。)

中断计时分析(ILTIMING)

中断计时分析可以测量系统中的中断延迟。该工具使用诸多OAL(OEM适配层)支持功能测量ISR和IST的中断响应时间。这些数字对于了解系统的限制至关重要。

我们来看一看基于AMD K6 500Mhz的CEPC系统的数字。

表3. dwOEMTPoolSize = 16 (CEPC的出厂默认值)

<在此插入文件结束标识>Windows CE和硬实时操作系统的OMAC定义吻合,它安装了构建、测试和部署实时设备所需的工具及资源。所有这些工具:内核跟踪程序、远程调用评测程序、计划程序计时分析和中断计时分析协同工作,可以帮助您在自己的平台上对Windows CE的实时能力进行评估。

如需了解更多信息,请阅读:

.NET Compact Framework的实时性能

Maarten Struys

Michel Verhagen

PTS软件

http://msdn.microsoft.com/library/en-us/dncenet/html/Real-Time_NETCF.asp

专用系统,Windows CE 5.0实时x86处理器

http://download.microsoft.com/download/7/2/f/72fef3b0-9545-46a4-8886-a94f265df9c4/EVA-2.9-TST-CE-x86-01-Iss1.00.pdf

专用系统,Windows CE 5.0实时ARM处理器

http://download.microsoft.com/download/7/2/f/72fef3b0-9545-46a4-8886-a94f265df9c4/EVA-2.9-OS-CE-01-I01.pdf

就Windows CE 5.0 Real-Time Podcast采访Windows CE架构师John Eldridge

http://blogs.msdn.com/mikehall/archive/2005/09/01/459443.aspx

Windows CE的实时决定论(Real-Time Determinism)

http://www.windowsfordevices.com/articles/AT6761039286.html
提示: 该帖被管理员或版主屏蔽
wince5.0中断的详细解释

1 WinCE中第一次对中断的处理是在OAL的OEMInit()中,该函数调用OALIntrInit()完成对中断的初始化.


2 OALIntrInit()
对中断的初始化做了如下工作:

2.1 通过配置IPR0-IPR33设置中断优先级,优先级定义在g_IntPriorities和g_IntPriorities2中,其中IRQ_OSMR0为最高优先级, IRQ_KEYPAD为最低优先级.


2.2
设置ICCR,在空闲模式时只对有效的,非屏蔽的中断响应.


2.3 BSPIntrInit().
该函数对BSP中需要处理的中断进行初始化工作,主要是设置ICMR寄存器使GPIO1(CPLD)和GPIO2为非屏蔽状态,然后通过OALIntrStaticTranslate建立IRQ和sysintr之间的关联.


2.4
对于WinCE来说,中断可以作如下分类:
1> 与BSP无关的Kernel使用的内部中断, 比如
IRQ_OSMR0(for
sched),IRQ_RTCALARM;

2> 与BSP有关的CPU内部中断,比如IRQ_USBOHCI, IRQ_OSM
R1, IRQ_KEYPAD, IRQ_FFUART, IRQ_BTUART, IRQ_STUART, IRQ_DMAC, IRQ_USBFN;

3> CPLD所管理的中断,以GPIO1通知CPU中断的产生,在收到G
PIO1产生的中断后可以通过读取地址0x0A00_0010判断具体的中断源,比如IRQ_GPIO1_MMCCD(MMC_IN_INT, MMC_OUT_INT), IRQ_GPIO1_USBCD(USB_IN_INT, USB_OUT_INT), IRQ_GPIO1_PCMCIA_S0_CSC(CF_IN_INT. CF_OUT_INT),还有在代码中没有处理的SIM卡中断(SIM_IN_INT, SIM_OUT_INT).

4> GPIO2_120产生的中断,通过寄存器ICIP的位10-GPIO_x通
知CPU中断的产生,然后查询寄存器GEDR_x可以判断具体的中断源,比如IRQ_GPIO12_MARATHO, IRQ_GPIO10_ETHERNET,IRQ_GPIO13_UCB1400;

5> 以Installable ISR形式产生的中断,这类中断通常是以DLL的形式安装,在注册表中定义其实际的IRQ中断值,比如IRQ_MMC_DETECT,IRQ_MMC_CONTROL;

2.5 有几种方式可以建立实际中断IRQ到系统中断sysintr之间的关联,对于build-in设备,可以在OEMInit中通过OALIntrStaticTranslate来建立静态的关联;但是对于installable ISRs,可以在注册表中定义实际的中断IRQ,然后通过在KernelIOControl中使用IOCTL_HAL_REQUEST_SYSINTR来实时分配新的sysintr,这有助于驱动的移植.


3
在CPU接收到中断后,对中断的处理是在 OEMInterruptHandler()中,该函数的首先屏蔽该中断,最后得到实际中断IRQ所对应的sysintr的值.


3.1
首先它通过中断寄存器ICHP得到实际的IRQ值;


3.2
对于GPIO产生的中断,通过清空GFER和GRER寄存器来屏蔽相同的中断;对于其余的CPU内部中断,通过清空ICMR寄存器的相应位来屏蔽中断;


3.3
对于Kernel使用的IRQ_OSMR0和IRQ_OSMR2,分别进行处理,得到sysintr;


3.4
对于通过GPIO0,GPIO1和GPIO2_120产生的中断,将IRQ的值进行转化,使之与OAL/intr.c和bulverder_intr.h中所定义的新的IRQ值相对应,此时的IRQ值就不是在ICIP中对定义的IRQ值了;


3.5
接着调用NKCallIntChain(irq)判断该IRQ是否已经被加入中断列表,同时开始调用该中断的ISR程序.


4
当中断处理完成后,调用OALIntrDoneIrqs(),重新使能中断.


[
背景和早期版本]

电源管理的目的是节能,基本的节能方法是使系统适时的进出休眠状态.比如用户按下On/Off按钮,或者监视用户活动的定时器超时,或者应用呼叫api都可以使得系统休眠,用户再次按下On/Off或者其他唤醒中断将使得系统退出休眠.从而可见,电源管理模块和用户活动情况密不可分,电源管理是用户活动所驱动的. WinCE中处理用户与系统交互的部分是GWES,所以早期电源管理工作是由GWES来实现.( GWES:Graphics,Windows and Events Subsystem.图形,窗口和事件子系统.主要负责图形输出和用户交互). 但GWES提供的电源管理模块功能过于粗糙死板:所有子设备只能有On和Suspend状态,应用程序无法得到任何状态转换通知,等等……直到WinCE4.0才引入了电源管理模块用以替代GWES中的电源管理功能.(进一步的,为了方便电源管理模块的集中管理,还需要关闭原来GWES对电源管理功能.方法是注册表HKLM\SYSTEM\CurrentControlSet\Control\Power设置DisableGwesPowerOff=1来禁止GWES插手电源管理.系统是默认禁止的.此外,一些用户活动情况仍旧依赖GWES获得,设置注册表HKLM\system\GWE下的ActivityEvent=PowerManager/ActivityTimer/UserActivity.从而告诉GWES,当鼠标,键盘,触摸屏等输入发生时候,GWES要SetEvent这个全局事件以通知电源管理模块.)

新的电源管理模块提供更完整和灵活的功能,系统电源可以自由灵活设定,子设备电源状态可以单独设定,应用可以获得电源通知等等.

[系统电源]

OEM可以依据需要任意定义系统电源状态,比如On,ScreenOff,UserIdle,SystemIdle,Suspend等.系统电源状态更多的是代表系统电源的一种配置方案,它是各个子设备电源配置的集合.它设定一种可能出现的情景,并且事先拟定了此情景下电力分配策略(哪些子设备打开,哪些子设备关闭).比如,也许On可以代表常规工作的情景,所有子设备打开的状态; ScreenOff可以代表LCD被用户请求关闭的情景,LCD背灯电源被关闭的状态; UserIdle可以代表用户一段时间没有操作的情景,cpu/soc将进入low power的状态; Suspend可以代表设备空闲很久了可以挂起的情景,所有非必要供电的子设备电源关闭的状态;等等…系统的电源状态的定义很灵活而且自由. 可以在注册表定义系统电源状态.比如:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\State\On]

"Default"=dword:0 ; D0

"Flags"=dword:10000 ; POWER_STATE_ON

上面定义了On状态,Flags是附加的状态信息(hints),对应pm.h中的宏定义POWER_STATE_ON.defaule表示在这个状态下所有子设备的默认状态.

电源管理模块的重点之一是制订系统电源管理策略,这包括定义系统电源状态,决定状态间转换的条件.以默认的版本为例子,简单图示如下:

子,简单图示如下:

On:用户与系统交互时候的状态.

UserIdle: 代表用户停止输入,但可能仍然在使用的情景,比如阅读文件.

SystemIdle: 代表用户停止使用设备,但处理器仍然工作的情景,比如,后台文件传输.

Suspend: 代表休眠状态.

用户在使用时候,系统处于On状态,用户停止输入,系统自动转入UserIdle状态,持续没有输入时间后,进入SystemIdle状态,持续一段时间后,系统将自动进入Suspend状态.应用程序也可以调用SetSystemPowerState()来进行状态切换.

在这个基础上,根据自己的平台特点,增加新的策略就基本可以满足常规产品需要.

1. On/Off按键. (A).电源管理模块已经支持了电源按键功能,最直接的办法可以在pdd中增加电源按键定义,按键io的初始化,检测等等,(B).从外部发送消息给电源管理模块来通知按键事件.(C).使用api直接转换状态.即不使用电源管理模块提供的按键功能,直接调用SetSystemPowerState使得系统进入Suspend状态.这是很常见的做法,我们设计一个电源按键的流驱动,检测到按键时候,呼叫api将系统电源转换到Suspend.

2. 加入背灯控制.比如在On状态下打开请求显示驱动打开背灯,在UserIdle和SystemIdle状态下请求显示驱动关闭背灯.

[设备电源]

支持电源管理的设备驱动的实现,存在有大量的例子.简单介绍如下:

电源管理模块并不直接实现对子设备的电源开关控制,子设备的电源控制是由各个设备驱动来控制的.电源管理模块透过设备驱动的IOCTLs来请求设备控制自身电源.系统电源状态是灵活自由设定的,而设备电源状态是固定的,最多有5个0,D1,D2,D3,D4代表Full on,Low on, Standby, Sleep, Off这5个状态.

不是所有的设备驱动都支持电源管理(至少,在电源管理出现前的早期的设备驱动不会支持).电源管理模块对设备驱动提出了一个规范和架构,满足规范的驱动纳入电源管理.对于流驱动控制的设备,要支持电源管理要满足的条件,简单来说有:1.声明自己是支持电源管理的(Iclass值).2.驱动中实现电源管理模块所要求的IOCTLs.3.驱动加载时候要汇报所支持的电源状态和相关特征.4.***_PowerDown和***_PowerUp接口接收系统休眠和唤醒通知.此外,设计驱动还应该了解:设备不一定具备所有5种状态,但至少可以工作在D0;电源管理模块可能会要求设备进入任何设备电源状态,并不仅仅是设备所汇报自己支持的那几个;如果被要求进入不支持的状态,应该进入另一个它所支持的更高功耗的状态;当前状态不需要重复设置;设备电源状态不一定和系统的电源状态同步.除了流驱动外,还有许多内建驱动需要支持电源管理功能.简单总结:1.显示驱动通过ExtCode接口(SETPOWERMANAGEMENT命令,类似IOCTLs)来控制显示驱动的电源,还控制背灯.2键盘驱动的接口KeybdDriverPowerHandler.3.触摸屏是TouchPanelPowerHandler.4.内建网络miniport驱动是MiniportReset接口.5.PCMCIA驱动是PowerUp和PowerDown.还有打印机,红外等一些内建驱动.

[OAL对电源管理的支持]

[系统的 idle状态]

当没有线程准备运行时候,内核就调用OEMIdle().这个函数在bsp中,可以由OEM来修改定制.一般我们在这个函数里面会要求cpu进入low power状态节省电流消耗.一般的cpu/soc都提供了对应idle的睡眠模式.当中断发生或者唤醒事件发生时候,要保证cpu快速离开idle状态,返回运行状态.

系统idle状态和前面说的UserIdle状态是不同概念,前者是cpu负荷情况驱动,代表系统空闲;后者是用户活动驱动,代表用户空闲.

一个OEMIdle()的推荐流程:

根据dwReschedTime变量来计算下次唤醒时间

判断sleep类型,假如需要,调整唤醒时间

Idle处理器和时钟

中断发生

判断唤醒源

更新CurMSec, idle计数值.

[系统suspend状态]

当用户按下OFF按钮或者应用调用api进入suspend状态时候,内核会调用OEMPowerOff()函数.在OEMPowerOff()函数里面实现系统挂起,并且系统唤醒后继续从OEMPowerOff()被挂起处执行. OEMPowerOff()时候要进入睡眠模式,睡眠模式根据cpu芯片的sleep模式来选择,要选择最低功耗的模式.如果cpu芯片提供的最低功耗模式是PowerDown模式,处理工作比较复杂,因为唤醒后是从reset处开始执行,要恢复挂起时候的环境,使得应用程序不知道自己被挂起过.一般按照这样流程来处理:关屏,清framebuffer, 保存必须的寄存器到内存, 设置io, 保存通用寄存器, 保存wakeup地址, 静止中断,清除cache, 使能唤醒源中断, 设置sdram自刷新, cpu进入PowerDown. 唤醒后的流程相反即可. 对于PowerDown模式之外的其他模式,比如慢时钟模式, 处理则简单很多,最重要的是设置唤醒源(一般是任何中断可唤醒), sdram进入自刷新状态.

[SDRAM的控制]

SDRAM的耗电比较大,一般是系统里面除了lcd背光外,sdram是最大的电力消耗设备.常见有mobile sdram和normal sdram这2种,mobile sdram相对于normal sdram增加了温度补偿自刷新,局部阵列自刷新,深度休眠特性,更加适合功耗限制设备,(但mobile sdram工作在更低电压(1.8~2.5v),我想,对有些3.3v总线的cpu未必适合,因为总线会增加很多电平转换的电路.)

在OEMPowerOff()函数里面,保存好当前环境到sdram,然后使得sdram进入自刷新状态,cpu就可以进入最低功耗的sleep模式.唤醒后需要退出自刷新状态.

[应用层于电源管理]

电源管理模块也提供了应用层接口,使得应用程序也可以参与到电源管理.

应用层可以通过SetSystemPowerState()来设置系统电源状态,可以通过SetDevicePower来设置子设备电源状态,可以通过SetPowerRequirement通知电源管理模块将子设备设置在特殊电源状态下,不随系统电源改变.此外,电源管理还提供了消息队列,应用层还可以通过RequestPowerNotifications函数请求电源管理模块发送相关消息(PBT_RESUME, PBT_POWERSTATUSCHANGE, PBT_TRANSITION, PBT_POWERINFOCHANGE).

设计应用程序也许有几点值得考虑:不要无谓占用cpu,尽可能快的让出cpu.比如一个很小的动画,哪怕只占1%的cpu也会导致一些系统无法进入低功耗.这里是2点建议1)当应用不在foreground时候,停止占用cpu.(2)用户没有和应用交互时候,停止应用对cpu的占用.另外一些应用也许是相反情况的,播放媒体文件时候,当开始播放时候,不希望自动进入suspend模式.可以(1)每隔一些时间就reset一次定时器.(2)或者设置所有定时器为0,停止电源管理(tcpmp就是这样的).

[电源管理的系统实现]

电源管理模块实体是一个动态链接库pm.dll来实现的.可以在pb的catalog窗口中选择电源管理组件添加到os中.如下图,微软提供了2个选择(二选一).第一个代表完整功能,所有api全功能实现,第二个代表空实现(形式上提供接口,但空函数).

电源管理模块的代码结构是分层的,MDD PDD.MDD是抽象公共库,不需要改动,PDD是平台相关,主要改动都在PDD.针对平台特性,微软提供了2种类型PDD示例.一种是default,另外一种是pda版本的.默认的情况,使用的是default.如果要使用pda版本的,需要在系统中指定环境变量SYSGEN_PM_PDA. default和pda版本的主要区别:

default版本定义了4种状态:On, UserIdle, SystemIdle, Suspend;

PDA版本定义了On, ScreenOff, Unattended, Resume, Suspend.

default版本的简单描述:UserIdle状态是描述用户在使用但没有操作,比如阅读.SystemIdle状态描述用户停止使用,但系统仍然工作,比如文件传输.

PDA版本简单描述:ScreenOff状态描述用户请求把屏幕背灯关闭.是用户主动关闭的情况,区别于UserIdle,UserIdle是自动的.Unattended状态表示后台工作,用户不会对其察觉的情景,比如ActiveSync每5分钟唤醒系统同步,然后继续suspend; Resume状态描述唤醒后情景,比如唤醒后在指定时间内决定转到哪个状态,否则继续suspend.

[定制电源管理模块的方法]

Pm.dll是由device.exe加载的,首先device.exe当然是必须的,在pb的catalog中检查Device Manager组件,或者检查SYSGEN_DEVICE变量.其次,仍旧应该选择上图的电源管理组件power management full.

方案一(推荐方案):在bsp的驱动目录中新建一个pm目录,在这里完成电源管理模块PDD部分的实现,并链接MDD最终生成一个pm.dll替代原来系统的pm.dll.

PDD参考微软提供的代码platform.cpp,主要修改是增加状态转换的动作执行单元.

方案二:完全不修改电源管理部分,因为默认的PDD在状态转换时候虽然没有动作,但是广播了PBT_TRANSITION消息,可以截获这个消息来进行状态转换.这样作法不如方案一直接.如果是进程实现,还浪费一个宝贵进程资源.

[影响系统功耗各方面考虑]

1.系统时钟周期

典型的WinCE系统时钟周期是1ms,增加时钟周期有助进一步降低设备功耗.在OEMInit()àOALTimerInit()修改系统时钟.

2.可变系统时钟节拍Variable Tick Scheduler

典型设计里wince每毫秒产生系统时钟中断,那么每隔1ms都会使得idle退出,如果发现没有线程就绪时候继续idle. 对有功耗限制的设计,可以考虑改变系统时钟节拍后进入idle状态.这样在预期的时间段里,idle状态不会被无谓的系统时钟中断唤醒.

3.LCD背灯的调节策略

早期的设计使用一个独立的驱动来实现背灯的控制和调节策略.简单介绍背灯驱动原理:背灯驱动启动一个监视工作线程,不停等待3个事件:

1. BackLightChangeEvent

2. PowerChangedEvent(供电电源发生变化,比如插手了AC电源,会获得了这个事件)

3. PowerManager/ActivityTimer/UserActivity(用户输入事件)

从注册表中读取超时值,当超时事件发生,则将系统背灯关闭.背灯关闭期间,用户重新活动时候,发生第3个事件,则打开背灯.注册表的超时值决定了背灯工作时间.类同pc上设置屏幕保护时间.此外,背灯驱动也需要提供对系统电源状态切换的支持.power down时候要关闭背灯,power up时候打开背灯.

电源管理模块可以定义一种系统电源状态来描述背灯关闭的情景(比如在UserIdle或者ScreenOff状态时候关闭背灯,On状态时候打开背灯)所以,背灯驱动可以被取消.

4.IO口的漏电流

空载IO避免设置成为输入口,考虑悬空输入导致门电路开关,造成电流消耗.负载IO依照情况设定,一般设置输出低.

5.电池驱动

电池驱动最主要的功能是监视系统电力.它提供了其他模块和应用对系统电源状态的查询,查询是AC,还是battary供电,查询电池电量等.
非常不错,非常感谢
学习了,谢谢