timer.c
来自「该BSP是基于PXA270+WINCE的BSP」· C语言 代码 · 共 329 行
C
329 行
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//------------------------------------------------------------------------------
//
// File: timer.c
//
// Intel Mainstone board initialization code.
//
#include <windows.h>
#include <nkintr.h>
#include <bulverde.h>
#include <oal.h>
//------------------------------------------------------------------------------
// Defines
//------------------------------------------------------------------------------
// External Variables
//------------------------------------------------------------------------------
// Global Variables
//------------------------------------------------------------------------------
// Local Variables
static XLLP_OST_HANDLE_T g_XllpOSTHandle;
static volatile UINT32 LastTimerMatch;
static UINT32 g_TotalPartialCounts = 0;
static volatile UINT32 g_LastPartialCounts = 0;
//------------------------------------------------------------------------------
// Local Functions
__inline void RechargeTimer(DWORD reschedTime, BOOL isTimerISR);
VOID OALTimerUpdateRescheduleTime(DWORD time);
BOOL OALTimerInit (UINT32 msecPerSysTick,
UINT32 countsPerMSec,
UINT32 countsMargin)
{
UINT32 TimerMatch;
UINT32 TimerCount;
// Initialize timer state global variable.
//
g_oalTimer.countsPerMSec = countsPerMSec;
g_oalTimer.msecPerSysTick = msecPerSysTick;
g_oalTimer.actualMSecPerSysTick = msecPerSysTick;
g_oalTimer.countsMargin = countsMargin;
g_oalTimer.countsPerSysTick = (countsPerMSec * msecPerSysTick);
g_oalTimer.actualCountsPerSysTick = (countsPerMSec * msecPerSysTick);
g_oalTimer.curCounts = 0;
g_oalTimer.maxPeriodMSec = (UINT32)0x7FFFFFFF/g_oalTimer.countsPerMSec;
// Initialize kernel-exported values.
//
idleconv = countsPerMSec;
curridlehigh = 0;
curridlehigh = 0;
// Initialize update reschedule time function pointer
pOEMUpdateRescheduleTime = OALTimerUpdateRescheduleTime;
// Initialize high resolution timer function pointers
pQueryPerformanceFrequency = OALTimerQueryPerformanceFrequency;
pQueryPerformanceCounter = OALTimerQueryPerformanceCounter;
// Obtain pointers to OST and INTC registers.
//
g_XllpOSTHandle.pOSTRegs = (XLLP_OST_T *) OALPAtoVA(BULVERDE_BASE_REG_PA_OST, FALSE);
g_XllpOSTHandle.pINTCRegs = (XLLP_INTC_T *)OALPAtoVA(BULVERDE_BASE_REG_PA_INTC, FALSE);
// XLLI initializes oier and rtsr to zeroes, so no further
// initialization needs to be done. Match timers and
// alarms are disabled.
//
// Current usage of Match registers:
// M0 - Scheduler
// M1 - Touch Panel
// M2 - Profiler
// Configure and arm the timer interrupt to interrupt starting at current count + 1 system tick interval.
//
TimerCount = g_XllpOSTHandle.pOSTRegs->oscr0;
TimerMatch = TimerCount + g_oalTimer.countsPerSysTick;
XllpOstConfigureTimer (&g_XllpOSTHandle, MatchReg0, TimerMatch);
// set LastTimerMatch to TimerCount
//
LastTimerMatch = TimerCount;
return(TRUE);
}
//------------------------------------------------------------------------------
//
// Function: OALTimerCountsSinceSysTick
//
// This function return count of hi res ticks since system tick.
// (How many ticks since the last timer interrupt?)
//
INT32 OALTimerCountsSinceSysTick()
{
return (g_XllpOSTHandle.pOSTRegs->oscr0 - LastTimerMatch);
}
//------------------------------------------------------------------------------
//
// Function: OALTimerIntrHandler
//
// This function implement timer interrupt handler. It is called from common
// ARM interrupt handler.
//
UINT32 OALTimerIntrHandler()
{
UINT32 sysIntr = SYSINTR_NOP;
LastTimerMatch = g_XllpOSTHandle.pOSTRegs->osmr0;
// Update the millisecond and high resolution counters
CurMSec += g_oalTimer.actualMSecPerSysTick;
g_oalTimer.curCounts += g_oalTimer.actualCountsPerSysTick;
// g_LastPartialCounts represents fractional milliseconds which will
// be added to CurMSec once they add up to one MSec.
g_TotalPartialCounts += g_LastPartialCounts;
g_LastPartialCounts = 0;
//Handle Wrap-around
if ((INT32)(CurMSec - dwReschedTime) >= 0)
sysIntr = SYSINTR_RESCHED;
else {
if ((g_TotalPartialCounts > g_oalTimer.countsPerMSec) && (dwReschedTime > (CurMSec + 1))) {
CurMSec++;
g_TotalPartialCounts -= g_oalTimer.countsPerMSec;
}
}
RechargeTimer(dwReschedTime, TRUE);
// Update LEDs.
//
OEMWriteDebugLED(0, (CurMSec/1000));
#ifdef OAL_ILTIMING
if (g_oalILT.active) {
if (--g_oalILT.counter == 0) {
sysIntr = SYSINTR_TIMING;
g_oalILT.counter = g_oalILT.counterSet;
g_oalILT.isrTime2 = OALTimerCountsSinceSysTick();
}
}
#endif
return (sysIntr);
}
//------------------------------------------------------------------------------
//
// Function: OALTimerUpdateRescheduleTime
//
// This function is called by kernel to set next reschedule time.
//
VOID OALTimerUpdateRescheduleTime(DWORD time)
{
UINT32 baseMSec;
INT32 counts;
// Get current system timer counter
baseMSec = CurMSec;
// Return if we are already setup correctly
if (time == (baseMSec + g_oalTimer.actualMSecPerSysTick)) goto cleanUp;
// How far we are from next tick
counts = g_oalTimer.actualCountsPerSysTick - OALTimerCountsSinceSysTick();
// If timer interrupts occurs, or we are within 1 ms of the scheduled
// interrupt, just return - timer ISR will take care of it.
if (baseMSec != CurMSec || counts < (INT32)g_oalTimer.countsPerMSec) {
goto cleanUp;
}
//Note: We are going to assume that RechargeTimer will not take more than 1 ms and since we have already
// checked above that there is at least 1 ms before the next timer interrupt, no timer interrupts
// can occur during RechargeTimer execution (thus we satisfy the condition imposed by RechargeTimer)
RechargeTimer(time, 0);
cleanUp:
return;
}
//------------------------------------------------------------------------------
//
// Function: RechargeTimer
//
//
// Assumes: a timer interrupt cannot occur inside this function (caller must ensure this condition)
//
__inline void RechargeTimer(DWORD reschedMSec, BOOL isTimerISR)
{
UINT32 baseMSec;
UINT32 deltaMSec = 0;
UINT32 deltaCounts;
UINT32 prevDeltaCounts;
UINT32 TimerCount;
UINT32 newTimerMatch;
UINT32 deltaMSec1;
baseMSec = CurMSec;
// reschedule time already passed?
if ((INT32)(baseMSec - reschedMSec) >= 0) {
// called by timerisr?
// if yes, schedule next tick to maxsystick (rescheduler will run when SYSINTR_RESCHED is returned by ISR)
if (isTimerISR)
deltaMSec = g_oalTimer.maxPeriodMSec;
// if no, need to schedule earlier than 1 systick (as rescheduler needs to run ASAP)
else
deltaMSec = 0;
}
else {
deltaMSec = reschedMSec - baseMSec;
// If next tick is longer than maximal tick period,
// then schedule next tick for max allowed MSec by the timer counter
if (deltaMSec > g_oalTimer.maxPeriodMSec)
deltaMSec = g_oalTimer.maxPeriodMSec;
}
// at this point we know the deltaMSec from CurMSec when next systick has to be scheduled
// since timers work off of counts calculate deltaCounts
deltaCounts = deltaMSec * g_oalTimer.countsPerMSec;
// Recharge timer to start new period
// ideal case: increment matchreg (osmr0) by deltaCounts
// but can't do simply this because while this routine is being executed oscr0 is incrementing.
// Hence, for these two conditions need to increment deltaCounts so that new osmr0 will be ahead of oscr0.
// 1) if deltaCounts is too small and incrementing osmr0 by deltaCounts puts it before oscr0
// 2) if incremeting osmr0 by deltaCounts puts it ahead of oscr0 but so close that by the time osmr0 is set oscr0
// will be ahead of it
// For both these conditions it is suffice to use this condition if (osmr0 + deltaCounts) < (oscr0 + margin) then increment deltaCounts
newTimerMatch = LastTimerMatch + deltaCounts;
/*** TIMER SPECIFIC CODE ***/
TimerCount = g_XllpOSTHandle.pOSTRegs->oscr0;
/*** TIMER SPECIFIC CODE END***/
// setting osmr0 to newTimerMatch puts it before oscr0?
if (((INT32)(TimerCount + g_oalTimer.countsMargin - LastTimerMatch - deltaCounts) > 0)) {
newTimerMatch = TimerCount + g_oalTimer.countsMargin;
prevDeltaCounts = deltaCounts;
//since timermatch value changed we need to recalculate deltaCounts and deltaMSec.
//These values are used to set actualMSecPerSysTick and actualCountsPerSysTick.
deltaCounts = (INT32)(newTimerMatch - LastTimerMatch);
if (deltaCounts < g_oalTimer.countsPerMSec) {
deltaMSec = 0;
g_LastPartialCounts = deltaCounts;
}
else if (deltaCounts < (g_oalTimer.countsPerMSec << 1)) { // deltaCounts < 2MSec
deltaMSec = 1;
g_LastPartialCounts = deltaCounts - g_oalTimer.countsPerMSec;
}
else {
deltaMSec1 = deltaCounts / g_oalTimer.countsPerMSec;
// deltaMSec1 above denotes the time that CurMSec should be incremented by next time when timer ISR runs.
// But if if oscr0 ran way ahead of osmr0 due to interrupts being turned off deltaMSec1 could be greater than
// deltaMSec which is the max value we can set for actualMSecPerSysTick at this point.
// Note: in this case we will lose some time off of CurMSec compared to the wallclock.
if (deltaMSec1 > deltaMSec) {
// keep the old deltaMSec (i.e. do not reset it) but set it to 1 if it is 0
if (!deltaMSec) {
deltaMSec = 1;
deltaCounts = g_oalTimer.countsPerMSec;
}
else {
deltaCounts = prevDeltaCounts;
}
}
else {
// reset deltaMSec
deltaMSec = deltaMSec1;
// deltaCounts is already calculated
g_LastPartialCounts = deltaCounts - deltaMSec * g_oalTimer.countsPerMSec;
}
}
}
/*** TIMER SPECIFIC CODE ***/
XllpOstConfigureTimer(&g_XllpOSTHandle, MatchReg0, newTimerMatch);
/*** TIMER SPECIFIC CODE END***/
g_oalTimer.actualMSecPerSysTick = deltaMSec;
g_oalTimer.actualCountsPerSysTick = deltaCounts;
#ifdef DEBUG
//code to check assumption that timer interrupt should not occur during this routine
//
if (baseMSec != CurMSec)
{
OEMWriteDebugLED(0, (0xEbbb | 0x1)); //code for error
while(1) {};
}
#endif
}
//------------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?