首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

嵌入式操作系统WINCEOAL点点滴滴 02

嵌入式操作系统WINCEOAL点点滴滴 02

2. X86平台的ISR结构

  X86平台的ISR保存在%_WINCEROOT%/PUBLIC/COMMON/OAK/CSP/I486/OAL/fwpc.c中,函数名为PeRPISR。下面分析一下此函数的主要代码:

ULONG PeRPISR(void){  ULONG ulRet = SYSINTR_NOP; ///返回值,既中断ID(以SYSINTR_为前缀)  UCHAR ucCurrentInterrupt; ///当前中断号  if (fIntrTime) ////// fIntrTime 用于测试SR和IST的延时时间,测试工具为ILTiming.exe。    ......  ucCurrentInterrupt = PICGetCurrentInterrupt(); ////返回当前中断IRQ  ///IRQ0,IRQ0为系统时钟(system tick)中断,具体见“二、实现系统时钟”  if (ucCurrentInterrupt == INTR_TIMER0)   ......  if (dwRebootAddress) ////是否需要重启动  RebootHandler();   ......  if(ucCurrentInterrupt == INTR_RTC) ////IRQ8,real-time clock的中断  ......  else if (ucCurrentInterrupt <= INTR_MAXIMUM) ///如果中断小于 INTR_MAXIMUM  {    ulRet = NKCallIntChain(ucCurrentInterrupt); ////调用中断链    if (ulRet == SYSINTR_CHAIN) ///如果中断链未包含中断      ////在IRQ 和SYSINTR之间转换,此函数返回IRQ对应的SYSINTR      ulRet = OEMTranslateIrq(ucCurrentInterrupt);     ......    PICEnableInterrupt(ucCurrentInterrupt, FALSE); ///启用除当前中断以外的所有中断  } ///else if  OEMIndicateIntSource(ulRet); ///通知内核已经发生SYSINTR中断}
  从以上代码不难看出ISR的任务就是返回以“SYSINTR_”为前缀的中断ID,如果不需要进一步执行IST,那么就返回SYSINTR_NOP。

3.中断注册步骤

参考X86平台的代码,中断注册步骤如下:


[NextPage]用SETUP_INTERRUPT_MAP宏关联SYSINTR和IRQ。以“SYSINTR_”为前缀的常量由内核使用,用于唯一标识发生中断的硬件。在Nkintr.h文件中预定义了一些SYSINTR,OEM可以在Oalintr.h文件中自定义SYSINTR。

用HookInterrupt函数关联硬件中断号和ISR。这里的硬件中断号为物理中断号,而非逻辑中断号IRQ。在InitPICs函数(和上述ISR位于同一文件)的最后调用了HookInterrupt函数,如下:
        for (i = 64; i < 80; i++)        HookInterrupt(i, (void *)PeRPISR); ///用ISR关联16个中断号         
4. 中断处理步骤


调用InterruptInitialize函数关联SYSINTR和IST,具体是关联IST等待的事件。一般在驱动程序中按如下编写:

      hEvent = CreateEvent(...) ///创建一个事件对象      InterruptInitialize(SYSINTR_SERIAL, hEvent, ...) ///关联一个串口中断ID和这个事件      hThd = CreateThread(..., MyISTRoutine, hEvent, ...) ///创建一个线程(IST)      CeSetThreadPriority(hThd, 152); ///提高此线程的优先级
IST执行I/O操作,一般IST按如下编写:
      for(;;) ///驱动程序一直处于服务状态      {          WaitForSingleObject(hEvent, INFINITE); ////无限等待事件          ...... //// I/O操作          InterruptDone(InterruptId); ///结束当前中断处理      }
ISR和IST之间数据传输
假如我们要从一个设备频繁的读取数据而每次读取量非常少,那么每次读取都要调用IST会降低性能。作为解决方案,ISR可以做读取工作(存放到缓冲区),并在缓冲区存放满后由IST到缓冲区读取。因为ISR运行在内核模式而IST运行在用户模式,IST不能轻易地访问ISR的缓冲区,为此CE提供了一个办法(参见标题为“Passing Data between an ISR and an IST”的帮助文档),您也可以到天极网嵌入式开发论坛询问。
二、实现系统时钟


系统时钟(system tick)概念
  系统时钟是内核需要的唯一中断(IRQ0),系统时钟每毫秒产生一个中断,当发生中断时内核在ISR中累计,到1000的倍数就是过了一秒钟。在处理系统时钟的ISR中不仅要累计计数,还要决定是否通知内核开始重新调度当前所有的线程。要实现一个OAL,系统时钟是第一个必须做的事。

X86平台系统时钟中断的处理工作
  系统时钟由InitClock函数负责初始化工作,一般是在OEMInit函数中调用。当发生中断时,ISR首先用下列语句累计计数:
      CurMSec += SYSTEM_TICK_MS; /////SYSTEM_TICK_MS = 1
然后根据下列语句判断应该返回什么值:
      if ((int) (dwReschedTime – CurMSec) >= 0)           return SYSINTR_RESCHED; ///重新调度      else          return SYSINTR_NOP; ///不再执行任何操作
  上述代码中全局变量dwReschedTime在schedule.c中定义,也就是由内核的调度模块决定在何时开始重新调度线程。CurMSec累计了从WindowsCE启动到当前总共产生了多少个system tick。实现系统时钟后还要实现OEMIdle函数,当没有线程准备运行时OEMIdle被调用,OEMIdle函数将CPU置于空闲模式,但在空闲模式下仍然要累计系统时钟。

三、I/O控制代码

[NextPage]
I/O控制代码作用
  应用软件或者驱动程序可以调用KernelIoControl函数与OAL层通信,而KernelIoControl在内部调用OEMIoControl函数。OEMIoControl是一个OAL API,OEM可以在OEMIoControl中编写自己的I/O控制代码实现一些功能,或者说与应用软件通信。I/O控制代码常用的例子如重启计算机、得到系统信息、设置RTC、得到设备ID等。还有一些系统程序使用的特殊的I/O控制代码。在这里说明一下,我经过实验证实CE提供的得到设备ID方法并非有效。

编写自己的I/O控制代码步骤
在pkfuncs.h或者新编写一个.h文件中按如下格式定义:
#define IOCTL_MY_CONTROL CTL_CODE(FILE_DEVICE_HAL, 3000, METHOD_NEITHER, FILE_ANY_ACCESS)

在oemioctl.c中修改OEMIoControl函数,添加如下代码:
case IOCTL_MY_CONTROL:
......

在应用程序中调用KernelIoControl函数,具体参数参见帮助文档。
返回列表