WinCE 6.0中断驱动程序分析

简介: Windows Embedded CE 6.0的中断处理过程主要分为两部分:  中断服务例程(ISR):处于内核中的低级处理程序,中断发生时首先被调用。中断服务线程(IST):处于驱动或者应用中的中断处理线程,由系统调度,完成大部分的中断处理工作。

Windows Embedded CE 6.0的中断处理过程主要分为两部分:

  •  中断服务例程(ISR):处于内核中的低级处理程序,中断发生时首先被调用。
  • 中断服务线程(IST):处于驱动或者应用中的中断处理线程,由系统调度,完成大部分的中断处理工作。

ISR的实现在OALOEM适配层)中,它只处理最低级的中断响应,通常是获取IRQSYSINTR并设置MCU内部的中断控制寄存器。中断处理的主要部分在驱动或者应用的中断处理线程中。中断处理线程与其他普通线程一样,使用同一个线程优先级管理系统。ISRIST之间通过事件对象进行同步。IST中创建一个事件对象,并使用函数WaitForSingleObject()等待该事件被触发。ISR中通知内核触发相应的事件对象。Windows Embedded CE 6.0的中断处理的过程如下图所示。

                       Windows Embedded CE 6.0 的中断处理过程

  在其他的一些嵌入式操作系统中,在介绍中断处理时经常会提到一个中断向量表的概念,如uC/OS。当中断发生时它会进入IRQ的处理程序,并根据IRQ的值跳转到事先分配好的中断向量表相应的中断处理函数中。但在WinCE中实际上并不存在中断向量表的概念,而只有一个异常向量表,对应于MCU的几种运行模式。WinCE的中断处理对应于两个异常IRQHandlerFIQHandler,通常我们使用的是IRQHandler。当外部中断产生时,系统执行IRQHandler(),IRQHandler()中调用OEMInterruptHandler()获取IRQ对应的SYSINTR,然后根据SYSINTR调用函数OEMNotifyIntrOccurs()触发与SYSINTR关联的事件,最后由IST完成主要的中断处理工作。这种中断处理机制在一定程度上影响了系统的实时性,但提高了IST的灵活性。有关WinCE系统实时性分析,将在另外一篇中描述。

     下面结合DEVICEEMULATORPWRBUTTON驱动进行分析。该驱动也是一个流驱动,所以可以用驱动调试助手进行动态加载和卸载,但需要对代码进行相应的修改,否则会出问题。

    首先看PBT_Init()函数,代码如下:

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Code
DWORD
PBT_Init(DWORD dwContext)
img_405b18b4b6584ae338e0f6ecaf736533.gifimg_1c53668bcee393edac0d7b3b3daff1ae.gif
{
    DWORD   IDPowerButtonThread;
    DWORD   IDResetButtonThread;
    HMODULE hmCore;

    
//
    
// 从CORE Library中获取电源管理器"SetSystemPowerState"的函数指针/
    pfnSetSystemPowerState = NULL;

    hmCore 
= (HMODULE) LoadLibrary(_T("coredll.dll"));

    
if(hmCore != NULL)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif    
{
        pfnSetSystemPowerState 
= (PFN_SetSystemPowerState) GetProcAddress(hmCore, _T("SetSystemPowerState"));

        
if(pfnSetSystemPowerState == NULL)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
            FreeLibrary(hmCore);
        }

    }


    
//初始化相关的虚拟内存地址
    InitializeAddresses();

    
// 创建POWER Button的IST和RESET Button的IST
    ResetButtonIntrThreadHandle = CreateThread(00, (LPTHREAD_START_ROUTINE) ResetButtonIntrThread, 00&IDResetButtonThread);
    
if (ResetButtonIntrThreadHandle == 0)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif    
{
        RETAILMSG(
1, (TEXT("PBT: CreateThread() Fail\r\n")));
    }

    PowerButtonIntrThreadHandle 
= CreateThread(00, (LPTHREAD_START_ROUTINE) PowerButtonIntrThread, 00&IDPowerButtonThread);
    
if (PowerButtonIntrThreadHandle == 0)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif    
{
        RETAILMSG(
1, (TEXT("PBT: CreateThread() Fail\r\n")));
    }


    
return (dwContext);
}

      RESET ButtonISTPOWER ButtonIST基本一致,所以这里只分析POWER ButtonIST,代码如下。      

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Code
static DWORD
PowerButtonIntrThread(PVOID pArg)
img_405b18b4b6584ae338e0f6ecaf736533.gifimg_1c53668bcee393edac0d7b3b3daff1ae.gif
{
    
//初始化中断寄存器,使能相应的中断
    EnablePowerButtonInterrupt();

    
//创建一个事件
    PwrButtonIntrEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    
//
    
// 根据IRQ获取一个SYSINTR
    
//
    if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &PwrButtonIrq, sizeof PwrButtonIrq, &PwrButtonSysIntr, sizeof PwrButtonSysIntr, NULL))
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif    
{
        RETAILMSG(
1, (TEXT("PBT: Error! Failed to request sysintr value for power button interrupt.\r\n")));
        
return(0);
    }

    
    
