/***************************************************************************/ /* Name : Timer.C */ /* Uses : Unit defining timers */ /* Date : */ /* Author : Andrew Que */ /* Revisions : */ /* v1.0 - ?? - QUE - Creation */ /* v1.1 - 10/5/2001 - QUE - Modified to use interrupt unit */ /* v1.2 - 10/23/2001 - QUE - Added continous countdown mode */ /* v1.3 - 12/?/2001 - QUE - Change to run of ether timer 1 or 4 */ /* */ /* Use : */ /* Each timer must follow these basic steps: */ /* 1. Define an instance of 'TimerType' */ /* 2. Set the timer instance to the desired time to count down to with */ /* the function 'Timer_SetTo' */ /* 3. Start the timer 'Timer_Start' */ /* */ /* The timer can should polled using 'Timer_IsTimmerFlag' function. */ /* When this function returns 'True', the count down is compleat. The */ /* timer may be reset using 'Timer_Reset' or set to a new value using */ /* 'Timer_SetTo'. Step 2 and 3 can be combine using 'Timer_SetAndStart'. */ /* While running, a timer may be paused using 'Timer_Stop'. The count */ /* down will then resume when 'Timer_Start' is next called. */ /* The 'TimerType' structure can be used directly to check how much time */ /* remains in the timer. Before checking anything in the sturcture, the */ /* 'Timer_Update' function must first be called. This is because the */ /* data in the structure is only changed when the timer is looked at. */ /* All units of time when using the set functions are in timer ticks. */ /* The function 'SecondsToTicks' can be used to convert milliseconds to */ /* timer ticks. */ /* At minimal, each timer instance must be polled at least once every */ /* 2**31 timer ticks. (At 100 us, this is about 60 hours. I hope this */ /* isn't a problem! ;) */ /* */ /* About : */ /* The way this unit works by keeping a free-running global clock. When */ /* any timer starts, the global clock value is saved. Any time the timer */ /* updated, the counts are decressed by the change in the global clock. */ /* Working in this fassion, the timer is only updated when it is looked */ /* at. */ /* */ /***************************************************************************/ #include "General.H" #include "Hardware.H" #include "Timer.H" #include "DSP.H" #include "Int.H" //--------------------------------------------------------------------------- // Magic value to generate 100us clock // // This value comes from the following : // 7.3728 MHz crystal // * 4 // 29.4912 MHz clock // 33.9084201388888888888888888888889 ns/clock // // 100 us = 100,000 ns // 100,000 ns / 33.9084201388888888888888888888889 ns/clock = 2949.12 // // So, 2949 is as close as we can get // // Using 100 us per clock tick gives the timers 100 us to almost a 120 hour // period. (Max determined by 2**32 100 us ticks) // // $$$DEBUG - You must compute your own "magic value" if a clock other than // the one decribed above is used. //--------------------------------------------------------------------------- #define MagicTimerValue 2949 volatile uint32 SystemTime = 0; //--------------------------------------------------------------------------- // Timer interrupt code //--------------------------------------------------------------------------- void SystemTimerInterrupt( void ) { ++SystemTime; #if ( USE_TIMER_1 ) DSP_REG( EVAIFRA ) |= BIT8; // <- Reset underflow interrupt flag #else DSP_REG( EVBIFRB ) |= BIT1; // <- Reset underflow interrupt flag #endif } //--------------------------------------------------------------------------- // Return system time // WOW!!! What's going on here? // Volatile 32-bit values (such as our system clock) can be changed durring // the middle of reading the value. The upper and lower word may not match // up because of this. This will prevent this from creating a problem. //--------------------------------------------------------------------------- uint32 GetSystemTime( void ) { volatile uint32 Result; do { Result = SystemTime; // <- Read it } while ( Result != SystemTime ); // <- Make sure it's the same return Result; } //--------------------------------------------------------------------------- // Timer initilaztion // $$$DEBUG - I only have setups for use of timer 1 or timer 4. If you need // to use timer 2 or 3 you'll have to write the initilizing code yourself ;) //--------------------------------------------------------------------------- void InitilizeTimerUnit( void ) { #if ( USE_TIMER_1 ) // Set interrupt vector SetInterruptVector( T1CINT , SystemTimerInterrupt ); DSP_REG( GPTCONA ) = 0x0000; // <- Control register DSP_REG( T1CNT ) = 0x0000; // <- Reset counter DSP_REG( T1CMPR ) = 0; // <- Compair value DSP_REG( T1PR ) = MagicTimerValue; // <- Period value DSP_REG( T1CON ) = BIT15 | BIT14 // <- Emulation control | BIT12 // <- Continues count up // <- Prescaler, 00 = Sytem clock | BIT6 // <- Enable | BIT2 // <- Reload when count = period immediatly | BIT1; // <- Compair enable DSP_REG( EVAIMRA ) |= BIT8; // <- Enable interrupt on underflow DSP_REG( EVAIFRA ) |= BIT8; // <- Reset underflow interrupt flag #else // Set interrupt vector SetInterruptVector( T4CINT , SystemTimerInterrupt ); DSP_REG( GPTCONA ) = 0x0000; // <- Control register DSP_REG( T4CNT ) = 0x0000; // <- Reset counter DSP_REG( T4CMPR ) = 0; // <- Compair value DSP_REG( T4PR ) = MagicTimerValue; // <- Period value DSP_REG( T4CON ) = BIT15 | BIT14 // <- Emulation control | BIT12 // <- Continues count up // <- Prescaler, 00 = Sytem clock | BIT6 // <- Enable | BIT2 // <- Reload when count = period immediatly | BIT1; // <- Compair enable DSP_REG( EVBIMRB ) |= BIT1; // <- Enable interrupt on underflow DSP_REG( EVBIFRB ) |= BIT1; // <- Reset underflow interrupt flag #endif InsertRegister( &SystemTimeRegister ); SystemTimeRegister.RegisterNumber = Register_SystemTime; SystemTimeRegister.RegisterRange = 2; SystemTimeRegister.Get = SystemTime_Get; SystemTimeRegister.Put = NULL; } uint32 msToTicks( uint32 ms ) { // 100 us/tick // 10 ticks = 1 ms return ( ms * 10 ) + 1; } uint32 usToTicks( uint32 us ) { // 1 tick per 100 us return ( us / 100 ) + 1; } void Timer_SetTo( struct TimerType * Timer , sint32 Ticks ) { Timer->Preset = Ticks; Timer->Count = Timer->Preset; Timer->IsTriggered = False; } void Timer_SetAndStart( struct TimerType * Timer , sint32 Ticks ) { Timer_SetTo( Timer , Ticks ); Timer_Start( Timer ); } void Timer_Reset( struct TimerType * Timer ) { Timer->Count = Timer->Preset; Timer->IsTriggered = False; } void Timer_Start( struct TimerType * Timer ) { Timer->IsRunning = True; Timer->Old = GetSystemTime(); } void Timer_Stop( struct TimerType * Timer ) { Timer->IsRunning = False; } void Timer_Update( struct TimerType * Timer ) { uint32 Ticks; uint32 TimeHold = GetSystemTime(); if ( Timer->IsRunning ) { if ( TimeHold >= Timer->Old ) Ticks = TimeHold - Timer->Old; else Ticks = Timer->Old - ( 0xFFFFFFFFL - TimeHold + 1 ); Timer->Old = TimeHold; Timer->Count -= (sint32)Ticks; if ( Timer->Count <= 0 ) { Timer->IsTriggered = True; if ( Timer->Mode == TimerMode_Continues ) { Timer->Count %= Timer->Preset; Timer->Count = Timer->Preset - Timer->Count; } else Timer->IsRunning = False; } } } boolean Timer_IsTimmerFlag( struct TimerType * Timer ) { boolean Result; Timer_Update( Timer ); Result = Timer->IsTriggered; // Single shot trigger flag in continuse mode if ( Timer->Mode == TimerMode_Continues ) Timer->IsTriggered = False; return Result; } void Wait( uint Time ) { uint32 StartTime = GetSystemTime(); while ( GetSystemTime() - StartTime < Time ); } void WaitFine( ulong us ) { uint16 LastTime; uint16 CurrentTime; long WaitTime = us / 4; LastTime = DSP_REG( T1CNT ); while ( WaitTime > 0 ) { CurrentTime = DSP_REG( T1CNT ); if ( CurrentTime >= LastTime ) WaitTime -= ( CurrentTime - LastTime ); else WaitTime -= ( MagicTimerValue - LastTime ) + CurrentTime; LastTime = CurrentTime; } }