以前写的原创博文,这里放一份
前一段时间研究了一下WinCE下的中断结构,整理了一下,希望与大家讨论。
最下面有PDF版本下载,便于保存
Windows Embedded CE
关键字:WinCE,中断,体系,结构
摘要:本文主要以WinCE
中断的结构与实现方式
前言
过中断来实现自己的功能或者与系统内核交互,系统时钟本身也是由时钟中断产生的。所以
本文旨在分析WinCE下的中断的结构,以及常用的几种实现方式,来帮助读者了解WinCE
中断体系及实现自己的中断处理结构。
embedded CE 5.0。
一 WinCE中断体系结构
![](https://i-blog.csdnimg.cn/blog_migrate/1999e759947e30960f3a51f5106d3c46.jpeg)
图 1 WinCE中断体系结构
解这 4层之间的交互传递将对我们了解WinCE中断处理很有帮助。
1 硬件层:
enable/disable硬件中断。
2 内核层:
触发相应的Event。关于SYSINTR 和 IRQ 的概念后面会说明。
3 OAL层
4 IST处理层
但是对中断的实时性大打折扣
二
说到这里先解释下IRQ,ISR,IST及 SYSINTR 的概念、意义及相互关系。
IRQ:
IRQ (Interrupt request),中断请求。
这里就是外设或其它请求服务的设备发出来的中断。属于硬件中断,可能是一个电平触发的
GPIO 中断,也可能是内部DMA的一个中断。
ISR:
ISR (Interrupt serviceroutine),
WinCE实际上使用 ISR来处理中断,即默认的中断入口函数,在 ARM体系中,系统默认的
ISR就是 OEMInterruptHandler
IST:
IST (Interrupt servicethread),
在 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中断处理原理
这个函数就是我们实现中断处理的中心函数,首先我们从CPU 的寄存器里获得中断的信息,
这些信息告诉我们到底是哪个 IRQ 源触发了中断。
一般实现中断服务的方式有以下几种:
1. 简单中断处理——ISR模型
中直接对中断源进行判断,然后调用服务程序。
自己打开中断,不过处理起来较麻烦),所以如果处理时间过长,其他中断很可能被忽略,
造成中断丢失。并且系统被锁定,使得系统任务的响应变慢。
2. 中断处理的一般流程——IST模型
所以,对于中断响应的实时性不是特别高,所以系统的运行响应速度就显得更加重要。而且
目前的嵌入式设备的处理速度越来越高,已经几乎达到了当时奔 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 模型的中断实时性较差,但是这种代价对于我们的应用是值得
的。
四
的实现步骤。
1 驱动程序中 IST的构建与中断初始化
上面介绍的 IST流程中,很多步骤都是WinCE的内置支持,也就是说你只要调用相应
的 API就可以实现功能了,不需要自己编写太多的代码。只需要实现一些流程代码。
首先是驱动程序端的中断初始化。假设现在有一个驱动程序,需要服务中断源,IRQ
为 0x12。
a)
KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR,&(dwIrq),
其中 dwIrq为IRQ号,即0x12
dwSysIntr 为系统中断号,也就是调用返回的结果存放的位置
b)
由于我们的IST是需要Event激活的,所以这里申请一个 Event。
申请 Event的步骤比较简单和标准
hISTEvent = CreateEvent(0,FALSE,FALSE,NULL);
c)
调用 InterruptInitialize(dwSysIntr,hISTEvent,0,0)将 SYSINTR 与 Event绑
定,用来在OEMInterruptHandler 中返回SYSINTR 时激活相对应的 Event
d)
到了这一步,中断关于系统方面的初始化基本结束,剩下的就是创建一个 IST,然
后等待 Event来运行中断服务代码,例如:
while(TRUE) {
WaitForSingleObject(hISTEvent,INFINITE) ==
WAIT_OBJECT_0)
…
}
这里需要注意的是IST什么时候创建都可以,但是在InterruptInitialize之前不要
运行 IST 以等待这个 Event,也就是说在 InterruptInitialize 之前不要使用这个
Event,否则会导致InterruptInitialize失败。
还有就是不要使用WaitForMultipleObjects来等待Event。
在中断处理完成之后需要调用 InterruptDone,参数为该中断的SYSINTR。来通
知系统,中断处理完成,系统重新使能该中断
2
OEM 层 主 要 是 控 制 IRQ 的enable
(BSPIntrDisableIrq), 当然要初始化 IRQ 的配置,使其在正确的触发状态,比如上升延
触发
IRQ 的 SYSINTR,然后返回合法的SYSINTR给系统,系统查表激活相应的Event,对应
的 IST进行中断服务,然后再次等待 Event。
SYSINTR,具体格式为:
NULL, 0, NULL);
五 可安装的 ISR
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。
2 IISR的实现
下面我们来看看IISR 的具体实现步骤:
链表中的中断处理程序。所以我们可以有两个选择,一个就是类似 ISR 模型,直接在链表
中的中断处理程序中判断是不是自己的中断,并且做处理。还有一种方式就是类似 IST 模
型,如果判断是自己的中断,则返回一个SYSINTR,以此SYSINTR 来激活IST。
程序添加到链表,又如何在中断来的时候去搜索链表。
理 IISR 的模块,源程序可以在WINCE500\PUBLIC\COMMON\OAK\DRIVERS\GIISR
找到。熟悉了 GIISR,想实现自己的 IISR 处理程序或者基于 ISR 模型的处理,都比较简
单了。
a)
步骤与IST模型中介绍的一样,这里就不重复叙述了,IISR 在这里与 IST模型并
没有任何的不同。其与 IST 模型的唯一不同点就是如何根据 IRQ 来判断相应的
SYSINTR。
在 IST 模型中是 OEM写死的一个判断程序,而 IISR 可以动态来注册一个判断程
序给系统调用,这是唯一的实现区别。
b)
某一个特定的IRQ相关联
这个过程是通过调用LoadIntChainHandler函数来实现的。
这里我们的中断服务dll叫做”giisr.dll”,处理函数名叫做”ISRHandler”,对应IRQ
为0x20,则函数调用形式如下:
HANDLE
TEXT(“ISRHandler”), 0x20);
c)
己的管理。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 {
(if associated device is asserting IRQ)
asserting IRQ
x86)
obtain mask
determine if device is asserting IRQ
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 地址处获得。
d)
到这里全部的初始化就完成了,可以看出,和IST模型相比就是多了两步 b)和 c),
这两步决定了中断判断的方式,这也是 IISR的根本所在。
3
下面详细介绍下可安装中断在 ISR 中被判断的过程。
同 样 , 中 断 到 来 时 进 入 的 函 数 是 OEMInterruptHandler , 在
OEMInterruptHandler中会调用NKCallIntChain来遍历该IRQ对应注册的IISR。
这是一个链表结构,所以对中断判断程序是一个顺序调用的过程,即先注册的设备先
判断,如果判断到正确的结果,则返回合法的 SYSINTR,OEMInterruptHandler
也同样返回这个值。所以即使后面的设备也符合条件,也不会被执行。如果整个链表
中都没找到正确的设备,则返回 SYSINTR_CHAIN。OEMInterruptHandler 在判
断到返回结果为SYSINTR_CHAIN 时,即表示请求中断的设备不在链表中。
4
当以某一个寄存器或者地址的值,不足以判断到底是系统中哪个设备触发的中断
的时候,GIISR 就不是这么好用了。比如多个不同的外设,使用同一个GPIO 来触发
中断。外设需要读取多个寄存器或者需要一个复杂些的计算(不只是简单的一个&操作)
才能判定中断是否是其产生的。这时候我们需要使用自己的一套 IISR的处理方式。
当然我们不希望去改动整个微软对于 IISR 的处理结构,所以我们就要区分开来上
面介绍的 GIISR 的模型里,哪些是微软的架构,哪些是GIISR 自己的实现。
a)
dll,在这个 dll中除了需要一个判断处理程序(也就是通过
LoadIntChainHandler传递进去的那个参数),还需要一个CreateInstance
的函数,这个是必须的。在你不改动微软内核的情况下,名字也是固定的,详
细地函数定义,可以参考 GIISR 的 CreateInstance。至于 IOControl,最
好也参考 GIISR 的定义一个,如果不需要去调用KernelLibIoControl 的话,
应该是可以不实现的。
b)
函数时将CreateInstance 的返回值作为参数传递进去,如果处理函数在判断
到不是自己触发的中断,应该返回 SYSINTR_CHAIN,否则返回一个有意义
的 SYSINTR 值
c)
现 DestroyInstance 这个函数,被系统调用,这个是可选的。
GIISR 自己的实现:
使用同一套代码管理这些中断处理程序,每个中断处理程序在内部的数组中占用
一项,这些项目记录着中断处理程序激活使用的SYSINTR 以及判定其触发中断的标
准。这个标准就是读取某个寄存器或地址来用掩码来判断。同时这个数组项目对应的
结构体数据就是通过KernelLibIoControl 传递进去的。
程序。
时传递进自定义的一个 dll 与处理函数,CreateInstance 一定要实现,不过返回值
可以忽略。
SYSINTR。
处理函数来判断。CreateInstance 的返回值就是区别不同设备的这个 Index。
5 IISR资源的释放
当我们需要注销这个 IISR 的时候,需要调用 FreeIntChainHandler,来将该中
断服务从链表中删除。
FreeIntChainHandler(hIsrHandler);
hIsrHandler 就是LoadIntChainHandler 的返回值。
6 使用 IISR的注意事项
由于 IISR 是动态的被加载的,也就是说注册的 dll 会被加载到内核空间。所以不
能调用任何非内核的函数。
并且,如果将 IISR的 dll放在 bib文件的MODULES section里面,需要设置”K”
属性,如
giisr.dll
如果放在 FILES section 里面,需要保证没有 fixup类型的变量。
六
图 1 WinCE 中断体系结构
重新看图 1。这里画出了从硬件中断发生,到 IST 运行的全部过程,中间就是
我们要研究的延时部分。
图中的”IST 延迟”标志,实际上就是我们所说的中断延时。但是将 IST 延时细
分,可以分为如下几部分延时:
a)
这部分延时是指从硬件触发中断到进入OEM的 ISR程序的时间,在 ARM 体系
下,就是进入OEMInterruptHandler 的时间。
b)
在 OEMInterruptHandler 中判断 IRQ,并且返回相应的 SYSINTR 的时间,
也是OEM最主要把握的时间。
c)
主要就是系统根据ISR 返回的SYSINTR激活相应的Event,系统调度运行,
到 IST执行的这段时间
2 如何降低中断延时
上面的中断延时原因中,a)是我们一般不去过问的部分,这段代码是微软实现
的,原则上是可以改的,但是收效不大,而且可能引入 bug,建议不要去动。c)是
我们可以部分控制的,关键就是IST的优先级,其他部分我们也使用微软默认的实
现。
b) 这 一 部 分 就 是 我 们 着 重 需 要 管 理 的 了 , 因 为 这 一 块 就 是
OEMInterruptHandler 函数的实现,是 OEM 自己实现的代码。也就是说,判断
中断源,返回SYSINTR的过程是我们唯一较可行的控制中断延时的地方。
关于这部分的优化,首先就是尽量用较简单的逻辑判断 IRQ,来返回相应的
SYSINTR,而且 OEMInterruptHandler 会调用NKCallIntChain,所以 IISR 的
链表长度与 IISR处理函数的效率也是影响中断延时的重要因素。
一般在判断 IRQ 的过程中,我们会把最可能出现、最频繁出现的IRQ 放在最前
面判断,比如系统时钟。这样在刚进入OEMInterruptHandler就可以判断到IRQ,
并返回,节省了时间,提高了效率。同样这种方法也适用于 IISR,将最可能出现,
最频繁出现的设备放在链表的前面,可以提高遍历的效率。
七
WinCE 下提供了较灵活的中断处理方式,包括ISR,IST,IISR 三种主要方式。
对于 ARM 架构下的系统开发,我们常用的就是 IST 与 IISR 两种方式。两种方式
从本质上都是通过返回正确的SYSINTR来激活相应的 IST来服务中断。不同点只是对
IRQ 判断的方式,IST 是内置的 OEM 写死的判断程序,而 IISR 是通过一个预留的接
口,来动态注册判断程序,从而给了系统一个注册新中断的机会。同时 IISR 可以实现
中断共享,在一个 IRQ 上通过链表的方式不断添加判断程序,从而让多个设备共享同
一个 IRQ,同时又可以有自己独立的中断判断程序。
在最后,我们分析了中断延时的一些因素,这里并没有详细的进行分析,只是提出
了一些降低中断延时的方法。
参考文献:
1.
2.
3.