四极管 整理wince挂起和唤醒(suspend/wakeup)以及实现关机功能文章

学习wince挂起和唤醒以及关机功能,后面再把自己调试心得记下来。

以下文章来源:

http://hi.baidu.com/mikenoodle/blog/item/3d659a16bb9ef656f3de328e.html

http://www.cnblogs.com/wangweixf/archive/2008/12/08/1350226.html

wince挂起和唤醒

不管任何方式的系统挂起,最终都会调用OEMPowerOff()函数来实现.OEMPowerOff()函数由OEM来完成,这个函数也许会位于 power.c或者off.c的文件中.OEMPowerOff()是OEM来实现的,代码和流程也许不同,但基本按照下面的方法来完成.

挂起的过程:

1.先进行平台相关的动作,比如清屏,设置AD,usb等.

2.保存芯片所有的寄存器值到一个静态数组(就是堆栈中)

3.设置io,关闭kitl等

4.呼叫OALCPUPowerOff()进行挂起.

OALCPUPowerOff()是一个位于startup.s中的汇编函数,它按照下面的流程实现挂起功能

5.保存通用寄存器r4-r12,lr到堆栈

6. 保存wakeup后的地址,MMU寄存器,进入各模式将sp和lr寄存器保存到内存RAM的某一个位置,这个位置是由config.bib指定保留的.为什么不象之前一样保存到堆栈呢?因为系统唤醒后跳转到reset开始执行,这时候堆栈还没有初始化.这也是poweroff过程复杂的原因.

7.计算刚才保存的数据块的检验和并保存到GSTATUS3寄存器.(GSTATUS3和GSTATUS4是状态寄存器,挂起直到唤醒过程都会保存里面的值)

8.禁止中断.

9.清cache

10.使能唤醒中断,能唤醒可以是外部中断0,1,2,或者RTC中断

11.设置sdram进入自刷新模式,最终cpu进入power off状态

唤醒的过程:

cpu的power off模式和其他睡眠模式不同,其他的睡眠模式唤醒后会从睡眠处继续运行,而power off模式唤醒后是从reset处执行.reset有3种可能情况,1.正常的上电冷启动,包括reset信号线有效造成的reset.2.看门狗失效造成的reset.3.power off之后被外部中断或者rtc中断唤醒的reset.在reset之后可以根据GSTATUS2寄存器来判断是否从power off唤醒.还有一个问题,不论何种方式reset,都是先执行bootloader的代码,所以唤醒过程需要bootloader的参与配合.具体流程:

1.外部中断或者rtc中断唤醒cpu进入bootloader

2.bootloader中停止sdram的自刷新模式,然后跳到内核开始地址.有些bootloader会做的更多,因为前面我们把数据都保存到了ram中的某处,事实上只要知道这个ram地址就可以取得数据进入唤醒过程.所以有些bootloader会直接去唤醒.我认为这并不好,增加了bootloader的依赖性,层次间的耦合性也高了.

3.检查checksum,因为之前设置sdram处于自刷新状态,在poweroff期间sdram里面的数据会保持,增加checksum是有必要的安全措施.

4.从RAM取得之前保存的参数,其中包含了唤醒后应该跳转的地址,和MMU的配置数据以及各个模式的sp和lr.

5.启动mmu

6.跳到唤醒后的新地址.

7.进入各个模式恢复sp和lr.

8.恢复r4-r12,lr

9.跳转到lr,即相当于OALCPUPowerOff()返回,返回到OEMPowerOff()中.

10.打开kitl,恢复所有寄存器,恢复平台之前状态.

唤醒过程实际是一个挂起的逆过程.如此,系统成功唤醒,所有运行的应用程序不知道自己被系统挂起过而继续运行.



实现关机功能

wince5.0带的电源管理驱动只实现了“休眠(SUSPEND)”功能,未实现“关机(SHUT_DOWN)”功能。调用函数 SetSystemPowerState()时,无论参数是POWRE_STATE_OFF还是POWRE_STATE_SUSPEND,最终均为 SUSPEND。如果需要关机,还需要其他的办法。

我看过有人专门写了一个PowerKey的驱动,用来实现关机。但是这种方法的问题在于,关机时系统不会通知应用程序,往往需要自己设计一套消息由PowerKey驱动来通知应用程序,在很多时候非常的不方便。

我使用的方法是利用Wince自身的电源管理驱动,与系统结合的比较紧密

1. 建立新的PM PDD(platform.cpp),系统自带的PDD在WINCE500/PUBLIC/COMMON/OAK/DRIVERS/PM/PDD目录,可以选取一个作为修改的模板.同时,修改电源管理的SOURCE文件,使用新的PDD。

2.PlatformMapPowerStateHint()函数负责把电源状态标记映射为电源状态的名称,在此函数中做如下修改:

            把POWER_STATE_OFF映射为shutdown而不是suspend

3. PlatformSetSystemPowerState()函数负责设置电源的状态,函数中有这么一句

                     if((dwNewStateFlags & POWER_STATE_RESET) != 0)

     这一句监测是否用户准备RESET系统,并在后面做相应的RESET动作。

     在此之前,增加if((dwNewStateFlags & POWER_STATE_OFF) != 0){关机代码}

4. 在注册表文件中增加

; wwwwww, the following key is added to impliment the shut down function(not suspend)

