semtech loranet stack源码里实现了一个基于硬件rtc的tickless软件定时器,可以很方便移植到不同mcu上来使用,该定时器基于硬件rtc,使用rtc alarm和链表来实现,链表头存放的是最近到期的timer,相比基于systick的周期定时实现的软件定时器而言会更适合低功耗方面的应用。源代码如下,
/*!
* \file timer.h
*
* \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 )
*/
#ifndef __TIMER_H__
#define __TIMER_H__
#ifdef __cplusplus
extern "C"
{
#endif
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
/*!
* \brief Timer object description
*/
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;
/*!
* \brief Timer time variable definition
*/
#ifndef TimerTime_t
typedef uint32_t TimerTime_t;
#define TIMERTIME_T_MAX ( ( uint32_t )~0 )
#endif
/*!
* \brief Initializes the timer object
*
* \remark TimerSetValue function must be called before starting the timer.
* this function initializes timestamp and reload value at 0.
*
* \param [IN] obj Structure containing the timer object parameters
* \param [IN] callback Function callback called at the end of the timeout
*/
void TimerInit( TimerEvent_t *obj, void ( *callback )( void *context ) );
/*!
* \brief Sets a user defined object pointer
*
* \param [IN] context User defined data object pointer to pass back
* on IRQ handler callback
*/
void TimerSetContext( TimerEvent_t *obj, void* context );
/*!
* Timer IRQ event handler
*/
void TimerIrqHandler( void );
/*!
* \brief Starts and adds the timer object to the list of timer events
*
* \param [IN] obj Structure containing the timer object parameters
*/
void TimerStart( TimerEvent_t *obj );
/*!
* \brief Checks if the provided timer is running
*
* \param [IN] obj Structure containing the timer object parameters
*
* \retval status returns the timer activity status [true: Started,
* false: Stopped]
*/
bool TimerIsStarted( TimerEvent_t *obj );
/*!
* \brief Stops and removes the timer object from the list of timer events
*
* \param [IN] obj Structure containing the timer object parameters
*/
void TimerStop( TimerEvent_t *obj );
/*!
* \brief Resets the timer object
*
* \param [IN] obj Structure containing the timer object parameters
*/
void TimerReset( TimerEvent_t *obj );
/*!
* \brief Set timer new timeout value
*
* \param [IN] obj Structure containing the timer object parameters
* \param [IN] value New timer timeout value
*/
void TimerSetValue( TimerEvent_t *obj, uint32_t value );
/*!
* \brief Read the current time
*
* \retval time returns current time
*/
TimerTime_t TimerGetCurrentTime( void );
#ifdef __cplusplus
}
#endif
#endif // __TIMER_H__
/*!
* \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 "timer.h"
#include "rtc.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;
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 );
}