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 |
---|---|
Places the target device in a power-off state. |
|
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.