; the application should deal with the POWER_BROADCAST(to POWER_STATE_OFF)

[HKEY_LOCAL_MACHINE"SYSTEM"CurrentControlSet"Control"Power"State"ShutDown]

    "Default"=dword:4           ; D4

    "Flags"=dword:20000         ; POWER_STATE_OFF

 5. 修改到此完成,关机时调用 SetSystemPowerState即可,StateFlags参数设为POWER_STATE_OFF。关机时,系统会向应用程序发送POWER_BROADCAST(to POWER_STATE_OFF)消息

通过powerButton驱动分析WINCE的中断实现
前两天有一个朋友问了我s3c2410WINCE挂起/唤醒的实现,这两天我研究了一下s3c2410的PowerButton驱动,对WINCE的中断流程又有了进一步的了解,下面我就写一下我的心得和大家交流一下。
首先和PowerButton驱动有关的文件为:
1)$(_WINCEROOT)\PLATFORM\smdk2410\DRIVERS\PWRBTN文件夹下面的三个文件pwrbtn2410.c、pwrbtn2410.h和pwrbtn2410.def,这个PowerButton驱动是通过流驱动实现的,实现的过程很简单,下面会详细说明。
2)$(_WINCEROOT)\PLATFORM\smdk2410\INC\oalintr.h,定义非内核模式的中断号( non-kernelinterrupt identifiers )
3)$(_WINCEROOT)\PLATFORM\smdk2410\KERNEL\HAL\cfw.c,这里面主要实现了OEMInitInterrupts、OEMInterruptEnable、OEMInterruptDisable、OEMInterruptDone这几个重要的中断函数。
4)$(_WINCEROOT)\PLATFORM\smdk2410\KERNEL\HAL\ARM\armint.c,这里主要实现了OEMInterruptHandler这个中断处理函数。
下面我简单分析一下中断处理过程。
a)  首先你为自己的硬件(键盘,按键等需要使用的中断)定义一个中断名称,比如这个电源按键就起了一个中断名称SYSINTR_POWER,然后在oalintr.h里面把它定义成SYSINTR_FIRMWARE+n的形式
比如:    #defineSYSINTR_POWER        (SYSINTR_FIRMWARE+13)
n必须小于SYSINTR_MAXUMUM or SYSINTR_FIRMWARE+23
b)在cfw.c的OEMInitInterrupts中进行一些中断初始化工作,主要就是屏蔽所有中断,清除中断挂起等工作,代码我就不详细说明了,比较简单。在OEMInterruptEnable(这个函数会被InterruptInitialize函数调用)函数中主要进行中断开启工作,当驱动使用InterruptInitialize的时候(比如InterruptInitialize(SYSINTR_POWER,gPwrButtonIntrEvent, 0,0))就会把SYSINTR_POWER中断号传入,然后开启EIN0这个中断,并且把SYSINTR_POWER中断和事件gPwrButtonIntrEvent连接起来,代码如下:
INTERRUPTS_OFF(); //关闭所有中断
 switch (idInt)
 {......
case SYSINTR_POWER:
  s2410INT->rSRCPND = BIT_EINT0;
  // S3C2410X Developer Notice(page 4) warns against writing a 1 to a 0 bit in the INTPNDregister.
  if (s2410INT->rINTPND &BIT_EINT0) s2410INT->rINTPND = BIT_EINT0;
  s2410INT->rINTMSK &=~BIT_EINT0;
  s2410INT->rSRCPND = BIT_EINT2;
  // S3C2410X Developer Notice(page 4) warns against writing a 1 to a 0 bit in the INTPNDregister.
  if (s2410INT->rINTPND &BIT_EINT2) s2410INT->rINTPND = BIT_EINT2;
  s2410INT->rINTMSK &=~BIT_EINT2;
  break;
.....
}
INTERRUPTS_ON();//开启所有中断
 
这个文件里面还实现了OEMInterruptDisable函数用来禁止中断,与PowerButton相关的函数如下:
INTERRUPTS_OFF();
switch (idInt)
 {....
 case SYSINTR_POWER:
  s2410INT->rINTMSK |=BIT_EINT0;
  s2410INT->rINTMSK |=BIT_EINT2;
  break;        
  .....
 }
 INTERRUPTS_ON();
在这个文件中,还实现了OEMInterruptDone函数,做一些中断处理结束后的事情,当驱动调用InterruptDone时会把中断号传到这个函数来使用这个函数,与PowerButton相关的函数如下:
 INTERRUPTS_OFF(); 
 switch (idInt)
 {...
 case SYSINTR_POWER:
  s2410INT->rSRCPND =BIT_EINT0;
  if (s2410INT->rINTPND &BIT_EINT0) s2410INT->rINTPND = BIT_EINT0;
  s2410INT->rINTMSK &=~BIT_EINT0;
  s2410INT->rSRCPND =BIT_EINT2;
  if (s2410INT->rINTPND &BIT_EINT2) s2410INT->rINTPND = BIT_EINT2;
  s2410INT->rINTMSK &=~BIT_EINT2;
  break;
 }
 INTERRUPTS_ON();
以上几个中断函数都相当重要,而且功能我也讲得很清楚了,大家应该理解了吧^_^。
c)在armint.c中主要实现了OEMInterruptHandler这个中断处理函数,当有硬件中断来的时候会进入这个处理函数,我们看看与PowerButton有关的代码:
 else if (IntPendVal ==INTSRC_EINT0)  { // POWER BUTTON中断
  s2410INT->rINTMSK |=BIT_EINT0;
  s2410INT->rSRCPND = BIT_EINT0; /* InterruptClear    */
  if (s2410INT->rINTPND &BIT_EINT0) s2410INT->rINTPND  = BIT_EINT0;
  return(SYSINTR_POWER); //返回一个中断号通知系统发生了什么中断,系统通过这个中断产生一个事件//给IST使用。
在这里,我们把PowerButton和EINT0联系起来了,并且如果EINT0来了中断,就会返回系统一个中断号SYSINTR_POWER。
d)我们下面再来看看PowerButton驱动的实现。在pbut2410.c文件里,我们首先看看动态链接库的init实现:
PUBLIC DWORD
DSK_Init(DWORD dwContext)
{
 do
 {
  gPwrButtonIntrThread =CreateThread(0, 0, (LPTHREAD_START_ROUTINE) PBT_IntrThread, 0, 0,&IDThread);//创建了一个PBT_IntrThread线程,这个就是PowerButton的IST
  if (gPwrButtonIntrThread ==0)
  {
   break;
  }
 } while (0);
}
下面我们看看PBT_IntrThread的实现:
DWORD
PBT_IntrThread(PVOID pArg)
{
 PBT_InitializeAddresses();//得到EINI0口的虚拟地址
 PBT_EnableInterrupt(); //使能EINI0口中断
 gPwrButtonIntrEvent = CreateEvent(NULL, FALSE,FALSE, NULL);//创建一个事件//PowerButton事件
 if (!(InterruptInitialize(SYSINTR_POWER,gPwrButtonIntrEvent, 0, 0)))
 //通知系统使能SYSINTR_POWER这个中断,并且当这个中断产生时产生一个gPwrButtonIntrEvent事件,//第一个参数为与这个IST连接的中断ID,第二个参数为中断产生是产生的事件
 {
  RETAILMSG(1, (TEXT(":::SYSINTR_POWER Init Fail\r\n")));
 }
 while (1)
 {
  WaitForSingleObject(gPwrButtonIntrEvent,INFINITE);//等待中断发生
    if (gOffFlag == FALSE)
  {
   if(PBT_IsPushed())   /*To FilterNoise    *///判断是否是噪声
   {
    Sleep(200);
    if(PBT_IsPushed())
    { 
    }
    else
    {//如果不是噪声则:
                                   #if (WINCEOSVER >= 400)
                                       if(gpfnSetSystemPowerState != NULL)
                                       {
                                           gpfnSetSystemPowerState(NULL, POWER_STATE_SUSPEND,POWER_FORCE);
                                       }
                                       else
                                       {
                                           PowerOffSystem();//调用PowerOffSystem函数,在这//个函数里面又会调用OEMPowerOff函数,这个函数在power.c里
                                       }
                                   #else
                                       PowerOffSystem();
                                   #endif
                                   DriverSleep(0, FALSE);
    }
   }
   InterruptDone(SYSINTR_POWER);//通知系统调用OEMInterruptDone
    }
   }        
}
在看看一些函数
PRIVATE VOID
PBT_EnableInterrupt(VOID)
{
    v_pIOPregs->rGPFCON  &= ~(0x3 <<0);     /* 设置GPF0) 为EINT0                    */
    v_pIOPregs->rGPFCON  |=  (0x2<< 0);
   v_pIOPregs->rEXTINT0 &= ~(0x7 <<0);      /*配置EINT0为下降沿模式                */
   v_pIOPregs->rEXTINT0 |=  (0x2 << 0);
}
 
PRIVATE BOOL
PBT_IsPushed(VOID)
{//判断GPF0是否被按下
    return ((v_pIOPregs->rGPFDAT & (1 << 0)) ? FALSE :TRUE);
}
 
PRIVATE BOOL
PBT_InitializeAddresses(VOID)
{//分配EINT0的虚拟地址供驱动使用
    BOOL RetValue =TRUE;      
    /* IO Register Allocation */
    v_pIOPregs = (volatile IOPreg *)VirtualAlloc(0, sizeof(IOPreg),MEM_RESERVE, PAGE_NOACCESS);
    if (v_pIOPregs == NULL)
    {
        ERRORMSG(1,(TEXT("For IOPregs : VirtualAlloc failed!\r\n")));
        RetValue = FALSE;
    }
    else
    {
        if (!VirtualCopy((PVOID)v_pIOPregs, (PVOID)(IOP_BASE),sizeof(IOPreg), PAGE_READWRITE | PAGE_NOCACHE))
        {
             ERRORMSG(1,(TEXT("For IOPregs: VirtualCopy failed!\r\n")));
             RetValue = FALSE;
        }
    }
    
    if (!RetValue)
    {
//      RETAILMSG (1, (TEXT("::: PBT_InitializeAddresses - Fail!!\r\n")));
 
        if (v_pIOPregs)
        {
             VirtualFree((PVOID) v_pIOPregs, 0, MEM_RELEASE);
        }
 
        v_pIOPregs = NULL;
    }
    else RETAILMSG (1, (TEXT("::: PBT_InitializeAddresses -Success\r\n") ));
 
    return(RetValue);
}
 
