/*! * \file timer.c * * \brief Timer objects and scheduling management implementation * * \copyright Revised BSD License, see section \ref LICENSE. * * \code * ______ _ * / _____) _ | | * ( (____ _____ ____ _| |_ _____ ____| |__ * \____ \| ___ | (_ _) ___ |/ ___) _ \ * _____) ) ____| | | || |_| ____( (___| | | | * (______/|_____)_|_|_| \__)_____)\____)_| |_| * (C)2013-2017 Semtech * * \endcode * * \author Miguel Luis ( Semtech ) * * \author Gregory Cristian ( Semtech ) */ #include "utilities.h" #include "board.h" #include "rtc-board.h" #include "timer.h" /*! * Safely execute call back */ #define ExecuteCallBack( _callback_, context ) \ do \ { \ if( _callback_ == NULL ) \ { \ while( 1 ); \ } \ else \ { \ _callback_( context ); \ } \ }while( 0 ); /*! * Timers list head pointer */ static TimerEvent_t *TimerListHead = NULL; /*! * \brief Adds or replace the head timer of the list. * * \remark The list is automatically sorted. The list head always contains the * next timer to expire. * * \param [IN] obj Timer object to be become the new head * \param [IN] remainingTime Remaining time of the previous head to be replaced */ static void TimerInsertNewHeadTimer( TimerEvent_t *obj ); /*! * \brief Adds a timer to the list. * * \remark The list is automatically sorted. The list head always contains the * next timer to expire. * * \param [IN] obj Timer object to be added to the list * \param [IN] remainingTime Remaining time of the running head after which the object may be added */ static void TimerInsertTimer( TimerEvent_t *obj ); /*! * \brief Sets a timeout with the duration "timestamp" * * \param [IN] timestamp Delay duration */ static void TimerSetTimeout( TimerEvent_t *obj ); /*! * \brief Check if the Object to be added is not already in the list * * \param [IN] timestamp Delay duration * \retval true (the object is already in the list) or false */ static bool TimerExists( TimerEvent_t *obj ); void TimerInit( TimerEvent_t *obj, void ( *callback )( void *context ) ) { obj->Timestamp = 0; obj->ReloadValue = 0; obj->IsStarted = false; obj->IsNext2Expire = false; obj->Callback = callback; obj->Context = NULL; obj->Next = NULL; } void TimerSetContext( TimerEvent_t *obj, void* context ) { obj->Context = context; } void TimerStart( TimerEvent_t *obj ) { uint32_t elapsedTime = 0; CRITICAL_SECTION_BEGIN( ); if( ( obj == NULL ) || ( TimerExists( obj ) == true ) ) { CRITICAL_SECTION_END( ); return; } obj->Timestamp = obj->ReloadValue; obj->IsStarted = true; obj->IsNext2Expire = false; if( TimerListHead == NULL ) { RtcSetTimerContext( ); // Inserts a timer at time now + obj->Timestamp TimerInsertNewHeadTimer( obj ); } else { elapsedTime = RtcGetTimerElapsedTime( ); obj->Timestamp += elapsedTime; if( obj->Timestamp < TimerListHead->Timestamp ) { TimerInsertNewHeadTimer( obj ); } else { TimerInsertTimer( obj ); } } CRITICAL_SECTION_END( ); } static void TimerInsertTimer( TimerEvent_t *obj ) { TimerEvent_t* cur = TimerListHead; TimerEvent_t* next = TimerListHead->Next; while( cur->Next != NULL ) { if( obj->Timestamp > next->Timestamp ) { cur = next; next = next->Next; } else { cur->Next = obj; obj->Next = next; return; } } cur->Next = obj; obj->Next = NULL; } static void TimerInsertNewHeadTimer( TimerEvent_t *obj ) { TimerEvent_t* cur = TimerListHead; if( cur != NULL ) { cur->IsNext2Expire = false; } obj->Next = cur; TimerListHead = obj; TimerSetTimeout( TimerListHead ); } bool TimerIsStarted( TimerEvent_t *obj ) { return obj->IsStarted; } void TimerIrqHandler( void ) { TimerEvent_t* cur; TimerEvent_t* next; uint32_t old = RtcGetTimerContext( ); uint32_t now = RtcSetTimerContext( ); uint32_t deltaContext = now - old; // intentional wrap around // Update timeStamp based upon new Time Reference // because delta context should never exceed 2^32 if( TimerListHead != NULL ) { for( cur = TimerListHead; cur->Next != NULL; cur = cur->Next ) { next = cur->Next; if( next->Timestamp > deltaContext ) { next->Timestamp -= deltaContext; } else { next->Timestamp = 0; } } } // Execute immediately the alarm callback if ( TimerListHead != NULL ) { cur = TimerListHead; TimerListHead = TimerListHead->Next; cur->IsStarted = false; ExecuteCallBack( cur->Callback, cur->Context ); } // Remove all the expired object from the list while( ( TimerListHead != NULL ) && ( TimerListHead->Timestamp < RtcGetTimerElapsedTime( ) ) ) { cur = TimerListHead; TimerListHead = TimerListHead->Next; cur->IsStarted = false; ExecuteCallBack( cur->Callback, cur->Context ); } // Start the next TimerListHead if it exists AND NOT running if( ( TimerListHead != NULL ) && ( TimerListHead->IsNext2Expire == false ) ) { TimerSetTimeout( TimerListHead ); } } void TimerStop( TimerEvent_t *obj ) { CRITICAL_SECTION_BEGIN( ); TimerEvent_t* prev = TimerListHead; TimerEvent_t* cur = TimerListHead; // List is empty or the obj to stop does not exist if( ( TimerListHead == NULL ) || ( obj == NULL ) ) { CRITICAL_SECTION_END( ); return; } obj->IsStarted = false; if( TimerListHead == obj ) // Stop the Head { if( TimerListHead->IsNext2Expire == true ) // The head is already running { TimerListHead->IsNext2Expire = false; if( TimerListHead->Next != NULL ) { TimerListHead = TimerListHead->Next; TimerSetTimeout( TimerListHead ); } else { RtcStopAlarm( ); TimerListHead = NULL; } } else // Stop the head before it is started { if( TimerListHead->Next != NULL ) { TimerListHead = TimerListHead->Next; } else { TimerListHead = NULL; } } } else // Stop an object within the list { while( cur != NULL ) { if( cur == obj ) { if( cur->Next != NULL ) { cur = cur->Next; prev->Next = cur; } else { cur = NULL; prev->Next = cur; } break; } else { prev = cur; cur = cur->Next; } } } CRITICAL_SECTION_END( ); } static bool TimerExists( TimerEvent_t *obj ) { TimerEvent_t* cur = TimerListHead; while( cur != NULL ) { if( cur == obj ) { return true; } cur = cur->Next; } return false; } void TimerReset( TimerEvent_t *obj ) { TimerStop( obj ); TimerStart( obj ); } void TimerSetValue( TimerEvent_t *obj, uint32_t value ) { uint32_t minValue = 0; uint32_t ticks = RtcMs2Tick( value ); TimerStop( obj ); minValue = RtcGetMinimumTimeout( ); if( ticks < minValue ) { ticks = minValue; } obj->Timestamp = ticks; obj->ReloadValue = ticks; } TimerTime_t TimerGetCurrentTime( void ) { uint32_t now = RtcGetTimerValue( ); return RtcTick2Ms( now ); } TimerTime_t TimerGetElapsedTime( TimerTime_t past ) { if ( past == 0 ) { return 0; } uint32_t nowInTicks = RtcGetTimerValue( ); uint32_t pastInTicks = RtcMs2Tick( past ); // Intentional wrap around. Works Ok if tick duration below 1ms return RtcTick2Ms( nowInTicks - pastInTicks ); } static void TimerSetTimeout( TimerEvent_t *obj ) { int32_t minTicks= RtcGetMinimumTimeout( ); obj->IsNext2Expire = true; // In case deadline too soon if( obj->Timestamp < ( RtcGetTimerElapsedTime( ) + minTicks ) ) { obj->Timestamp = RtcGetTimerElapsedTime( ) + minTicks; } RtcSetAlarm( obj->Timestamp ); } TimerTime_t TimerTempCompensation( TimerTime_t period, float temperature ) { return RtcTempCompensation( period, temperature ); } void TimerProcess( void ) { RtcProcess( ); }