//关联SYSINTR和之前创建的事件
    if (!(InterruptInitialize(PwrButtonSysIntr, PwrButtonIntrEvent, 00)))
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif    
{
        RETAILMSG(
1, (TEXT("ERROR: PwrButton: Interrupt initialize failed.\r\n")));
    }


    
//POWER Button按下的处理程序
    for (;;)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif    
{
        WaitForSingleObject(PwrButtonIntrEvent, INFINITE);
        
        
if (PowerButtonIsPushed()) //确认按键确实被按下,消除抖动
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif
        {
            Sleep(
200);         //延迟200ms,排除长按的情况
            if (!PowerButtonIsPushed())    //按键被有效释放
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif
            {
                
//
                
//关闭系统 
                
//
                if(pfnSetSystemPowerState != NULL)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif                
{
                    RETAILMSG(
1,(TEXT("PBT: Signalling power manager to suspend\r\n")));
                    pfnSetSystemPowerState(NULL, POWER_STATE_SUSPEND, POWER_FORCE);
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif                }
 else {
                    RETAILMSG(
1,(TEXT("PBT: Suspending by calling PowerOffSystem\r\n")));
                    PowerOffSystem();
                }

                
//
                
//结束当前线程的时间片
                Sleep(0);
            }

            
else
                RETAILMSG(
1,(TEXT("PBT: Button held too long (ignored)\r\n")));
        }

        
else
            RETAILMSG(
1,(TEXT("PBT: Feeble button press or noise triggered it (ignored)\r\n")));

        InterruptDone(PwrButtonSysIntr);
    }

}

    以上代码结构清晰,不再赘述。但这样编译出来的驱动是不能通过驱动调试助手动态加载的,必须进行相应的修改才行。主要原因是没有善始善终,分配的系统逻辑中断没有释放,系统逻辑中断与事件的关联也没有取消。实验现象是,能通过驱动调试助手加载卸载,但中断并不能正常工作了。下面介绍一下解决这个问题的办法。

  首先定义一个全局变量g_bThreadExit初始化为FALSE。IST修改后的代码如下:

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Code
static DWORD
PowerButtonIntrThread(PVOID pArg)
img_405b18b4b6584ae338e0f6ecaf736533.gifimg_1c53668bcee393edac0d7b3b3daff1ae.gif
{
//初始化中断寄存器,使能相应的中断
    EnablePowerButtonInterrupt();

    
//创建一个事件
    PwrButtonIntrEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    
//
    
// 根据IRQ获取一个SYSINTR
    
//
    if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &PwrButtonIrq, sizeof PwrButtonIrq, &PwrButtonSysIntr, sizeof PwrButtonSysIntr, NULL))
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif    
{
        RETAILMSG(
1, (TEXT("PBT: Error! Failed to request sysintr value for power button interrupt.\r\n")));
        
return(0);
    }

    
    
//关联SYSINTR和之前创建的事件
    if (!(InterruptInitialize(PwrButtonSysIntr, PwrButtonIntrEvent, 00)))
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif    
{
        RETAILMSG(
1, (TEXT("ERROR: PwrButton: Interrupt initialize failed.\r\n")));
    }

    
    
// POWER Button按下的处理程序
    for (;;)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif    
{
        WaitForSingleObject(PwrButtonIntrEvent, INFINITE);
                
        
if(g_bThreadExit)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
            
break;    
        }


        
if (PowerButtonIsPushed())     //确认按键确实被按下,消除抖动
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif
        {
            Sleep(
200);         //延迟ms,排除长按的情况
            if (!PowerButtonIsPushed())    //按键被有效释放
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif
            {
                
//
                
//关闭系统
                
//
                if(pfnSetSystemPowerState != NULL)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif                
{
                    RETAILMSG(
1,(TEXT("PBT: Signalling power manager to suspend\r\n")));
                    pfnSetSystemPowerState(NULL, POWER_STATE_SUSPEND, POWER_FORCE);
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif                }
 else {
                    RETAILMSG(
1,(TEXT("PBT: Suspending by calling PowerOffSystem\r\n")));
                    PowerOffSystem();
                }

                
//
                
//结束当前线程的时间片
                
//
                Sleep(0);
            }

            
else
                RETAILMSG(
1,(TEXT("PBT: Button held too long (ignored)\r\n")));
        }

        
else
            RETAILMSG(
1,(TEXT("PBT: Feeble button press or noise triggered it (ignored)\r\n")));

        InterruptDone(PwrButtonSysIntr);
    }

    
    
//取消IRQ与SYSINTR之间的关联
    KernelIoControl(IOCTL_HAL_RELEASE_SYSINTR,&PwrButtonSysIntr, sizeof(UINT32),NULL,0, NULL);
    
    
//取消Event与PwrButtonSysIntr之间的关联
    InterruptDisable(PwrButtonSysIntr);

    CloseHandle(PwrButtonIntrEvent);
    
    RETAILMSG(
1, (TEXT("PowerButtonIntrThread Exit.\r\n")));
    
return 0;
}

  PBT_Deinit()修改后的代码如下:

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Code
BOOL
PBT_Deinit(DWORD dwContext)
img_405b18b4b6584ae338e0f6ecaf736533.gifimg_1c53668bcee393edac0d7b3b3daff1ae.gif
{
    RETAILMSG(
1, (TEXT("PBT: PBT_Deinit()\r\n")));
    
//设置退出线程的标志
g_bThreadExit = TRUE;
    
//模拟一个中断事件
SetInterruptEvent(PwrButtonSysIntr);
    
//延迟500ms,确保IST退出
Sleep(500);

    
return (TRUE);
}

  经过以上修改,该中断驱动程序就可以通过驱动调试助手动态加载和卸载,并能正常工作了。另外,在模拟器中由于没有外部中断按键,可以通过创建一个特定名称的事件与中断关联,并在另外一个应用或者驱动中设置该事件以模拟一个外部中断按键的触发,这种方法也可以在实际平台中根据需要使用。示例代码如下:

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Code
//打开与中断关联的事件
gIntrEvent = CreateEvent(NULL, FALSE, FALSE, _T("PBTINTR"));
//设置该事件,模拟一个中断的触发
SetEvent(gIntrEvent);
IST中创建与中断关联的事件代码修改如下:
PwrButtonIntrEvent 
= CreateEvent(NULL, FALSE, FALSE, _T("PBTINTR"));

  总的来说,WinCE中断处理过程结构清晰,方便开发人员灵活设计IST。在使用驱动调试助手调试有关中断的驱动程序时,需要善始善终,否则会出现中断不能正常工作的情况。

目录
相关文章
|
编译器 C语言
【C语言航路外传】一招解决visual studio部分函数不安全问题
【C语言航路外传】一招解决visual studio部分函数不安全问题
99 0
|
8月前
|
编译器 C语言 C++
C语言,C++编程软件比较(推荐的编程软件)
C语言,C++编程软件比较(推荐的编程软件)
|
缓存 Linux Serverless
讲讲Linux中加法器的那些趣事
导读读了此文能得到什么缘起环形缓冲区研究起加法器在Linux中的应用是因为最近从研究了环形缓冲区,其中对环形缓冲区优化的时候去看了Linux内核源码中的kfifo.c代码。这里简单提一嘴什么是环形缓冲区,英文名RingBuffer或者CyclicBuffer,是一个先进先出的队列,多用作网络编程缓存区等。 环形缓冲区可以看成是一个手尾相接的环,但其实他在内存中并不是一个环,而是一个连续的内存空间,
254 0
讲讲Linux中加法器的那些趣事
|
算法
C程序设计语言第二版习题2-9
在求反码时,表达式 x &= (x - 1) 用于把x最右边的值为1的位删除掉。请解释一下这样做的道理。用这一方法重写bitcount函数 ,使之执行得更快一点
836 0
C程序设计语言第二版习题4-1
编写一个函数strrindex(s, t),用于返回字符串t在s中最右出现的位置,如果 s中不 包含t,那么返回-1。
3695 0
|
机器学习/深度学习 算法 JavaScript
C程序设计语言第二版习题3-5
编写函数itob(n, s, b) ,用于把整数n转换成以b为基数的字符串并存到字符串s中。例如, itob(n, s, 16) 表示将n转换成16进制数,并存入s中。
994 0
|
机器学习/深度学习 算法 JavaScript
C程序设计语言第二版习题3-6
修改itoa函数,使得该函数可以接收三个参数。其中,第三个参数为最小字段宽度。为了保证转换后所得的结果至少具有第三个参数指定的最小宽度,在必要时应在所得结果的左边填充一定的空格。
768 0
|
机器学习/深度学习 JavaScript
C程序设计语言第二版习题3-4
在数的反码表示中,上述的 itoa 函数不能处理最大的负数,即 n 等于-2^(字长-1) 的情况。请解释其原因。修改该函数,使它在任何机器上运行时都能打印出正确的值。
731 0
|
算法 JavaScript
C程序设计语言第二版习题3-3
编写函数expand(s1, s2),将字符串s1中类似于a-z一类的速记符号在字符串s2中扩展为等价的完整列表abc...xyz。该函数可以处理大小写字母和数字,并可以处理a-b-c、a-z0-9与-a-z等类似的情况。作为前导和尾随的-字符原样打印。
4391 0
|
移动开发 算法
C程序设计语言第二版习题2-8
问题描述编写一个函数rightrot(x, n),该函数返回将x循环右移n位所得到的值。
920 0