在WinCE OAL中的电源管理主要由OEMIdle和OEMPowerOff两个函数实现。应该说OEMIdle实现的是处理器级的电源管理,而OEMPowerOff实现的是板级的电源管理。
在WinCE系统运行的时候,如果没有任何线程可以执行,那么内核就会调用OEMIdle函数。一般在OEMIdle中,处理器都会进入sleep模式或者idle模式,这取决于处理器本身所能支持的低功耗模式。应该说,这个时候系统中的各个设备还是正常工作的,只是处理器进入了一种低功耗模式。OEMIdle函数是OAL中必须实现的,微软也提供了一个例子,在%_WINCEROOT%\Platform\Common\Src\Common\Timer\Idle路径下,下面就这个例子简单分析一下:
void OEMIdle(DWORD idleParam)
{
UINT32 baseMSec, idleMSec, idleSysTicks;
INT32 usedCounts, idleCounts;
ULARGE_INTEGER idle;
//保存当前系统的毫秒数
baseMSec = CurMSec;
//根据下一次调度的时间计算出当前空闲的时间
idleMSec = dwReschedTime - baseMSec;
//如果没有空闲时间,直接返回
if ((INT32)idleMSec <= 0) return;
//如果空闲时间超过了硬件Timer的最大技术,则以硬件Timer的最大计数为准
if (idleMSec > g_oalTimer.maxPeriodMSec) {
idleMSec = g_oalTimer.maxPeriodMSec;
}
// 计算空闲时间相当于多少系统tick
idleSysTicks = idleMSec/g_oalTimer.msecPerSysTick;
// 计算空闲时间相当于多少个Timer的count
idleCounts = idleSysTicks * g_oalTimer.countsPerSysTick;
// 已经流逝了多少个Timer的count
usedCounts = OALTimerCountsSinceSysTick();
// 判断空闲时间大于1个系统tick
if (idleSysTicks > 1) {
// 调用OALTimerUpdate函数更新硬件Timer的count,延长为空闲时间
OALTimerUpdate(idleCounts, g_oalTimer.countsMargin);
// 更新实际的系统tick的毫秒数和count数
g_oalTimer.actualMSecPerSysTick = idleMSec;
g_oalTimer.actualCountsPerSysTick = idleCounts;
}
// 处理器进入Idle模式,并等待中断唤醒,此期间可能被其他中断唤醒,也可能
// 等到空间时间过去后,由系统Timer中断唤醒
OALCPUIdle();
// 判断空闲时间大于1个系统tick
if (idleSysTicks > 1) {
// 如果产生的中断不是系统Timer中断
if (CurMSec == baseMSec) {
// 调用OALTimerUpdate函数更新硬件Timer为原来的计数值
idleSysTicks = OALTimerUpdate(
g_oalTimer.countsPerSysTick, g_oalTimer.countsMargin
);
// 恢复相关变量赋值
g_oalTimer.actualMSecPerSysTick = g_oalTimer.msecPerSysTick;
g_oalTimer.actualCountsPerSysTick = g_oalTimer.countsPerSysTick;
// 更新CurMSec和idleCounts
CurMSec += idleSysTicks * g_oalTimer.actualMSecPerSysTick;
idleCounts = idleSysTicks * g_oalTimer.actualCountsPerSysTick;
g_oalTimer.curCounts += idleCounts;
idleCounts += OALTimerCountsSinceSysTick();
}
} else {
if (CurMSec == baseMSec) {
// 不是系统Timer中断,只更新idleCounts
idleCounts = OALTimerCountsSinceSysTick();
}
}
// 获得实际的空闲的count值
idleCounts -= usedCounts;
if (idleCounts < 0) idleCounts = 0;
// 更新64位计数器的计数值
idle.LowPart = curridlelow;
idle.HighPart = curridlehigh;
idle.QuadPart += idleCounts;
curridlelow = idle.LowPart;
curridlehigh = idle.HighPart;
}
在该函数中调用了两个重要的函数,以前没有提到,这里说一下:
UINT32 OALTimerUpdate(UINT32 period, UINT32 margin)
period:要更新的计数值
margin:硬件Timer被改变所需的count数
该函数主要用于更新硬件Timer的计数值,实际上就改变了硬件Timer产生中断的频率。
VOID OALCPUIdle()
该函数主要实现了硬件处理器进入Idle模式,进入Idle模式后,处理器会停止执行在该函数中,直到中断产生后,处理器从Idle模式恢复到正常模式,然后从该函数中返回。
该blog主要介绍了一种实现OEMIdle的思想。OEMIdle函数的实现依赖于处理器所支持的低功耗模式。有些处理器支持多种低功耗模式,根据需要可以在OEMIdle中实现。但是要知道进入低功耗后恢复到正常模式下所花费的时间会影响到整个系统的实时性,所以有时候要根据自己的产品需求,在省电和实时性之间做一个取舍。
在OAL中还有一个重要的函数OEMPowerOff。当系统进入Suspend的时候,首先所有的驱动的PowerDown函数会被调用,然后就会调用OEMPowerOff函数。在该函数中,处理器会进入深度休眠,直到中断产生后,处理器恢复到正常工作模式,从该函数中返回,接下来所有驱动的PowerUp函数会被调用,然后系统恢复正常工作模式。
void OEMPowerOff (void)
该函数的实现流程如下: