semtech loranet stack源码里实现了一个基于硬件rtc的tickless软件定时器,可以很方便移植到不同mcu上来使用,该定时器基于硬件rtc,使用rtc alarm和链表来实现,链表头存放的是最近到期的timer,相比基于systick的周期定时实现的软件定时器而言会更适合低功耗方面的应用。源代码如下,
#ifndef __TIMER_H__
#define __TIMER_H__
#ifdef __cplusplus
extern "C"
{
#endif
#include
#include
#include
typedef struct TimerEvent_s
{
uint32_t Timestamp; //! Current timer value
uint32_t ReloadValue; //! Timer delay value
bool IsStarted; //! Is the timer currently running
bool IsNext2Expire; //! Is the next timer to expire
void ( *Callback )( void* context ); //! Timer IRQ callback function
void *Context; //! User defined data object pointer to pass back
struct TimerEvent_s *Next; //! Pointer to the next Timer object.
}TimerEvent_t;
#ifndef TimerTime_t
typedef uint32_t TimerTime_t;
#define TIMERTIME_T_MAX ( ( uint32_t )~0 )
#endif
void TimerInit( TimerEvent_t *obj, void ( *callback )( void *context ) );
void TimerSetContext( TimerEvent_t *obj, void* context );
void TimerIrqHandler( void );
void TimerStart( TimerEvent_t *obj );
bool TimerIsStarted( TimerEvent_t *obj );
void TimerStop( TimerEvent_t *obj );
void TimerReset( TimerEvent_t *obj );
void TimerSetValue( TimerEvent_t *obj, uint32_t value );
TimerTime_t TimerGetCurrentTime( void );
#ifdef __cplusplus
}
#endif
#endif // __TIMER_H__
#include "timer.h"
#include "rtc.h"
#define ExecuteCallBack( _callback_, context )
do
{
if( _callback_ == NULL )
{
while( 1 );
}
else
{
_callback_( context );
}
}while( 0 );
static TimerEvent_t *TimerListHead = NULL;
static void TimerInsertNewHeadTimer( TimerEvent_t *obj );
static void TimerInsertTimer( TimerEvent_t *obj );
static void TimerSetTimeout( TimerEvent_t *obj );
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;
if( ( obj == NULL ) || ( TimerExists( obj ) == true ) )
{
return;
}
obj->Timestamp = obj->ReloadValue;
obj->IsStarted = true;
obj->IsNext2Expire = false;
if( TimerListHead == NULL )
{
// 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 );
}
}
}
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 = MCU_RTC_Get( );
uint32_t now = MCU_RTC_Get( );
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 < MCU_RTC_Get() ) )
{
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 )
{
TimerEvent_t* prev = TimerListHead;
TimerEvent_t* cur = TimerListHead;
// List is empty or the obj to stop does not exist
if( ( TimerListHead == NULL ) || ( obj == NULL ) )
{
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( );
RTC_ITConfig( RTC_IT_SEC, DISABLE);
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;
}
}
}
}
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 = ( value );
TimerStop( obj );
minValue = 1;
if( ticks < minValue )
{
ticks = minValue;
}
ticks += MCU_RTC_Get();
obj->Timestamp = ticks;
obj->ReloadValue = ticks;
}
TimerTime_t TimerGetCurrentTime( void )
{
uint32_t now = MCU_RTC_Get( );
return ( now );
}
static void TimerSetTimeout( TimerEvent_t *obj )
{
int32_t minTicks= 1;
obj->IsNext2Expire = true;
// In case deadline too soon
if( obj->Timestamp < ( RtcGetTimerElapsedTime( ) + minTicks ) )
{
obj->Timestamp = RtcGetTimerElapsedTime( ) + minTicks;
}
RTC_Alarm_Set( obj->Timestamp );
}