总结:
从上面PowerButton这个驱动我们就能把WINCE的中断处理过程了解清楚,基本的过程是首先在oalinitr.h中把中断IDdefine成SYSINTR_FIRMWARE+N的形式,然后
OEMInitInterrupts(cfw.c) -> OEMInterruptEnable(cfw.c) ->硬件中断到达 -> OEMInterruptHandler(armint.c) ->自己写的中断服务线程(IST)-> OEMInterruptDone(cfw.c)
其中IST的流程一般为:
创建一个事件CreateEvent -> InterruptInitialize(SYSINTR_XXXX,XXXXXEvent, 0, 0)把中断ID和IST联系起来,并且把中断ID和事件XXXXXEvent联系起来 ->WaitForSingleObject(XXXXXEvent,INFINITE)等待这个事件的产生,由于事件和中断联系起来了,实际就是等待中断的产生-> 实际的中断处理过程 -> InterruptDone(SYSINTR_ XXXX)通过这个函数调用OEMInterruptDone

本文出自 “Mobile and Linux Deve..” 博客,请务必保留此出处http://buaadallas.blog.51cto.com/399160/80929

WINCE的挂起/唤醒实现


根据上面那篇PowerButton驱动分析的文章,我们应该清楚了,按下PowerButton就可以最后调用OEMPowerOff这个函数,其实也可以通过调用APIPowerOffSystem来进入OEMPowerOff。我们看看这个函数实现:
这个函数在$(_WINCEROOT)\PLATFORM\smdk2410\KERNEL\HAL下的power.c文件中
VOID OEMPowerOff(void)
{
 volatile IOPreg *s2410IOP = (IOPreg*)IOP_BASE;
  volatile INTreg *s2410INT =(INTreg *)INT_BASE;
 volatile LCDreg *s2410LCD = (LCDreg*)LCD_BASE;   
    /* SaveCurrent Important CPU Regs...  */
   CPUSaveRegs(CPUBackupRegs);//保存寄存器的值
    /* LCDControllerDisable              */
   CPULCDOff();//关闭LCD电源
 /* Stop all GPIO */
 ConfigStopGPIO();//停止IO口
 /* Set misc register for power off */
 ConfigMiscReg();

    /* ActualPower-Off ModeEntry         */
   CPUPowerOff();//调用CPUPowerOff,这个函数在fw.s里面,调用完之后会进入挂起状态
    /*Recover Process, Load CPURegs      *///恢复过程
   CPULoadRegs(CPUBackupRegs);//恢复寄存器值
 /* Clear GSTATUS2 register : Write 1 to clear*/
 s2410IOP->rGSTATUS2 =s2410IOP->rGSTATUS2;//清除GSTATUS2寄存器,这里面存放的是CPU挂起时PC指针的地址
    /*InterruptClear                     *///清除中断
...
}
我们下面进入fw.s简单分析一下CPUPowerOff
   LEAF_ENTRY CPUPowerOff//CPUPowerOff 函数实现
    ; 1.保存寄存器状态以及返回地址到栈里
    ;
   stmdb   sp!,{r4-r12}                  
   stmdb   sp!, {lr}

    ; 2. 保存MMU& CPU 寄存器到RAM.
    ;
   ldr    r3,=SLEEPDATA_BASE_VIRTUAL        ; base of Sleep mode storage
   ldr    r2, =Awake_address
   str    r2, [r3],#4                       ; save resume function address (virtual).
   
   mrc    p15, 0, r2, c1, c0, 0
   ldr    r0, =MMU_CTL_MASK
   bic    r2, r2, r0
   str    r2, [r3],#4                       ; save MMU control data.
   mrc    p15, 0, r2, c2, c0, 0
   ldr    r0, =MMU_TTB_MASK
   bic    r2, r2, r0
   str    r2, [r3],#4                       ; save TTB address.
   mrc    p15, 0, r2, c3, c0, 0
   str    r2, [r3],#4                       ; save domain access control.
   str    sp, [r3],#4                       ; save SVC mode stack pointer.
   mrs    r2, spsr
   str    r2, [r3],#4                       ; save SVC status register.
   mov    r1,#Mode_FIQ:OR:I_Bit:OR:F_Bit    ; enter FIQ mode, no interrupts.
   msr    cpsr, r1
   mrs    r2, spsr
   stmia   r3!, {r2, r8-r12, sp,lr}          ; save the FIQ mode registers.
   mov    r1,#Mode_ABT:OR:I_Bit:OR:F_Bit    ; enter ABT mode, no interrupts.
   msr    cpsr, r1
   mrs    r0, spsr
   stmia   r3!, {r0, sp,lr}                  ; save the ABT mode Registers.
   mov    r1,#Mode_IRQ:OR:I_Bit:OR:F_Bit    ; enter IRQ mode, no interrupts.
   msr    cpsr, r1
   mrs    r0, spsr
   stmia   r3!, {r0, sp,lr}                  ; save the IRQ Mode Registers.
   mov    r1,#Mode_UND:OR:I_Bit:OR:F_Bit    ; enter UND mode, no interrupts.
   msr    cpsr, r1
   mrs    r0, spsr
   stmia   r3!, {r0, sp,lr}                  ; save the UND mode Registers.
   mov    r1,#Mode_SYS:OR:I_Bit:OR:F_Bit    ; enter SYS mode, no interrupts.
   msr    cpsr, r1
   stmia   r3!, {sp,lr}                      ; save the SYS mode Registers.
   mov    r1,#Mode_SVC:OR:I_Bit:OR:F_Bit    ; back to SVC mode, no interrupts.
   msr    cpsr, r1

   ; 3. Compute the checksum onSleepData (verify integrity of data after resume).
   ;
   ldr    r3,=SLEEPDATA_BASE_VIRTUAL        ; get pointer to SLEEPDATA.
   mov    r2, #0
   ldr    r0,=SLEEPDATA_SIZE                ; get size of data structure (in words).
