パワー マネージメントを有効にする
パワー マネージメント関数は、システム コールに応答して、システムの電源をオフにしたり、アイドル状態にしたりします。これらのシステム コールは、電源スイッチまたはアイドル タイマ カウントのスローなどのハードウェアまたはソフトウェア イベントによって発生します。次の表は、ターゲット デバイスでパワー マネージメントを有効にするためにインプリメントする必要がある関数の一覧です。
関数 | 説明 |
---|---|
OEMPowerOff | ターゲット デバイスの電源をオフの状態にします。 |
OEMIdle | ターゲット デバイスをアイドル状態にします。この状態は、最も電力を節約でき、すぐにアイドル状態から復帰できる状態です。 |
ユーザーが OFF ボタンを押した場合、あるいは GWES がユーザーまたは OEM によって事前に定義されたタイムアウト期間を入力した場合は、カーネルは OEMPowerOff を呼び出します。これに対して、実行できるスレッドがない場合は、カーネルは OEMIdle を呼び出します。
OEMPowerOff および OEMIdle のサンプル インプリメンテーションは、%_WINCEROOT%\Platform\Hardware Development Platform\Kernel\Hal ディレクトリにあります。
OEMIdle 関数がカーネルによって呼び出されると、OEM デバイスはスリープまたはアイドル状態になるように要求されます。この状態になるには、現在の状態の保存、メモリの更新 (必要な場合)、クロックの停止、および実行の中断が行われます。
割り込み (タイマによって発生するようにスケジュールされた割り込みも含みます) が発生すると、デバイスはアイドル状態から以前の状態に復帰し、スケジューラが呼び出されます。実行できる新しいスレッドがない場合は、カーネルは再度 OEMIdle を呼び出します。
頻繁にターゲット デバイスをスリープ状態から元の状態に復帰させる場合、カーネルは電力を節約するために、スケジュールされた次のカーネル イベントがいつ発生するかについての情報を含む変数を提供します。この情報を使用して、OEMIdle 関数の動作および OEM タイマの割り込みサービス ルーチン (ISR) をカスタマイズできます。
次の表は、OEM タイマ ISR および OEMIdle 関数で使用可能なカーネル変数の一覧です。
カーネル変数 | 説明 |
---|---|
dwPreempt | このスレッドが優先されるまでのミリ秒です。 |
dwSleepMin | 最初のタイムアウトがある場合、そのタイムアウトが満了し、再スケジュールを必要とするまでのミリ秒です。 |
ticksleft | 時間は経過したがスケジューラのスリープ待ち行列によって処理されていないシステム チック数です。したがって、ゼロ以外の値を指定すると、再スケジュールが発生します。 |
dwPartialDiffMSec | DiffMSec と共に使用され、スケジューラが最後にスリープ待ち行列を調べてから経過した時間を示します。 |
Windows CE ベースのデバイスで使用する電力を節約するには、OEMIdle 関数を使用して 1 ミリ秒以上の間プロセッサをスリープ状態にします。dwSleepMin および DiffMSec 変数 (dwSleepMin - DiffMSec) を使用してシステム チック タイマをプログラムして、最初のタイムアウト時にスリープ状態から復帰させることができます。ハードウェア タイマは、システム タイマよりもタイムアウトの最大値が少ないため、ハードウェア タイマのスリープ時間を最大値にプログラムできます。システムがアイドル状態から復帰するときは、OEMIdle は CurMSec および DiffMSec 変数を実際経過したミリ秒に更新する必要があります。サンプル プラットフォームも、別の割り込みが発生したときのために、CurMSec と DiffMSec 両方のミリ秒カウントを部分的に記録しているので、システム タイマが開始する前にシステムをアイドル状態から復帰させることができます。
次のコード サンプルは、これらの変数を OEMIdle にインプリメントする方法を示しています。
void
OEMIdle(
DWORD dwIdleParam
)
{
DWORD dwIdleMSec;
DWORD dwDiffMSecPrev = *pDiffMSec;
static DWORD dwPartialCurMSec = 0; // Keep CPU-specific sub-
// millisecond leftover.
// Use for 64-bit math
ULARGE_INTEGER currIdle = {
curridlelow,
curridlehigh
};
if (ticksleft) {
// if ticksleft, potentially there are threads on the sleep
// queue waiting to run, so don't idle
return;
}
if (bProfileTimerRunning) {
// system timer is running at CPU specific profiling value ?
// just call CPUEnterIdle and return
CPUEnterIdle(dwIdleParam);
return;
}
if (dwSleepMin == 0 || fIntrTime) {
//
// No minimum sleep time specified. Wakeup on the normal
// schedule tick.
//
CPUEnterIdle(dwIdleParam);
// Update global idle time and return
currIdle.QuadPart += RESCHED_PERIOD;
curridlelow = currIdle.LowPart;
curridlehigh = currIdle.HighPart;
return;
}
if (dwDiffMSecPrev >= dwSleepMin) {
//
// According to the globals, our sleep time has already passed!
//
return;
}
//
// Calculate the idle period
//
dwIdleMSec = dwSleepMin - dwDiffMSecPrev;
//
// The system timer may not be capable of arbitrary timeouts. Get the
// CPU-specific highest possible timeout available.
//
dwIdleMSec = CPUGetSysTimerCountMax(dwIdleMSec);
//
// Since OEMIdle() is being called in the middle of a normal
// reschedule period, CurMSec, dwPartialCurMSec, dwPartialDiffMSec
// and CurTicks need to be updated accordingly.
//
CPUGetSysTimerCountElapsed(RESCHED_PERIOD, pCurMSec, pDiffMSec,
&dwPartialCurMSec, &dwPartialDiffMSec, pCurTicks);
dwDiffMSecPrev = *pDiffMSec;
//
// Set the timer to wake up much later than usual, if needed.
//
CPUSetSysTimerCount(dwIdleMSec);
CPUClearSysTimerIRQ();
//
// Enable wakeup on any interrupt, then go to sleep.
//
CPUEnterIdle(dwIdleParam);
INTERRUPTS_OFF();
//
// We're awake! The wake-up ISR (or any other ISR) has already run.
//
if (dwDiffMSecPrev != *pDiffMSec) {
//
// We completed the full period we asked to sleep. Update the
// counters.
//
// Subtract resched period, because ISR also incremented.
*pCurMSec += (dwIdleMSec - RESCHED_PERIOD);
*pDiffMSec += (dwIdleMSec - RESCHED_PERIOD);
CurTicks.QuadPart += (dwIdleMSec - RESCHED_PERIOD) *
dwReschedIncrement;
currIdle.QuadPart += dwIdleMSec;
} else {
//
// Some other interrupt woke us up before the full idle period
// was complete. Determine how much time has elapsed.
//
currIdle.QuadPart += CPUGetSysTimerCountElapsed(dwIdleMSec,
pCurMSec, pDiffMSec, &dwPartialCurMSec, &dwPartialDiffMSec,
pCurTicks);
}
// Re-arm counters
CPUSetSysTimerCount(RESCHED_PERIOD);
CPUClearSysTimerIRQ();
// Update global idle time
curridlelow = currIdle.LowPart;
curridlehigh = currIdle.HighPart;
return;
拡張されたアイドル期間をインプリメントするには、上のコード例と OAL を使用します。次の OEM タイマ ISR のコード例は、カーネルがスレッドをスケジュールするか (SYSINTR_RESCHED が返されます)、何も行わないか (SYSINTR_NOP が返されます) を決定するために使用します。
ULONG ulRet = SYSINTR_NOP;
if (ticksleft || (dwSleepMin && (dwSleepMin <= DiffMSec)) ||
(dwPreempt && (dwPreempt <= DiffMSec)))
ulRet = SYSINTR_RESCHED;
必要に応じて SYSTINTR_NOP を返すことにより、システム チック ISR は、カーネルが不必要に再スケジュールしないようにして、電力を節約します。