分享

WINCE中断结构分析

 cici5177 2011-06-17
  • 编辑时间: 历史版本编辑者:wgr
    • 内容长度:图片数:目录数:
    • 修改原因:

    Windows Embedded CE  中断结构分析
    关键字:WinCE,中断,体系,结构
    摘要:本文主要以WinCE  .NET 5.0 为操作系统平台,ARM为硬件平台,分析了WinCE下中断的结构与实现方式
    作者:ETDrawer@ARMCE

     

    目录

    [显示全部]

    前言

     

      在嵌入式系统当中,对于中断的处理是非常重要的一部分内容。许多外围设备都需要通过中断来实现自己的功能或者与系统内核交互,系统时钟本身也是由时钟中断产生的。所以本文旨在分析WinCE下的中断的结构,以及常用的几种实现方式,来帮助读者了解WinCE中断体系及实现自己的中断处理结构。
      下面的介绍如非特殊说明,均以 ARM 架构为硬件基础,操作系统代码使用 Windows embedded CE 5.0。

    WinCE中断体系结构

    从结构上看,WinCE中断涉及4 层,即:硬件层、内核层、OAL层、IST处理层。了解这 4层之间的交互传递将对我们了解WinCE中断处理很有帮助。

    1 硬件层:
      硬件层就是实际触发中断的硬件,这里主要有两方面作用,一个是触发中断,第二个是enable/disable硬件中断。

    2 内核层:
      这一层由内核来处理,包括中断异常产生后跳转到相应的ISR,以及根据SYSINTR 来触发相应的Event。关于SYSINTR 和 IRQ 的概念后面会说明。

    3 OAL层
      这一层主要就是我们需要实现的代码了,来识别硬件IRQ,对应到SYSINTR。

    4 IST处理层
      一般使用 IST 来做实际的中断处理,这样不会占用很多的锁定系统时间来处理中断,但是对中断的实时性大打折扣

     

    IRQ,ISR,IST和 SYSINTR

    说到这里先解释下IRQ,ISR,IST及 SYSINTR 的概念、意义及相互关系。

    IRQ:
    IRQ (Interrupt request),中断请求。
    这里就是外设或其它请求服务的设备发出来的中断。属于硬件中断,可能是一个电平触发的GPIO 中断,也可能是内部DMA的一个中断。

    ISR:
    ISR (Interrupt service routine),  中断处理程序。
    WinCE实际上使用 ISR来处理中断,即默认的中断入口函数,在 ARM体系中,系统默认的ISR就是 OEMInterruptHandler

    IST:
    IST (Interrupt service thread),  中断服务线程。
    在 ARM 的结构中,ISR 一般不会用来进行任何实际的操作,而只是返回一个 SYSINTR,实际的操作全部在IST中完成,IST一般是在Device Manager 的一个线程中运行的一段高优先级的应用程序代码,用来服务实际的中断请求。

    SYSINTR:
    在 WinCE中,SYSINTR 就是 system interrupt,就是一个操作系统的逻辑中断。一般对于中断的处理方式都是将一个IRQ映射为一个或者多个(中断共享)SYSINTR,而后,在实际的ISR中根据IRQ返回一个对应的SYSINTR用来告诉操作系统需要服务的逻辑对象。

    使用逻辑中断的好处当然就是可以实现虚拟的中断(一个 SYSINTR 就被 OS 认为是一个独立中断)和中断共享(单 IRQ对应多 SYSINTR)。 逻辑中断是WinCE需要处理的实际对象,至于这个对象是一个共享的IRQ,还是一个虚拟的中断,还是独立的物理中断,系统并不过问,从而隔离了硬件与逻辑,我们的 ISR 需要做的也正是这种物理中断到逻辑中断的映射。

     

    WinCE中断处理原理


      下面基于 ARM 体系,来介绍 WinCE 中断处理的流程与原理。
      对于一个硬件IRQ中断,系统内核在捕获之后,会交给 OEMInterruptHandler 处理,这个函数就是我们实现中断处理的中心函数,首先我们从CPU 的寄存器里获得中断的信息,这些信息告诉我们到底是哪个 IRQ 源触发了中断。
    一般实现中断服务的方式有以下几种:

    1. 简单中断处理——ISR模型
      最简单的中断处理办法就是在ISR中直接处理,这里就是指在OEMInterruptHandler中直接对中断源进行判断,然后调用服务程序。

      这种方式的优点和缺点一样明显。
      优点:快速的响应了中断处理,使系统的实时性能增加不少
      缺点:由于进入OEMInterruptHandler的时候关闭了系统中断(当然你可以在ISR中自己打开中断,不过处理起来较麻烦),所以如果处理时间过长,其他中断很可能被忽略,造成中断丢失。并且系统被锁定,使得系统任务的响应变慢。

    2. 中断处理的一般流程——IST模型
      前面看到了 ISR 模型的优缺点。作为WinCE,主要定位还是民用的消费类电子产品,所以,对于中断响应的实时性不是特别高,所以系统的运行响应速度就显得更加重要。而且目前的嵌入式设备的处理速度越来越高,已经几乎达到了当时奔 3 的水平。所以 ISR 的模型并不适用于WinCE。

    如果把中断服务程序当作一个系统进程或者线程来处理,这样就不会造成系统被锁定,中断被屏蔽等问题,使得中断服务程序和其它进程、线程一样被系统管理调度。于是就有了IST的概念

    IST 模型的想法是,在 ISR 中判断到具体的中断源 IRQ,就返回一个标志,用来标记需要哪个程序来服务中断,然后重新打开中断,恢复系统调度,系统根据这个标志来激活相应的程序来服务中断。

    这个就是最常用的中断处理模型。使得中断被关闭,系统被锁定的时间最短。
    在 WinCE中,经常使用的就是建立中断服务线程(IST),然后以IRQ 来申请一个系统逻辑中断号(SYSINTR),创建一个事件(Event),将 Event 与 SYSINTR 绑定,随后 IST阻塞在等待Event上面。

    ISR 中只给系统返回一个 SYSINTR,系统根据绑定关系激活相应的Event,使得随后的 IST得以运行。

    这就是 IST的一般服务流程

    IST模型的缺点就是中断服务的延迟较大,从 ISR 返回,到 IST开始运行,中间需要花费一定的时间,相比 ISR 模型的中断实时性较差,但是这种代价对于我们的应用是值得的。

     

    IST模型的实现

     

      下面我们来看IST模型具体在我们的驱动中是如何实现的。
      上面已经介绍了 IST 模型的一般服务流程,下面我们针对驱动程序实例,来分析具体的实现步骤。
    1 驱动程序中 IST的构建与中断初始化
    上面介绍的 IST流程中,很多步骤都是WinCE的内置支持,也就是说你只要调用相应的 API就可以实现功能了,不需要自己编写太多的代码。只需要实现一些流程代码。 首先是驱动程序端的中断初始化。假设现在有一个驱动程序,需要服务中断源,IRQ为 0x12。

    a)  以 IRQ 为参数,申请 SYSINTR,方法为调用
    KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &(dwIrq),  
                                     sizeof(UINT32), &dwSysIntr,  
                                     sizeof(UINT32), NULL)
    其中 dwIrq为IRQ号,即0x12 dwSysIntr 为系统中断号,也就是调用返回的结果存放的位置

    /* ARMCE深入
    实际这个 IOControl调用的是函数 OALIoCtlHalRequestSysIntr,该函数的位置在 WINCE500\PLATFORM\COMMON\SRC\COMMON\INTR\COMMON\Ioctl.c
    最终调用 OALIntrRequestSysIntr,这个由 OEM 自己实现,一般就是维护了 2张表,一张是从 SYSINTR 到 IRQ 号码的映射表,另一张是 IRQ 到 SYSINTR 的映射表,两者是对应的关系。 这里注意一点,一个IRQ 映射为多个SYSINTR 是支持的,也就是共享中断,但是系统默认的实现并没有支持一个 SYSINTR 映射为多个 IRQ,你可以自己实现。


    这两张表对于后面在OEMInterruptHandler中由IRQ查找SYSINTR提供了方便
    */

    b)  创建与 SYSINTR 绑定的Event
    由于我们的IST是需要Event激活的,所以这里申请一个 Event。
    申请 Event的步骤比较简单和标准
    hISTEvent = CreateEvent(0,FALSE,FALSE,NULL);

    c)  将SYSINTR 与Event绑定
    调用 InterruptInitialize(dwSysIntr,hISTEvent,0,0)将 SYSINTR 与 Event 绑定,用来在OEMInterruptHandler 中返回SYSINTR 时激活相对应的 Event

    /* ARMCE深入
    InterruptInitialize 的 代 码 参 考 SC_InterruptInitialize , 在E:\WINCE500\PRIVATE\WINCEOS\COREOS\NK\KERNEL\Intrapi.c中
    主要做两件事情:
    #1 将 Event 的 Ptr 填入一个数组,这个数组是记录每个 SYSINTR 对应激活的Event

    #2 调用OEMInterruptEnable,使能SYSINTR所对应的IRQ,并且将标志 IRQ,被引用次数的变量加1(WinCE6 的代码中未见这一变量)
    */


    d)  创建一个 IST,并且等待hISTEvent
    到了这一步,中断关于系统方面的初始化基本结束,剩下的就是创建一个 IST,然后等待 Event来运行中断服务代码,例如:
      
    while(TRUE) {
    WaitForSingleObject(hISTEvent,INFINITE) ==
    WAIT_OBJECT_0)


    }

    这里需要注意的是IST什么时候创建都可以,但是在InterruptInitialize之前不要运行 IST 以等待这个 Event,也就是说在 InterruptInitialize 之前不要使用这个Event,否则会导致InterruptInitialize失败。
    还有就是不要使用WaitForMultipleObjects来等待Event。
    在中断处理完成之后需要调用 InterruptDone,参数为该中断的SYSINTR。来通知系统,中断处理完成,系统重新使能该中断


        到了这里,驱动的中断初始化工作就全部完成了。


    2  OEM层需要做的工作
    OEM 层 主 要 是 控 制 IRQ 的 enable  (BSPIntrEnableIrq) 与 disable (BSPIntrDisableIrq), 当然要初始化 IRQ 的配置,使其在正确的触发状态,比如上升延触发

        至此一个中断处理的IST模型就实现了,系统在IRQ 触发时调用映射函数,获得相应IRQ 的 SYSINTR,然后返回合法的SYSINTR给系统,系统查表激活相应的Event,对应的 IST进行中断服务,然后再次等待 Event。

      3  中断资源的释放
        当不需要当前中断继续服务的时候可以通过调用 KernelIoControl 来释放申请到的SYSINTR,具体格式为:
      KernelIoControl(IOCTL_HAL_RELEASE_SYSINTR,  dwSysIntr,  sizeof(DWORD),
    NULL, 0, NULL);

      其中 dwSysIntr 就是需要释放的 SYSINTR 号码。

    /* ARMCE深入
      这里实际调用的是OALIntrReleaseSysIntr这个函数,由OEM自己实现。所做的动作也很简单,就是从SYSINTR 到 IRQ 和 IRQ 到 SYSINTR 的那两个映射关系数组中删除映射关系。
    */

     

    可安装的ISR


      1    为什么要使用可安装 ISR(以下简称 IISR)
        需要 IISR 的目的有两种:
    I. 动态的安装中断程序
    在系统运行的过程中注册中断,这种中断一般是不可预知设备的中断,常用在总线设备中,比如PCI设备

    II. 中断共享
    当多个中断源使用同一个中断号(IRQ)时,就需要使用 IISR 来实现了 当然如果是需要动态安装的共享中断就最适合了。

    因为我们的 IST模型中,中断服务程序就是驱动中的IST,IRQ与 IST是一对一的关系。所以在需要动态添加一个中断服务程序的时候就没有办法处理了。
    同样由于 IRQ 与 IST 的一一对应关系对于一个 IRQ 对应多个需要服务的 IST 就同样没有办法处理。
    基于上面的情况才会有IISR 的出现,IISR 从本质上是在ISR 中提供了一个接口,当ISR 调用 NKCallIntChain时,以此IRQ为参数,在链表中依次查找是哪一个服务程序来服务这次 IRQ,然后返回相应的 SYSINTR,此后的动作与 IST 模型就基本一样,通过SYSINTR 来激活Event,从而启动相应的 IST。
      所以 IISR 的实现就是动态的向某一个 IRQ服务程序链表添加结点的过程。

    2 IISR的实现
    下面我们来看看IISR 的具体实现步骤:
      首先我们需要了解IISR 服务中断的实现原理,如上面描述的,根据IRQ,来顺序调用链表中的中断处理程序。所以我们可以有两个选择,一个就是类似 ISR 模型,直接在链表中的中断处理程序中判断是不是自己的中断,并且做处理。还有一种方式就是类似 IST 模型,如果判断是自己的中断,则返回一个SYSINTR,以此SYSINTR 来激活IST。
      无论哪种方法,关于注册中断和查询中断的方式是一样的,下面我们来看下如何将中断程序添加到链表,又如何在中断来的时候去搜索链表。
      Microsoft提供了一个通用的IISR的处理模型,叫做 GIISR,这是一个以 IST模型处理 IISR 的模块,源程序可以在WINCE500\PUBLIC\COMMON\OAK\DRIVERS\GIISR找到。熟悉了 GIISR,想实现自己的 IISR 处理程序或者基于 ISR 模型的处理,都比较简单了。
      下面我们就分析这种比较通用的处理 IISR 的模型。
    a)  首先我们需要以 IRQ 来申请 SYSINTR,并且将 SYSINTR 与 Event 绑定,这些步骤与IST模型中介绍的一样,这里就不重复叙述了,IISR 在这里与 IST模型并没有任何的不同。其与 IST 模型的唯一不同点就是如何根据 IRQ 来判断相应的SYSINTR。 在 IST 模型中是 OEM写死的一个判断程序,而 IISR 可以动态来注册一个判断程
    序给系统调用,这是唯一的实现区别。


    b)  下面我们需要注册可安装中断程序的 dll,和 dll中的中断处理函数,并且将他们与某一个特定的IRQ相关联
    这个过程是通过调用LoadIntChainHandler函数来实现的。这里我们的中断服务dll叫做”giisr.dll”,处理函数名叫做”ISRHandler”,对应IRQ为0x20,则函数调用形式如下:
    HANDLE  hIsrHandler  =  LoadIntChainHandler(TEXT(“giisr.dll”),
    TEXT(“ISRHandler”), 0x20);

    /* ARMCE深入
    LoadIntChainHandler的源代码可以在WINCE500\PRIVATE\WINCEOS\COREOS\NK\KERNEL\Loader.c中找到,就是函数SC_LoadIntChainHandler WinCE6 在 KDriver.c 中的 NKLoadIntChainHandler,两者功能大致相同,只是在一些结构体定义上略有不同

    其主要功能就是加载 dll 到 NK,并且获得三个函数的指针,一个是CreateInstance,一个是你刚才传进来的处理函数,这里就是 ISRHandler,还一个就是 IOControl,后面会用到。
    首先调用CreateInstance 来获得一个实例的数据,这个数据就是一个 index,用来标示其中的一个中断处理程序的索引。
    这里可能需要解释下 GIISR 的处理原理。我们所有的可安装中断都通过 giisr.dll里面的 ISRHandler来处理,在NKCallIntChain被调用的时候,会遍历所有注册到这个 IRQ 的中断处理函数,这里全部都是同一个函数 ISRHandler。那么就需要可以区分每一次调用,所以就在 giisr 模块里面维护一个数组,每一个中断服务程序占用一个数组成员,这些数组成员的Index就是他们在giisr里面的唯一标识。所以 CreateInstance 的任务就是查找数组,找到第一个空闲位置,将 Index 返回。在ISRHandler 被调用到的时候,会将这个Index传递进去,根据这个 Index,ISRHandler 就能够知道是数组中哪个成员正在被查询,进而确定是不是这个成员需要处理中断请求,进而确定该返回的 SYSINTR。详细的步骤下面会一一说明,大家先有一个概念,在从 CreateInstance 返回了可用的数组 Index 之后,调用 HookIntChain,此函数在Kdriver.c中。这个函数的功能比较简单,我们先了解下共享中断在系统中的处理。
    前面有所提到,在调用 NKCallIntChain 时会遍历一个链表,每个链表头就是系统维护的一个数组中的一个成员,每一个 IRQ 号码都对应数组中的一个成员,这个数组就是 pIntChainTable。IRQ 按照其号码在数组中对应相应的数组成员,注意pIntChainTable 是一个 256 个成员的数组,这也就意味着你的 IRQ 号码数字不能超过 255,当然这是指你传递进来的 IRQ 号码,如果你的 IRQ 号码都是大于255 的,可以对实际 IRQ 号码做处理,保证其数字值在 0-255,比如对 256 取模,这样当然就可以传递进来了。
    pIntChainTable中的每个元素都是一个链表的头,当你向一个IRQ 添加中断处理的时候,实际是建立了一个新的PINTCHAIN元素,然后向pIntChainTable中的IRQ 索引位置去添加,如果该位置不为空,则查找这个元素指向的下一个元素,在这个单向链表的操作下,将新的中断处理程序加入。
    这个加入工作就是HookIntChain做的 */

    c) 

    上一步在GIISR中通过CreateInstance把这个新的中断处理程序加入GIISR自己的管理。GIISR 的主要作用就是判断当中断来的时候,是不是其内部数组中的某个成员需要服务中断。所以需要更多的信息用来判断中断是否匹配当前的中断服务程序,所以我们需要把信息传递进去,这里就是调用KernelLibIoControl。
    具体的方法为:


    KernelLibIoControl(hIsrHandler,IOCTL_GIISR_INFO,&giisr_info,
    sizeof(GIISR_INFO), NULL, 0, NULL);


    这里就是把 giisr_info 的内容传递给刚才注册的中断,giisr_info 是一个GIISR_INFO的结构体,其内容如下:


    typedef struct _GIISR_INFO {
           DWORD SysIntr;                            // SYSINTR for ISR handler to return
    (if associated device is asserting IRQ)
           BOOL CheckPort;                          // If true, check port to see if device is
    asserting IRQ
           BOOL PortIsIO;                            // Port is IO port (possibly true only for
    x86)
           BOOL UseMaskReg;                        //  If  true,  read  from MaskAddr  to
    obtain mask
           DWORD PortAddr;                          // Port Address
           DWORD PortSize;                          // Port data width in bytes
           DWORD Mask;                                  // Mask  to  use  on  data  port  to
    determine if device is asserting IRQ
           DWORD MaskAddr;                          //  Address  of  register  to  use  as
    mask
    } GIISR_INFO, *PGIISR_INFO;

    这些成员都是需要设置的,具体含义如下

    SysIntr:这个中断所对应的系统中断号,即第一步申请到的 SYSINTR,系统在确定是当前的设备出发的 IRQ 之后会返回这个 SysIntr
    CheckPort:一般为 TRUE,如果为 FALSE 则直接返回 SysIntr,而不是判断是不是当前设备触发的中断

    PortIsIO:是不是IO端口,这个可能只是在 x86 下使用,我们置为 FALSE

    UseMaskReg:是否使用地址来获得 Mask,如果为TRUE,则Mask 字段无意义

    PortAddr:实际上是可以判断中断是哪个设备出发的那个寄存器的地址

    PortSize:PortAddr的位宽,标志PortAddr是1字节(BYTE),2 字节(WORD),还是 4字节(DWORD)的寄存器,其他不支持

    Mask:一个掩码位,在UseMaskReg为FALSE 的情况下与PortAddr的值进行位或运算,如果不为0,则确定为当前设备触发的中断
    MaskAddr:当 UseMaskReg为 TRUE 的时候,使用这个地址来获得掩码的值,

    给动态的判断中断提供了接口
    仔细看了上面各个成员的介绍,大家就应该可以了解 GIISR 是如何判断中断是不是当前设备产生的。
    所有的判断依据就是这个结构体。一般我们会将 CheckPort 置为 TRUE,然后让系统去读取PortAddr地址处的值,这个值可以标志是否为当前设备触发的中断。
    获得这个值以后,与一个 mask 值进行或运算(&),如果值不为 0,则认为是当前设备触发的中断。这个 mask 值在 UseMaskReg 为 FALSE 时是成员 Mask,反之是从 MaskAddr 地址处获得。

    /* ARMCE深入
    KernelLibIoControl 的 源 代 码 可 以 在 Kdriver.c 中 找 到 , 就 是SC_KernelLibIoControl 。 SC_KernelLibIoControl 直 接 调 用 的NKKernelLibIoControl,这个函数同样在 kdriver.c 中。分析源码可知,就是调用了 giisr.dll 中的 IOControl,参数为 IOCTL_GIISR_INFO,就是传递进去了一个 GIISR_INFO 结构体,用来给 Index 标示的数组元素赋值,这个 Index 就是前面CreateInstance返回的那个数值,通过 hIsrHandler传递进去。 */

    d)  下面就是启动IST,等待Event,这里和IST模型没有任何区别。

    到这里全部的初始化就完成了,可以看出,和IST模型相比就是多了两步 b)和 c),这两步决定了中断判断的方式,这也是 IISR的根本所在。

      
    3  中断的判断
    下面详细介绍下可安装中断在 ISR 中被判断的过程。
    同 样 , 中 断 到 来 时 进 入 的 函 数 是 OEMInterruptHandler , 在OEMInterruptHandler中会调用NKCallIntChain来遍历该IRQ对应注册的IISR。这是一个链表结构,所以对中断判断程序是一个顺序调用的过程,即先注册的设备先判断,如果判断到正确的结果,则返回合法的 SYSINTR,OEMInterruptHandler也同样返回这个值。所以即使后面的设备也符合条件,也不会被执行。如果整个链表中都没找到正确的设备,则返回 SYSINTR_CHAIN。OEMInterruptHandler 在判断到返回结果为SYSINTR_CHAIN 时,即表示请求中断的设备不在链表中。

    /* ARMCE深入
    NKCallIntChain 的代码在 KDriver.c 中,是个非常简单的函数,以传递进来的IRQ 为 Index 找到链表的头,然后去调用各链表节点的 hIsrHandler,并且以CreateInstance 的返回值为参数。当有返回的值不是 SYSINTR_CHAIN 时,就返回 这个值, 反之继续 查找, 直到链表 尾部, 如果还没 有找到, 就返 回SYSINTR_CHAIN。
    */

    4  自定义的 IISR
      我们可以不使用GIISR,而自己实现IISR功能,只要知道了IISR 的原理。

    当以某一个寄存器或者地址的值,不足以判断到底是系统中哪个设备触发的中断的时候,GIISR 就不是这么好用了。比如多个不同的外设,使用同一个GPIO 来触发中断。外设需要读取多个寄存器或者需要一个复杂些的计算(不只是简单的一个&操作)才能判定中断是否是其产生的。这时候我们需要使用自己的一套 IISR的处理方式。当然我们不希望去改动整个微软对于 IISR 的处理结构,所以我们就要区分开来上面介绍的 GIISR 的模型里,哪些是微软的架构,哪些是GIISR 自己的实现。

      微软的 IISR 架构:
    a)  首先需要使用 LoadIntChainHandler 去注册这个 IISR 的处理判断程序的dll,在这个 dll中除了需要一个判断处理程序(也就是通过 LoadIntChainHandler传递进去的那个参数),还需要一个CreateInstance的函数,这个是必须的。在你不改动微软内核的情况下,名字也是固定的,详细地函数定义,可以参考 GIISR 的 CreateInstance。至于 IOControl,最好也参考 GIISR 的定义一个,如果不需要去调用KernelLibIoControl 的话,应该是可以不实现的。

    b)  在OEMInterruptHandler 中调用NKCallIntChain去遍历链表,在调用处理函数时将CreateInstance 的返回值作为参数传递进去,如果处理函数在判断到不是自己触发的中断,应该返回 SYSINTR_CHAIN,否则返回一个有意义
    的 SYSINTR 值

    c)  在需要注销这个IISR 的时候,调用 FreeIntChainHandler,需要在dll中实现 DestroyInstance 这个函数,被系统调用,这个是可选的。

    GIISR 自己的实现:
    使用同一套代码管理这些中断处理程序,每个中断处理程序在内部的数组中占用一项,这些项目记录着中断处理程序激活使用的SYSINTR 以及判定其触发中断的标准。这个标准就是读取某个寄存器或地址来用掩码来判断。同时这个数组项目对应的结构体数据就是通过KernelLibIoControl 传递进去的。

      所以对于需要使用自己的特殊方式判断中断触发的程序,可以使用自己的中断判别程序。
      我们可以为每一个中断外设都实现自己的处理dll。在调用LoadIntChainHandler时传递进自定义的一个 dll 与处理函数,CreateInstance 一定要实现,不过返回值可以忽略。
      在处理函数中,我们直接根据自己的外设来判断中断条件,然后返回相应的SYSINTR。

      其实使用 CreateInstance 的含义就是想把同一判断类型的设备使用一套统一的处理函数来判断。CreateInstance 的返回值就是区别不同设备的这个 Index。

    5 IISR资源的释放
    当我们需要注销这个 IISR 的时候,需要调用 FreeIntChainHandler,来将该中断服务从链表中删除。

    FreeIntChainHandler(hIsrHandler);
    hIsrHandler 就是LoadIntChainHandler 的返回值。
      /*ARMCE 深入
    FreeIntChainHandler 函数实现同样在 loader.c 中的SC_FreeIntChainHandler。所做的动作就是,如果有 DestroyInstance 函数,则调用 DestroyInstance。然后调用 UnhookIntChain。UnhookIntChain的函数实现在 kdriver.c 中,是一个标准的单向链表删除其中一个元素的操作,这里不再详细解释。
      */

    6 使用 IISR的注意事项
    由于 IISR 是动态的被加载的,也就是说注册的 dll 会被加载到内核空间。所以不能调用任何非内核的函数。
    并且,如果将 IISR的 dll放在 bib文件的MODULES section里面,需要设置”K”属性,如:
    giisr.dll               $(_FLATRELEASEDIR)\giisr.dll       NK   SHK

    如果放在 FILES section 里面,需要保证没有 fixup类型的变量。 
        

    WinCE 中断的延迟


      1 造成中断延迟的原因

    这里画出了从硬件中断发生,到 IST 运行的全部过程,中间就是我们要研究的延时部分。 图中的”IST 延迟”标志,实际上就是我们所说的中断延时。但是将 IST 延时细分,可以分为如下几部分延时:
    a)  ISR 延时
    这部分延时是指从硬件触发中断到进入OEM的 ISR程序的时间,在 ARM 体系下,就是进入OEMInterruptHandler 的时间。

    b)  ISR 执行时间
    在 OEMInterruptHandler 中判断 IRQ,并且返回相应的 SYSINTR 的时间,也是OEM最主要把握的时间。
    c)  从ISR 返回到 IST运行的时间
    主要就是系统根据ISR 返回的SYSINTR激活相应的Event,系统调度运行,到 IST执行的这段时间

    2 如何降低中断延时
    上面的中断延时原因中,

    a)是我们一般不去过问的部分,这段代码是微软实现的,原则上是可以改的,但是收效不大,而且可能引入 bug,建议不要去动。

    b) 这 一 部 分 就 是 我 们 着 重 需 要 管 理 的 了 , 因 为 这 一 块 就 是OEMInterruptHandler 函数的实现,是 OEM 自己实现的代码。也就是说,判断中断源,返回SYSINTR的过程是我们唯一较可行的控制中断延时的地方。
    关于这部分的优化,首先就是尽量用较简单的逻辑判断 IRQ,来返回相应的SYSINTR,而且 OEMInterruptHandler 会调用NKCallIntChain,所以 IISR 的链表长度与 IISR处理函数的效率也是影响中断延时的重要因素。


    一般在判断 IRQ 的过程中,我们会把最可能出现、最频繁出现的IRQ 放在最前面判断,比如系统时钟。这样在刚进入OEMInterruptHandler就可以判断到IRQ,并返回,节省了时间,提高了效率。同样这种方法也适用于 IISR,将最可能出现,最频繁出现的设备放在链表的前面,可以提高遍历的效率。

    c)是我们可以部分控制的,关键就是IST的优先级,其他部分我们也使用微软默认的实现。


     

    总结

     

    WinCE 下提供了较灵活的中断处理方式,包括ISR,IST,IISR 三种主要方式。 对于 ARM 架构下的系统开发,我们常用的就是 IST 与 IISR 两种方式。两种方式从本质上都是通过返回正确的SYSINTR来激活相应的 IST来服务中断。不同点只是对IRQ 判断的方式,IST 是内置的 OEM 写死的判断程序,而 IISR 是通过一个预留的接口,来动态注册判断程序,从而给了系统一个注册新中断的机会。同时 IISR 可以实现中断共享,在一个 IRQ 上通过链表的方式不断添加判断程序,从而让多个设备共享同一个 IRQ,同时又可以有自己独立的中断判断程序。


    在最后,我们分析了中断延时的一些因素,这里并没有详细的进行分析,只是提出了一些降低中断延时的方法。

    参考文献:


    1.  Microsoft Windows CE .NET 中的中断体系结构    , Nat Frampton
    2.  Platform Builder for Microsoft Windows CE 5.0 Help, Microsoft
    3.  Microsoft Windows Embedded CE 5.0 source code, Microsoft



      本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
      转藏 分享 献花(0

      0条评论

      发表

      请遵守用户 评论公约

      类似文章 更多