timer.c

来自「S3C24A0的完整BSP包,对开发此芯片的开发者很有用.」· C语言 代码 · 共 285 行

C
285
字号
//
// 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.
//
//------------------------------------------------------------------------------
//
//  Module: timer.c           
//
//  Interface to OAL timer services.
//
#include <windows.h>
#include <nkintr.h>
#include <ceddk.h>
#include <oal.h>
#include <s3c24A0.h>

//#define ENABLE_WATCH_DOG 1
//------------------------------------------------------------------------------
// Local Variables 

static volatile S3C24A0_PWM_REG *g_pPWMRegs = NULL;
 
DWORD OEMCount1ms;

//------------------------------------------------------------------------------
//
//  Static:  g_timer
//
//  This structure contains timer internal variable. The period is value used
//  to timer recharge, margin value define maximal latency for timer recharge
//  without shift, base value is actual period base and compare is actual
//  period end. The period isn't changed by timer reduce/extend function.
//
static struct {
    UINT32 base;
    UINT32 compare;
} g_timer;

//------------------------------------------------------------------------------
//
//  Function:  OALTimerInitCount
//
//  This function initialize count/compare timer.
//
VOID OALTimerInitCount(UINT32 period)
{
    g_timer.base = OALTimerGetCount();
    g_timer.compare = g_timer.base + period;
    OALTimerSetCompare(g_timer.compare); 
}

//------------------------------------------------------------------------------
//
//  Function: OALTimerInit
//
//  This function is typically called from the OEMInit to initialize
//  Windows CE system timer. The tickMSec parameter determine timer
//  period in milliseconds. On most platform timer period will be
//  1 ms, but it can be usefull to use higher value for some
//  specific (low-power) devices.
//
//  Implementation for s3c24A0 is using timer 4 as system timer.
//
BOOL OALTimerInit(UINT32 msecPerSysTick, UINT32 countsPerMSec, UINT32 countsMargin	) 
{
	BOOL	rc = FALSE;
	UINT32	countsPerSysTick;
	UINT32	sysIntr, irq;
	DWORD	ttmp;
	volatile S3C24A0_INTR_REG   *pIntrRegs = NULL;

	RETAILMSG(1, (L"+OALTimerInit( %d, %d, %d )\r\n", msecPerSysTick, countsPerMSec, countsMargin));
	
	// Validate Input parameters
	countsPerSysTick = countsPerMSec * msecPerSysTick;
	if (
	    msecPerSysTick < 1 || msecPerSysTick > 1000 ||
	    countsPerSysTick < 1 || countsPerSysTick > 65535
		) {
	    OALMSG(OAL_ERROR, (
	        L"ERROR: OALTimerInit: System tick period out of range..."
			));
	    goto cleanUp;
	}
	
	// Initialize timer state global variable    
	g_oalTimer.msecPerSysTick = msecPerSysTick;
	g_oalTimer.countsPerMSec = countsPerMSec;
	g_oalTimer.countsMargin = countsMargin;
	g_oalTimer.countsPerSysTick = countsPerSysTick;
	g_oalTimer.curCounts = 0;
	g_oalTimer.maxPeriodMSec = 0xFFFF/g_oalTimer.countsPerMSec;
	
	// Set kernel exported globals to initial values
	idleconv = countsPerMSec;
	curridlehigh = 0;
	curridlehigh = 0;
	
	// Initialize high resolution timer function pointers
	pQueryPerformanceFrequency = OALTimerQueryPerformanceFrequency;
	pQueryPerformanceCounter = OALTimerQueryPerformanceCounter;

	// Create SYSINTR for timer	
	irq = IRQ_TIMER4;
	sysIntr = OALIntrRequestSysIntr(1, &irq, OAL_INTR_FORCE_STATIC);
	RETAILMSG(1, (TEXT("####SysIntr corresponding to IRQ_TIMER4 = %d\r\n"), sysIntr));
	
	// Hardware Setup
	g_pPWMRegs    = (S3C24A0_PWM_REG*)OALPAtoUA(S3C24A0_BASE_REG_PA_PWM);
	pIntrRegs	    = (S3C24A0_INTR_REG*)OALPAtoVA(S3C24A0_BASE_REG_PA_INTR, FALSE);

	//set the prescaler
	g_pPWMRegs->TCFG0 &= ~(0xff << 8);					// Clear Prescaler 1's Value
	g_pPWMRegs->TCFG0 |= (SYS_TIMER_PRESCALER << 8);	// Configure the Prescalar
	
	/* Timer4 Divider field clear */
	g_pPWMRegs->TCFG1 &= ~(0xf << 16);

	//select MUX input
#if	  ( SYS_TIMER_DIVIDER == D2 )
	  	g_pPWMRegs->TCFG1  |=  (D1_2 << 16);	// 1/2
#elif ( SYS_TIMER_DIVIDER == D4 )
		g_pPWMRegs->TCFG1  |=  (D1_4 << 16);	// 1/4
#elif ( SYS_TIMER_DIVIDER == D8 )
		g_pPWMRegs->TCFG1  |=  (D1_8 << 16);	// 1/8
#elif ( SYS_TIMER_DIVIDER == D16 )
		g_pPWMRegs->TCFG1  |=  (D1_16 << 16);	// 1/16
#endif

	// Set timer register
	g_pPWMRegs->TCNTB4 = g_oalTimer.countsPerSysTick;

	// Start timer in auto reload mode
	ttmp = g_pPWMRegs->TCON & (~(0xf << 20)); 	// Clear the timer4 related stuff [22:20]
	g_pPWMRegs->TCON = ttmp | (2 << 20);		// update TCNTB4, stop
	g_pPWMRegs->TCON = ttmp | (5 << 20);		// auto reload mode,  start

	// Set OEM timer count for 1 ms
	OEMCount1ms = OEM_COUNT_1MS;

	//Unmask timer interrupt //being done below
	pIntrRegs->INTMSK		&= ~BIT_TIMER34;
	pIntrRegs->INTSUBMSK	&= ~BIT_SUB_TIMER4;
		
	// Enable System Tick interrupt
	if (!OEMInterruptEnable(sysIntr, NULL, 0)) 
	{
		RETAILMSG(1, (L"ERROR: OALTimerInit: Interrupt enable for system timer failed"));
		goto cleanUp;
		
	}

	// Initialize counter/compare registers
	OALTimerInitCount(g_oalTimer.countsPerSysTick);
		
	//    
	// Define ENABLE_WATCH_DOG to enable watchdog timer support.
	// NOTE: When watchdog is enabled, the device will reset itself if watchdog timer is not refreshed within ~4.5 second.
	//       Therefore it should not be enabled when kernel debugger is connected, as the watchdog timer will not be refreshed.
	//
#ifdef ENABLE_WATCH_DOG
		{
			extern void SMDKInitWatchDogTimer (void);
			SMDKInitWatchDogTimer ();
		}
#endif
		
	// Done, return success       
	rc = TRUE;
		
cleanUp:
	OALMSG(OAL_TIMER && OAL_FUNC, (L"-OALTimerInit(rc = %d)\r\n", rc));
	return rc;
}


//------------------------------------------------------------------------------
//
//  Function: OALTimerIntrHandler
//
//  This function implement timer interrupt handler. It is called from common
//  ARM interrupt handler.
//
UINT32 OALTimerIntrHandler()
{
    UINT32 sysIntr = SYSINTR_NOP;

    // Update high resolution counter
    g_oalTimer.curCounts += g_oalTimer.countsPerSysTick;
                             
    // Update the millisecond counter
    CurMSec += g_oalTimer.msecPerSysTick;

    // Reschedule?
    if ((int)(CurMSec - dwReschedTime) >= 0) sysIntr = SYSINTR_RESCHED;

#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: OALTimerCountsSinceSysTick
//
//  This function return count of hi res ticks since system tick.
//
//  Timer 4 counts down, so we should substract actual value from 
//  system tick period.
//

INT32 OALTimerCountsSinceSysTick()
{
//   RETAILMSG(1, (TEXT("OALTimerCountsSinceSysTick\r\n")));
    return (g_oalTimer.countsPerSysTick - g_pPWMRegs->TCNTO4);
}

//------------------------------------------------------------------------------
//
//  Function: OALTimerUpdate
//
//  This function is called to change length of actual system timer period.
//  If end of actual period is closer than margin period isn't changed (so
//  original period elapse). Function returns time which already expires
//  in new period length units. If end of new period is closer to actual time
//  than margin period end is shifted by margin (but next period should fix
//  this shift - this is reason why OALTimerRecharge doesn't read back 
//  compare register and it uses saved value instead).
//
UINT32 OALTimerUpdate(UINT32 period, UINT32 margin)
{
    UINT32 rc, count, offset, edge;

    count = OALTimerGetCount();
    edge = count + margin;

    if (period == 0) {
        // Period is zero, let interrupt occurs asap
        rc = 0;
        g_timer.compare = edge;
        OALTimerSetCompare(g_timer.compare);
    } else if ((INT32)(g_timer.compare - edge) > 0) {
        // We are more than margin before actual compare, update period
        offset = count - g_timer.base;
        // Avoid divide/multiply for most cases
        if ((INT32)(offset - period) <= 0) {
            rc = 0;
        } else {            
            rc = offset/period;
            g_timer.base += rc * period;         
        }
        // New compare value is base plus period
        g_timer.compare = g_timer.base + period;
        // Are we more than margin before new compare?
        if ((INT32)(g_timer.compare - edge) > 0) {
            // Yes, then use calculated value
            OALTimerSetCompare(g_timer.compare);
        } else {
            // No, shift real compare value
            OALTimerSetCompare(edge);
        }            
    } else {
        // No, stay with original period
        rc = (g_timer.compare - g_timer.base)/period - 1;
    }    
    return rc;
}

//------------------------------------------------------------------------------

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?