first commit for opensource
first commit for opensource
This commit is contained in:
382
components/connectivity/LoraWAN/system/timer.c
Normal file
382
components/connectivity/LoraWAN/system/timer.c
Normal file
@@ -0,0 +1,382 @@
|
||||
/*!
|
||||
* \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 )
|
||||
{
|
||||
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( );
|
||||
}
|
Reference in New Issue
Block a user