30
   ldr    r1, [r3],#4                       ; compute the checksum.
   and    r1, r1, #0x1
   mov    r1, r1, LSL #31
   orr    r1, r1, r1, LSR #1
   add    r2, r2, r1
   subs    r0, r0,#1
   bne    %b30

   ldr    r0, =vGPIOBASE
   str    r2, [r0,#oGSTATUS3]               ; save the checksum in the Power Manager Scratch pad register.
    ; 4.屏蔽和清除所有中断.
    ;
   ldr    r0, =vINTBASE
   mvn    r2, #0
   str    r2, [r0, #oINTMSK]
   str    r2, [r0, #oSRCPND]
   str    r2, [r0, #oINTPND]
......
    ; 6.设置外部唤醒中断(EINT0-2: power-button 和keyboard).
    ;
   ldr    r0, =vGPIOBASE
   ldr    r1, =0x550a
   str    r1, [r0, #oGPFCON]
   ldr    r1, =0x55550100
   str    r1, [r0, #oGPGCON]
    ; 7.转换到power-off 模式.
    ;
 ldr  r0,=vMISCCR   ; hitthe TLB
 ldr  r0,[r0]
 ldr  r0, =vCLKCON
 ldr  r0,[r0]
    ; **Theseregisters are used later during power-off.
    ;
   ldr    r0, =vREFRESH  
   ldr    r1,[r0]                           ; r1 = rREFRESH.
   orr    r1, r1, #(1 << 22)
    ; **Theseregisters are used later during power-off.
    ;
   ldr    r2, =vMISCCR
   ldr    r3, [r2]
   orr    r3, r3, #(7 <<17)                 ; make sure that SCLK0:SCLK->0, SCLK1:SCLK->0, SCKE=L duringboot-up.
    ; **Theseregisters are used later during power-off.
    ;
   ldr    r4, =vCLKCON
   ldr    r5,=0x7fff8                       ; power-off mode.
 ldr  r8,=0xEA000000
 add  r8, r8,#0x3f0
 add  r8, r8,#0xe  ; make value to0xEA0003FE
 ldr  r6,=0x92000000  ; make address to0x9200 1004 or 0x9200 0004
 ldr    r7, [r6]   ;Check ROM Address data, if 0xEA0003FE, it is EBOOT
 cmp  r7,r8
 bne  %f50
 add  r6, r6,#0x1000  ; Because eboot startupcode is located at 0x1000.
50
 add  r6, r6,#0x4  ;
 mov    pc,r6    ;jump to Power off code in ROM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 b      SelfRefreshAndPowerOff
       
 ALIGN  32                     ; for I-Cache Line(32Byte, 8 Word)
SelfRefreshAndPowerOff  ; runwith Instruction Cache's code
 str    r1, [r0]  ; 使能SDRAMself-refresh模式,这样RAM中的数据不会丢失!!
 str  r3,[r2]  ; MISCCR Setting
 str    r5, [r4]  ; Power Off !!
 b      .
上面的代码就实现了wince的挂起,下面我们分析唤醒的流程,通过外部中断,其实是让CPU复位,这样系统又从0地址开始执行,但是,通过下面的代码我们可以知道是冷启动还是唤醒!!!
 ldr  r1,=GSTATUS2          ; Determine Booting Mode
 ldr  r10,[r1]
 tst  r10,#0x2
 beq  %F2          ;if not wakeup from PowerOffmode如果第2位不为1则表示是唤醒
                       ;    Skip MISCCRsetting
 b  %F3      ;if wakeup from PowerOff mode
         ;   goto Power-up code.
下面还有一段
 tst  r10,#0x2
 beq  BringUpWinCE                   ; 如果GSTATUS2为1则正常启动,否则进入唤醒
; Recover Process : Starting Point
;  1. Checksum Calculation saved Data
 ....
40
 ldr  r1, [r3],#4   ; pointerto SLEEPDATA
 and  r1, r1,#0x1
 mov  r1, r1,LSL #31
 orr  r1, r1,r1, LSR #1
 add  r2, r2,r1
 subs r0, r0,#1    ;dec the count
 bne  %b40          ; loop till done 
 ldr  r0,=GSTATUS3
 ldr  r3,[r0]    ;get the Sleep data checksum from the Power Manager Scratch padregister
 teq  r2,r3          ; compare to what we saved before going to sleep
; bne  BringUpWinCE     ; bad news - do a cold boot - If emergency power off case, normalbooting.
 bne  JumpToRAM    ;bad news - do a cold boot - If emergency power off case, normalbooting.
 b  MMUENABLE
JumpToRAM
 ldr  r2,=0x201000     ;offset into the RAM
 ldr  r3,=0x30000000     ;add physical base
 add  r2, r2,r3
 mov    pc,r2       ; & jump to StartUp address
MMUENABLE
;  2. MMU Enable
 ldr    r10, [r5, #SleepState_MMUDOMAIN] ; load the MMUdomain access info
 ldr    r9,  [r5,#SleepState_MMUTTB]  ; load theMMU TTB info 
 ldr    r8,  [r5,#SleepState_MMUCTL]  ; load theMMU control info 
 ldr    r7,  [r5, #SleepState_WakeAddr] ; load the LR address
 nop   
 nop
 nop
 nop
 nop
....
; 唤醒过程
1
 mcr  p15, 0,r10, c3, c0, 0  ; setup access todomain 0
 mcr  p15, 0,r9,  c2, c0,0  ; PT address
 mcr  p15, 0,r0,  c8, c7,0    ; flush I+D TLBs
 mcr  p15, 0,r8,  c1, c0,0  ; restore MMU control
; 3. 跳到内核fw.s(Awake_address)唤醒地址处!!!!!1
 mov    pc,r7      ; & jump to new virtual address (back up Power managementstack)跳到挂起时的地址
 nop
唤醒时的执行地址如下:
Awake_address
;      1. Recover CPU Registers
 ldr    r3, =SLEEPDATA_BASE_VIRTUAL  ;Sleep mode information data structure
 add    r2, r3, #SleepState_FIQ_SPSR
 mov    r1, #Mode_FIQ:OR:I_Bit:OR:F_Bit ; Enter FIQ mode, nointerrupts
 msr    cpsr, r1
 ldr    r0,  [r2], #4
 msr    spsr, r0
 ldr    r8,  [r2], #4
 ldr    r9,  [r2], #4
 ldr    r10, [r2], #4
 ldr    r11, [r2], #4
 ldr    r12, [r2], #4
 ldr    sp,  [r2], #4
 ldr    lr,  [r2], #4
; mov    r1, #Mode_ABT:OR:I_Bit:OR:F_Bit ; Enter ABT mode, nointerrupts
 mov    r1,#Mode_ABT:OR:I_Bit   ;Enter ABT mode, no interrupts
 msr    cpsr, r1
 ldr    r0, [r2], #4
 msr    spsr, r0
 ldr    sp, [r2], #4
 ldr    lr, [r2], #4
; mov    r1, #Mode_IRQ:OR:I_Bit:OR:F_Bit ; Enter IRQ mode, nointerrupts
 mov    r1,#Mode_IRQ:OR:I_Bit   ;Enter IRQ mode, no interrupts
 msr    cpsr, r1
 ldr    r0, [r2], #4
 msr    spsr, r0
 ldr    sp, [r2], #4
 ldr    lr, [r2], #4
; mov    r1, #Mode_UND:OR:I_Bit:OR:F_Bit ; Enter UND mode, nointerrupts
 mov    r1,#Mode_UND:OR:I_Bit   ;Enter UND mode, no interrupts
 msr    cpsr, r1
 ldr    r0, [r2], #4
 msr    spsr, r0
 ldr    sp, [r2], #4
 ldr    lr, [r2], #4
; mov    r1, #Mode_SYS:OR:I_Bit:OR:F_Bit ; Enter SYS mode, nointerrupts
 mov    r1,#Mode_SYS:OR:I_Bit   ;Enter SYS mode, no interrupts
 msr    cpsr, r1
 ldr    sp, [r2], #4
 ldr    lr, [r2]
; mov    r1, #Mode_SVC:OR:I_Bit:OR:F_Bit ; Enter SVC mode, nointerrupts
 mov    r1,#Mode_SVC:OR:I_Bit   ;Enter SVC mode, no interrupts
 msr    cpsr, r1
 ldr    r0, [r3, #SleepState_SVC_SPSR]
 msr    spsr, r0
;      2. Recover Last mode's REG's, & go back to caller ofCPUPowerOff()
 ldr    sp, [r3, #SleepState_SVC_SP]
 ldr    lr, [sp], #4
 ldmia   sp!,{r4-r12}
 mov    pc,lr                         ; and now back to our sponsors
最后一句就是把返回地址装入PC,这个返回地址是什么,哈哈,就是在power.c的OEMPowerOff函数 CPUPowerOff()后应该执行的指令,也就是需要执行CPULoadRegs(CPUBackupRegs)了!!这下大家都明白了吧!!

本文出自 “Mobile and Linux Deve..” 博客,请务必保留此出处http://buaadallas.blog.51cto.com/399160/80930

csdn论坛

求WinCE的关机函数[问题点数:20分,无满意答案结帖,结帖人:kornberg206]

楼主发表于:2009-04-09 19:29:54
是哪个函数啦?
 
 
#1楼 得分:0回复于:2009-04-09 20:37:41
winCE好像只有挂机的功能,要完全关机的话需要硬件上支持.我以前做了一个项目中是采用了两个IO口来实现的,一个用于检测一个执行关机.长按3秒关机,短按是挂起.实现挂机的函数是PowerOffSystem()
 
#2楼 得分:0回复于:2009-04-09 20:41:59
SetSystemPowerState
 
#3楼 得分:0回复于:2009-04-09 21:23:48
其实关机函数就是通过一个IO来控制电源芯片,

电源芯片不工作了,就关机了。
 
#4楼 得分:0回复于:2009-04-09 22:54:19
可以用IOCTROL实现的,在KERNEL里面提供,而且要BSP支持,SetSystemPowerState有些BSP没有实现该接口,那也是不行的。一般是要调用到驱动里面OEMPowerOff()这个函数,所以,要在这个函数面实现电源
切断的操作
 
#5楼 得分:0回复于:2009-04-09 23:24:11
引用 4 楼 ok138ok 的回复:
可以用IOCTROL实现的,在KERNEL里面提供,而且要BSP支持,SetSystemPowerState有些BSP没有实现该接口,那也是不行的。一般是要调用到驱动里面OEMPowerOff()这个函数,所以,要在这个函数面实现电源
切断的操作


OEMPowerOff()这个函数是 挂起系统的时候调用的。
挂起不是关机。这函数我上周刚弄过。
 
#6楼 得分:0回复于:2009-04-09 23:28:47
早的CE版本用PowerOffSystem,现在用SetSystemPowerState
无论是关机还是进休眠,一般都需要对文件系统等进行处理,对CPU的一些状态进行保存,不会直接断电的
 
#7楼 得分:0回复于:2009-04-10 00:14:50
引用 6 楼 hzdysymbol 的回复:
早的CE版本用PowerOffSystem,现在用SetSystemPowerState
无论是关机还是进休眠,一般都需要对文件系统等进行处理,对CPU的一些状态进行保存,不会直接断电的


对,这个关机首先是通过电源管理,会调用驱动一些电源管理相关函数的。我现在直接断电了,以后再搞回来吧。以前 2440 4.2BSP使用的是PowerOffSystem函数。
 
  • upig用户头像
  • upig
  • (31531640@qq.com)
  • 等 级:

#8楼 得分:0回复于:2009-04-10 00:25:28
一般的BSP中的pwrbutton驱动中都有示例的,一看就知道了。
 
#9楼 得分:0回复于:2009-04-10 08:59:56
关机函数是不能切断电源的,就算CPU不工作了,电源芯片以及一些外围设备还是有功耗,不知道楼主的需求是什么?
 
#10楼 得分:0回复于:2009-04-10 09:42:54
我在PowerButton驱动中加入SetSystemPowerState( NULL, POWER_STATE_OFF, POWER_FORCE );
就会打印如下的信息,然后屏幕就像挂起的样子,貌似没有什么区别。那我什么时候切断整个硬件上的电源合适呢?


PWR_IST: pPWR->State = 0x2 
PMGET! System Power state is 'on', flags 0x00010000
>>>>>>>>>>>>>>>>>>> state == on <<<<<<<<<<<<<<<<<<<
!!!!!!!!!!!! BACKLIGHT OFF !!!!!!!!!!!!
BAK: IOCTL_POWER_SET: D4 
CAMERA: CIS_PowerDown
CamClockOn = 0
CIS: IOCTL_POWER_SET: D4 
OEMIoControl: Unsupported Code 0x10100f4 - device 0x0101 func 61
HW_USBClocks::D4 
CAMERA: CIS_PowerDown
CamClockOn = 0
WAVEDEV::HardwareContext::PowerDown 
PCF: HW_PowerDown
 
#11楼 得分:0回复于:2009-04-10 09:49:13
挂起也是一样的信息,怎么回事啊。晕倒,那我还不如直接断电呢。
BAK: IOCTL_POWER_SET: D4 
CAMERA: CIS_PowerDown
CamClockOn = 0
CIS: IOCTL_POWER_SET: D4 
OEMIoControl: Unsupported Code 0x10100f4 - device 0x0101 func 61
HW_USBClocks::D4 
CAMERA: CIS_PowerDown
CamClockOn = 0
WAVEDEV::HardwareContext::PowerDown 
PCF: HW_PowerDown
 
#12楼 得分:0回复于:2009-04-10 10:24:39
我刚才试过了PowerOffSystem这个函数和SetSystemPowerState( NULL, POWER_STATE_OFF, POWER_FORCE ); 效果是一样的
同样会去调用OEMPowerOff函数——休眠也是调用这个函数实现的!

那怎么实现关机呢?为了关机安全,我觉得在PowerButton驱动里面弄一个全局变量,然后在OAL里面引用,这样就可以区分是休眠还是关机了。
 
  • lyx_wq用户头像
  • lyx_wq
  • (河蟹社会)
  • 等 级:

#13楼 得分:0回复于:2009-04-10 11:11:43

学习!UP!
 
#14楼 得分:0回复于:2009-04-10 11:13:23
现在我想问一下什么条件下可以实现软件控制电源断电,也就是真正的关机
 
#15楼 得分:0回复于:2009-04-10 11:21:22
引用 14 楼 hudaweikevin 的回复:
现在我想问一下什么条件下可以实现软件控制电源断电,也就是真正的关机


关掉供电芯片即可。
 
#16楼 得分:0回复于:2009-04-10 11:25:43
记下来,下回可以用这个关手机了hehe
 
#17楼 得分:0回复于:2009-04-10 11:25:52
其实手机也没有切断电源——从以前的老手机拔下电池就要重新设定时间就可以看出来。

手机估计那个所谓关机键应该是深度休眠而已。

那我们的PDA完全可以实现深度休眠即可吧。断电直接装个总开关算了。
 
#18楼 得分:0回复于:2009-04-10 11:35:42
引用 12 楼 gooogleman 的回复:
我刚才试过了PowerOffSystem这个函数和SetSystemPowerState( NULL, POWER_STATE_OFF, POWER_FORCE ); 效果是一样的
同样会去调用OEMPowerOff函数——休眠也是调用这个函数实现的!

那怎么实现关机呢?为了关机安全,我觉得在PowerButton驱动里面弄一个全局变量,然后在OAL里面引用,这样就可以区分是休眠还是关机了。


晕,这样是不行的。

OAL下的lib不能和DLL共享的。我想电源管理一定有特殊的标志的。请了解的,指点一下。哈哈

两个DLL驱动中如何共享一个变量?——这个怎么做的呢?很想了解一下啊。
 
#19楼 得分:0回复于:2009-04-10 11:48:14
那个我买了个万和的导航仪 WinCE界面是开放出来的  
但是只能在它的那个自带的导航仪的程序开启的时候按电源键才能选择关机,
如果我只进WinCE界面再按电源键的话是没任何反应的,
所以我想所谓的IO口控制电源芯片,它的那个驱动里肯定已经完成的,
但是我不知道如何调用,我的目的是自己写个CE程序放在WinCE桌面上,想关机点一下图标就可了,
不知道各位高手有个见解?
 
#20楼 得分:0回复于:2009-04-10 11:57:52
找厂家问问,只能这样了
 
#21楼 得分:0回复于:2009-04-10 11:57:55
SetSystemPowerState(NULL, POWER_STATE_SUSPEND, POWER_FORCE); 函数吧
在reset 和suspend 按键的驱动里面就用的是这个。
------
我在应用程序里面也可以用这个实现关机、重启、休眠
 
#22楼 得分:0回复于:2009-04-10 13:26:16
引用 19 楼 kornberg206 的回复:
那个我买了个万和的导航仪 WinCE界面是开放出来的
但是只能在它的那个自带的导航仪的程序开启的时候按电源键才能选择关机,
如果我只进WinCE界面再按电源键的话是没任何反应的,
所以我想所谓的IO口控制电源芯片,它的那个驱动里肯定已经完成的,
但是我不知道如何调用,我的目的是自己写个CE程序放在WinCE桌面上,想关机点一下图标就可了,
不知道各位高手有个见解?


你想通过自已的应用程序控制关机的话必需知道他关机的接口函数,问问厂家的软件工程师或许会告诉你.
 
  • Veabol用户头像
  • Veabol
  • (Veabol)
  • 等 级:
  • 2

    6

    4

#23楼 得分:0回复于:2009-04-10 13:50:00
引用 18 楼 gooogleman 的回复:
引用 12 楼 gooogleman 的回复:
.....
两个DLL驱动中如何共享一个变量?——这个怎么做的呢?很想了解一下啊。

我目前是采用访问同一个变量地址来操作的。
 
#24楼 得分:0回复于:2009-04-10 14:16:27
up
 
#25楼 得分:0回复于:2009-04-10 14:38:36
引用 23 楼 Veabol 的回复:
引用 18 楼 gooogleman 的回复:
引用 12 楼 gooogleman 的回复:
.....
两个DLL驱动中如何共享一个变量?——这个怎么做的呢?很想了解一下啊。
我目前是采用访问同一个变量地址来操作的。


怎么做,指针就行?我试试。
 
#26楼 得分:0回复于:2009-04-10 14:42:34
实际上所以的关机函数都是依赖OAL层的实现,所以要实现你的要求,首先要确定你关机的目的,比如要关闭那些设备和电源等,然后根据你的OAL实现的功能来确定你要调用的函数。
 
#27楼 得分:0回复于:2009-04-10 14:58:48
SetSystemPowerState()
 
#28楼 得分:0回复于:2009-04-10 16:42:20
非常雷人。

我想用SetSystemPowerState( NULL, POWER_STATE_OFF, POWER_FORCE ); 
在OEMPowerOff里面
加入

RETAILMSG(1, (TEXT("PowerOffSystem!Test@gooogleman\r\n")));
//v_pIOPregs->GPFDAT&=(~(1<<5)); // directly cut off power
pIOPort->GPFDAT&=(~(1<<5)); // directly cut off power

想让系统关机,但是居然关不了,只是挂起了。

我记得4.2BSP是这样的。咋5.0就不行了呢。
 
#29楼 得分:0回复于:2009-04-10 17:15:47
引用 28 楼 gooogleman 的回复:
非常雷人。

我想用SetSystemPowerState( NULL, POWER_STATE_OFF, POWER_FORCE );
在OEMPowerOff里面
加入

RETAILMSG(1, (TEXT("PowerOffSystem!Test@gooogleman\r\n")));
//v_pIOPregs->GPFDAT&=(~(1 < <5)); // directly cut off power
pIOPort->GPFDAT&=(~(1 < <5)); // directly cut off power

想让系统关机,但是居然关不了,只是挂起了。

我记得4.2BSP是这样的。咋5.0就不行了呢。


搞定。

原来PM会导致一些寄存器不是原来的值了,所以必须重新设置GPFCON寄存器才行的。
pIOPort->GPFCON=0x000055aa;

这样就能把电断了。

楼主就这么干了。我觉得要做好电就是要弄个方法把挂起和关机分清楚。

(*^__^*) 嘻嘻……楼主借你的宝地,涂鸦了,别介意啊。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值