multi_rtimer.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /*!
  2. * \file multi_rtimer.c
  3. *
  4. * \brief Timer objects and scheduling management implementation
  5. *
  6. * \copyright Revised BSD License, see section \ref LICENSE.
  7. *
  8. * \code
  9. * ______ _
  10. * / _____) _ | |
  11. * ( (____ _____ ____ _| |_ _____ ____| |__
  12. * \____ \| ___ | (_ _) ___ |/ ___) _ \
  13. * _____) ) ____| | | || |_| ____( (___| | | |
  14. * (______/|_____)_|_|_| \__)_____)\____)_| |_|
  15. * (C)2013-2017 Semtech
  16. *
  17. * \endcode
  18. *
  19. * \author Miguel Luis ( Semtech )
  20. *
  21. * \author Gregory Cristian ( Semtech )
  22. *
  23. * \author Forest-Rain
  24. */
  25. #include "board.h"
  26. #ifdef MULTI_RTIMER_USING_TRAGET_MCU_STM32_RTC
  27. #include "hw_rtc_stm32.h"
  28. #endif
  29. #include "multi_rtimer.h"
  30. /*!
  31. * Safely execute call back
  32. */
  33. #define EXECUTE_CALLBACK( _callback_ ) \
  34. do \
  35. { \
  36. if( _callback_ == NULL ) \
  37. { \
  38. while( 1 ); \
  39. } \
  40. else \
  41. { \
  42. _callback_( ); \
  43. } \
  44. }while( 0 );
  45. /*!
  46. * Timers list head pointer
  47. */
  48. static timer_event_t *timer_list_head = NULL;
  49. /*!
  50. * \brief Sets a timeout with the duration "timestamp"
  51. *
  52. * \param [IN] timestamp Delay duration
  53. */
  54. static void rtimer_set_timeout( timer_event_t *obj );
  55. /*!
  56. * \brief Check if the Object to be added is not already in the list
  57. *
  58. * \param [IN] timestamp Delay duration
  59. * \retval true (the object is already in the list) or false
  60. */
  61. static bool rtimer_exists( timer_event_t *obj );
  62. void rtimer_init( timer_event_t *obj, void ( *callback )( void ) )
  63. {
  64. obj->Timestamp = 0;
  65. obj->ReloadValue = 0;
  66. obj->IsStarted = false;
  67. obj->IsNext2Expire = false;
  68. obj->Callback = callback;
  69. obj->Context = NULL;
  70. obj->Next = NULL;
  71. }
  72. static void rtimer_reload(void)
  73. {
  74. // Start the next timer_list_head if it exists AND NOT running
  75. if( ( timer_list_head != NULL ) && ( timer_list_head->IsNext2Expire == false ) )
  76. {
  77. rtimer_set_timeout( timer_list_head );
  78. }
  79. }
  80. void rtimer_start( timer_event_t *obj )
  81. {
  82. uint32_t elapsedTime = 0;
  83. timer_event_t **target = &timer_list_head;
  84. MULTI_RTIMER_CRITICAL_SECTION_BEGIN( );
  85. if( ( obj == NULL ) || ( rtimer_exists( obj ) == true ) )
  86. {
  87. MULTI_RTIMER_CRITICAL_SECTION_END( );
  88. return;
  89. }
  90. obj->Timestamp = obj->ReloadValue;
  91. obj->IsStarted = true;
  92. obj->IsNext2Expire = false;
  93. obj->Next = NULL;
  94. // The timer list is automatically sorted. The timer list head always contains the next timer to expire.
  95. if( timer_list_head == NULL )
  96. {
  97. rtc_set_timer_context( );
  98. }
  99. else
  100. {
  101. elapsedTime = rtc_get_timer_elapsed_time( );
  102. obj->Timestamp += elapsedTime;
  103. for(; *target; target = &((*target)->Next) )
  104. {
  105. if(((*target)->Timestamp > obj->Timestamp))
  106. {
  107. (*target)->IsNext2Expire = false;
  108. obj->Next = *target;
  109. break;
  110. }
  111. }
  112. }
  113. *target = obj;
  114. // Start the next timer_list_head if it exists AND NOT running
  115. rtimer_reload();
  116. MULTI_RTIMER_CRITICAL_SECTION_END( );
  117. }
  118. void rtimer_stop( timer_event_t *obj )
  119. {
  120. MULTI_RTIMER_CRITICAL_SECTION_BEGIN( );
  121. timer_event_t **target = &timer_list_head;
  122. // List is empty or the obj to stop does not exist
  123. if( ( timer_list_head == NULL ) || ( obj == NULL ) )
  124. {
  125. MULTI_RTIMER_CRITICAL_SECTION_END( );
  126. return;
  127. }
  128. obj->IsStarted = false;
  129. // Stop the Head and RTC Timeout
  130. if( timer_list_head == obj )
  131. {
  132. // The head is already running
  133. if( timer_list_head->IsNext2Expire == true )
  134. {
  135. timer_list_head->IsNext2Expire = false;
  136. if( timer_list_head->Next != NULL )
  137. {
  138. timer_list_head = timer_list_head->Next;
  139. rtimer_set_timeout( timer_list_head );
  140. }
  141. else
  142. {
  143. rtc_stop_alarm( );
  144. timer_list_head = NULL;
  145. }
  146. MULTI_RTIMER_CRITICAL_SECTION_END( );
  147. return;
  148. }
  149. }
  150. // Stop an object within the list
  151. for(; *target; target = &((*target)->Next) )
  152. {
  153. if( *target == obj )
  154. {
  155. *target = obj->Next;
  156. break;
  157. }
  158. }
  159. MULTI_RTIMER_CRITICAL_SECTION_END( );
  160. }
  161. static bool rtimer_exists( timer_event_t *obj )
  162. {
  163. timer_event_t* cur = timer_list_head;
  164. while( cur != NULL )
  165. {
  166. if( cur == obj )
  167. {
  168. return true;
  169. }
  170. cur = cur->Next;
  171. }
  172. return false;
  173. }
  174. void rtimer_reset( timer_event_t *obj )
  175. {
  176. rtimer_stop( obj );
  177. rtimer_start( obj );
  178. }
  179. void rtimer_set_value( timer_event_t *obj, uint32_t value )
  180. {
  181. uint32_t minValue = 0;
  182. uint32_t ticks = rtc_ms2tick( value );
  183. rtimer_stop( obj );
  184. minValue = rtc_get_minimum_timeout( );
  185. if( ticks < minValue )
  186. {
  187. ticks = minValue;
  188. }
  189. obj->Timestamp = ticks;
  190. obj->ReloadValue = ticks;
  191. }
  192. TimerTime_t rtimer_get_current_time( void )
  193. {
  194. uint32_t now = rtc_get_timer_value( );
  195. return rtc_tick2ms( now );
  196. }
  197. TimerTime_t rtimer_get_elapsed_time( TimerTime_t past )
  198. {
  199. if ( past == 0 )
  200. {
  201. return 0;
  202. }
  203. uint32_t nowInTicks = rtc_get_timer_value( );
  204. uint32_t pastInTicks = rtc_ms2tick( past );
  205. // Intentional wrap around. Works Ok if tick duration below 1ms
  206. return rtc_ms2tick( nowInTicks - pastInTicks );
  207. }
  208. static void rtimer_set_timeout( timer_event_t *obj )
  209. {
  210. int32_t minTicks= rtc_get_minimum_timeout( );
  211. obj->IsNext2Expire = true;
  212. // In case deadline too soon
  213. if( obj->Timestamp < ( rtc_get_timer_elapsed_time( ) + minTicks ) )
  214. {
  215. obj->Timestamp = rtc_get_timer_elapsed_time( ) + minTicks;
  216. }
  217. rtc_set_alarm( obj->Timestamp );
  218. }
  219. void rtimer_irq_handler( void )
  220. {
  221. timer_event_t** target = &timer_list_head;
  222. timer_event_t* cur;
  223. uint32_t old = rtc_get_timer_context( );
  224. uint32_t now = rtc_set_timer_context( );
  225. // intentional wrap around
  226. uint32_t deltaContext = now - old;
  227. // Update timeStamp based upon new Time Reference
  228. // because delta context should never exceed 2^32
  229. if( timer_list_head != NULL )
  230. {
  231. for(; (*target)->Next; target = &((*target)->Next) )
  232. {
  233. if( (*target)->Next->Timestamp > deltaContext )
  234. {
  235. (*target)->Next->Timestamp -= deltaContext;
  236. }
  237. else
  238. {
  239. (*target)->Next->Timestamp = 0;
  240. }
  241. }
  242. // Execute immediately the alarm callback
  243. cur = timer_list_head;
  244. timer_list_head = timer_list_head->Next;
  245. cur->IsStarted = false;
  246. EXECUTE_CALLBACK( cur->Callback );
  247. }
  248. // Remove all the expired object from the list
  249. while( ( timer_list_head != NULL ) && ( timer_list_head->Timestamp < rtc_get_timer_elapsed_time( ) ) )
  250. {
  251. cur = timer_list_head;
  252. timer_list_head = timer_list_head->Next;
  253. cur->IsStarted = false;
  254. EXECUTE_CALLBACK( cur->Callback );
  255. }
  256. // Start the next timer_list_head if it exists AND NOT running
  257. rtimer_reload();
  258. }
  259. #ifdef MULTI_RTIMER_USING_RTC_TEMPERTURE_COMPENSATION
  260. TimerTime_t rtimer_temp_compensation( TimerTime_t period, float temperature )
  261. {
  262. return rtc_temp_compensation( period, temperature );
  263. }
  264. #endif
  265. void rtimer_process( void )
  266. {
  267. rtc_process( );
  268. }