Share via


Enabling Power Management (Windows Embedded CE 6.0)

1/5/2010

Power management functions respond to system calls for turning the system off or for idling it. These system calls may be triggered by either hardware or software events, such as throwing a power switch or an idle timer count.

The following table shows the functions you must implement to enable power management on your target device.

Function Description

OEMPowerOff

Places the target device in a power-off state.

OEMIdle

Places the target device in an idle state. This is the lowest energy usage state possible balanced with the need to return from idle quickly.

The kernel calls OEMIdle whenever there are no threads ready to run. The kernel provides an extern DWORD dwReschedTime value that indicates when the next known event is to occur. Such asynchronous events as user-generated interrupt from a keyboard or mouse can occur before then and need to be handled.

The kernel calls OEMPowerOff when the user presses the OFF button or when the system requests the device to power off. OEMPowerOff is responsible for handling the state of the board-level logic through the suspend and resume process.

Note

Individual drivers are responsible for handling the board-level logic for individual devices, such as video, audio, and USB.

You can find code samples of OEMIdle in %_WINCEROOT%\Platform\Common\Src\Common\Timer\Idle\Idle.c.

When the kernel calls the OEMIdle function, the OEM device is requested to go into a sleep, or idle, state. This consists of saving the current state, placing the memory into a refresh state if necessary, stopping the clock, and suspending execution.

In order to conserve power while continually awakening the target device, OEMIdle should sleep for as long as possible. This is usually until the sooner of two events: dwReschedTime, or the maximum delay supported by the hardware timer.

When an interrupt occurs, scheduled or otherwise, the device ends its idle state, the previous state is restored, and the scheduler is invoked. If no new threads are ready to run, the kernel will again call OEMIdle.

When the system returns from idle, OEMIdle must update the CurMSec variable with the real number of milliseconds that have elapsed. The sample OAL also keeps partial millisecond counts, dwPartialCurMSec, in case another interrupt occurs, which will cause the system to stop idling before the system timer fires.

The following code example shows how to implement OEMIdle with these variables.

void OEMIdle(DWORD idleParam)
{
    UINT32 baseMSec;
    UINT32 idleTimeMSec;
    INT32 idleSysTicks;
    INT32 usedCounts;
    INT32 idleCounts;
    ULARGE_INTEGER idle;

    // Get current system timer counter
    baseMSec = CurMSec;

    // Compute the remaining idle time
    idleTimeMSec = dwReschedTime - baseMSec;
    
    // Idle time has expired - we need to return
    if ((INT32)idleTimeMSec <= 0) return;

    // Limit the maximum idle time to what is supported.  
    // Counter size is the limiting parameter.  When kernel 
    // profiler or interrupt latency timing is active it is set
    // to one system tick.
    if (idleTimeMSec > g_oalTimer.maxIdleMSec) {
        idleTimeMSec = g_oalTimer.maxIdleMSec;
    }

    // We can wait only full systick
    idleSysTicks = idleTimeMSec/g_oalTimer.msecPerSysTick;
    
    // This is idle time in hi-res ticks
    idleCounts = idleSysTicks * g_oalTimer.countsPerSysTick;

    // Find how many hi-res ticks was already used
    usedCounts = OALTimerCountsSinceSysTick();

    // Prolong beat period to idle time -- don't do it idle time isn't
    // longer than one system tick. Even if OALTimerExtendSysTick function
    // should accept this value it can cause problems if kernel profiler
    // or interrupt latency timing is active.
    if (idleSysTicks > 1) {
        OALTimerExtendSysTick(idleCounts, g_oalTimer.countsMargin, 0);
    }

    // Move SoC/CPU to idle mode
    OALCPUIdle();

    // Return system tick period back to original. Don't call when idle
    // time was one system tick. See comment above.
    if (idleSysTicks > 1) {

        // Return system tick period back to original
        idleSysTicks = OALTimerReduceSysTick(
            g_oalTimer.countsPerSysTick, g_oalTimer.countsMargin
        );

        // Do we need offset counters?
        if (idleSysTicks > 0) {        
            // Fix system tick counters
            CurMSec += idleSysTicks * g_oalTimer.msecPerSysTick;
            g_oalTimer.curCounts += idleSysTicks * g_oalTimer.countsPerSysTick;
        }            

        // Get where we are inside tick
        idleCounts = OALTimerCountsSinceSysTick();
    }

    // Update idle time
    idle.LowPart = curridlelow;
    idle.HighPart = curridlehigh;
    idle.QuadPart += idleCounts - usedCounts;
    curridlelow  = idle.LowPart;
    curridlehigh = idle.HighPart;
}

Use the previous code example and the system tick ISR to implement the extended idle period.

The following code example is used to determine whether the kernel should schedule a thread (SYSINTR_RESCHED returned) or do nothing (SYSINTR_NOP returned).

ULONG ulRet = SYSINTR_NOP;
if ((int)(CurMSec >= dwReschedTime))
       ulRet = SYSINTR_RESCHED;

By returning SYSTINTR_NOP when appropriate, a system tick ISR can prevent the kernel from rescheduling unnecessarily and therefore save power.

For more information about the variables the OAL needs to update in order to support OEMIdle, see CPU Utilization.

See Also

Tasks

How to Develop an OEM Adaptation Layer