1 /*!
2 * \file timer.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 #include "utilities.h"
24 #include "board.h"
25 #include "rtc-board.h"
26 #include "timer.h"
27
28 /*!
29 * Safely execute call back
30 */
31 #define ExecuteCallBack( _callback_, context ) \
32 do \
33 { \
34 if( _callback_ == NULL ) \
35 { \
36 while( 1 ); \
37 } \
38 else \
39 { \
40 _callback_( context ); \
41 } \
42 }while( 0 );
43
44 /*!
45 * Timers list head pointer
46 */
47 static TimerEvent_t *TimerListHead = NULL;
48
49 /*!
50 * \brief Adds or replace the head timer of the list.
51 *
52 * \remark The list is automatically sorted. The list head always contains the
53 * next timer to expire.
54 *
55 * \param [IN] obj Timer object to be become the new head
56 * \param [IN] remainingTime Remaining time of the previous head to be replaced
57 */
58 static void TimerInsertNewHeadTimer( TimerEvent_t *obj );
59
60 /*!
61 * \brief Adds a timer to the list.
62 *
63 * \remark The list is automatically sorted. The list head always contains the
64 * next timer to expire.
65 *
66 * \param [IN] obj Timer object to be added to the list
67 * \param [IN] remainingTime Remaining time of the running head after which the object may be added
68 */
69 static void TimerInsertTimer( TimerEvent_t *obj );
70
71 /*!
72 * \brief Sets a timeout with the duration "timestamp"
73 *
74 * \param [IN] timestamp Delay duration
75 */
76 static void TimerSetTimeout( TimerEvent_t *obj );
77
78 /*!
79 * \brief Check if the Object to be added is not already in the list
80 *
81 * \param [IN] timestamp Delay duration
82 * \retval true (the object is already in the list) or false
83 */
84 static bool TimerExists( TimerEvent_t *obj );
85
TimerInit(TimerEvent_t * obj,void (* callback)(void * context))86 void TimerInit( TimerEvent_t *obj, void ( *callback )( void *context ) )
87 {
88 obj->Timestamp = 0;
89 obj->ReloadValue = 0;
90 obj->IsStarted = false;
91 obj->IsNext2Expire = false;
92 obj->Callback = callback;
93 obj->Context = NULL;
94 obj->Next = NULL;
95 }
96
TimerSetContext(TimerEvent_t * obj,void * context)97 void TimerSetContext( TimerEvent_t *obj, void* context )
98 {
99 obj->Context = context;
100 }
101
TimerStart(TimerEvent_t * obj)102 void TimerStart( TimerEvent_t *obj )
103 {
104 uint32_t elapsedTime = 0;
105
106 CRITICAL_SECTION_BEGIN( );
107
108 if( ( obj == NULL ) || ( TimerExists( obj ) == true ) )
109 {
110 CRITICAL_SECTION_END( );
111 return;
112 }
113
114 obj->Timestamp = obj->ReloadValue;
115 obj->IsStarted = true;
116 obj->IsNext2Expire = false;
117
118 if( TimerListHead == NULL )
119 {
120 RtcSetTimerContext( );
121 // Inserts a timer at time now + obj->Timestamp
122 TimerInsertNewHeadTimer( obj );
123 }
124 else
125 {
126 elapsedTime = RtcGetTimerElapsedTime( );
127 obj->Timestamp += elapsedTime;
128
129 if( obj->Timestamp < TimerListHead->Timestamp )
130 {
131 TimerInsertNewHeadTimer( obj );
132 }
133 else
134 {
135 TimerInsertTimer( obj );
136 }
137 }
138 CRITICAL_SECTION_END( );
139 }
140
TimerInsertTimer(TimerEvent_t * obj)141 static void TimerInsertTimer( TimerEvent_t *obj )
142 {
143 TimerEvent_t* cur = TimerListHead;
144 TimerEvent_t* next = TimerListHead->Next;
145
146 while( cur->Next != NULL )
147 {
148 if( obj->Timestamp > next->Timestamp )
149 {
150 cur = next;
151 next = next->Next;
152 }
153 else
154 {
155 cur->Next = obj;
156 obj->Next = next;
157 return;
158 }
159 }
160 cur->Next = obj;
161 obj->Next = NULL;
162 }
163
TimerInsertNewHeadTimer(TimerEvent_t * obj)164 static void TimerInsertNewHeadTimer( TimerEvent_t *obj )
165 {
166 TimerEvent_t* cur = TimerListHead;
167
168 if( cur != NULL )
169 {
170 cur->IsNext2Expire = false;
171 }
172
173 obj->Next = cur;
174 TimerListHead = obj;
175 TimerSetTimeout( TimerListHead );
176 }
177
TimerIsStarted(TimerEvent_t * obj)178 bool TimerIsStarted( TimerEvent_t *obj )
179 {
180 return obj->IsStarted;
181 }
182
TimerIrqHandler(void)183 void TimerIrqHandler( void )
184 {
185 TimerEvent_t* cur;
186 TimerEvent_t* next;
187
188 uint32_t old = RtcGetTimerContext( );
189 uint32_t now = RtcSetTimerContext( );
190 uint32_t deltaContext = now - old; // intentional wrap around
191
192 // Update timeStamp based upon new Time Reference
193 // because delta context should never exceed 2^32
194 if( TimerListHead != NULL )
195 {
196 for( cur = TimerListHead; cur->Next != NULL; cur = cur->Next )
197 {
198 next = cur->Next;
199 if( next->Timestamp > deltaContext )
200 {
201 next->Timestamp -= deltaContext;
202 }
203 else
204 {
205 next->Timestamp = 0;
206 }
207 }
208 }
209
210 // Execute immediately the alarm callback
211 if ( TimerListHead != NULL )
212 {
213 cur = TimerListHead;
214 TimerListHead = TimerListHead->Next;
215 cur->IsStarted = false;
216 ExecuteCallBack( cur->Callback, cur->Context );
217 }
218
219 // Remove all the expired object from the list
220 while( ( TimerListHead != NULL ) && ( TimerListHead->Timestamp < RtcGetTimerElapsedTime( ) ) )
221 {
222 cur = TimerListHead;
223 TimerListHead = TimerListHead->Next;
224 cur->IsStarted = false;
225 ExecuteCallBack( cur->Callback, cur->Context );
226 }
227
228 // Start the next TimerListHead if it exists AND NOT running
229 if( ( TimerListHead != NULL ) && ( TimerListHead->IsNext2Expire == false ) )
230 {
231 TimerSetTimeout( TimerListHead );
232 }
233 }
234
TimerStop(TimerEvent_t * obj)235 void TimerStop( TimerEvent_t *obj )
236 {
237 CRITICAL_SECTION_BEGIN( );
238
239 TimerEvent_t* prev = TimerListHead;
240 TimerEvent_t* cur = TimerListHead;
241
242 // List is empty or the obj to stop does not exist
243 if( ( TimerListHead == NULL ) || ( obj == NULL ) )
244 {
245 CRITICAL_SECTION_END( );
246 return;
247 }
248
249 obj->IsStarted = false;
250
251 if( TimerListHead == obj ) // Stop the Head
252 {
253 if( TimerListHead->IsNext2Expire == true ) // The head is already running
254 {
255 TimerListHead->IsNext2Expire = false;
256 if( TimerListHead->Next != NULL )
257 {
258 TimerListHead = TimerListHead->Next;
259 TimerSetTimeout( TimerListHead );
260 }
261 else
262 {
263 RtcStopAlarm( );
264 TimerListHead = NULL;
265 }
266 }
267 else // Stop the head before it is started
268 {
269 if( TimerListHead->Next != NULL )
270 {
271 TimerListHead = TimerListHead->Next;
272 }
273 else
274 {
275 TimerListHead = NULL;
276 }
277 }
278 }
279 else // Stop an object within the list
280 {
281 while( cur != NULL )
282 {
283 if( cur == obj )
284 {
285 if( cur->Next != NULL )
286 {
287 cur = cur->Next;
288 prev->Next = cur;
289 }
290 else
291 {
292 cur = NULL;
293 prev->Next = cur;
294 }
295 break;
296 }
297 else
298 {
299 prev = cur;
300 cur = cur->Next;
301 }
302 }
303 }
304 CRITICAL_SECTION_END( );
305 }
306
TimerExists(TimerEvent_t * obj)307 static bool TimerExists( TimerEvent_t *obj )
308 {
309 TimerEvent_t* cur = TimerListHead;
310
311 while( cur != NULL )
312 {
313 if( cur == obj )
314 {
315 return true;
316 }
317 cur = cur->Next;
318 }
319 return false;
320 }
321
TimerReset(TimerEvent_t * obj)322 void TimerReset( TimerEvent_t *obj )
323 {
324 TimerStop( obj );
325 TimerStart( obj );
326 }
327
TimerSetValue(TimerEvent_t * obj,uint32_t value)328 void TimerSetValue( TimerEvent_t *obj, uint32_t value )
329 {
330 uint32_t minValue = 0;
331 uint32_t ticks = RtcMs2Tick( value );
332
333 TimerStop( obj );
334
335 minValue = RtcGetMinimumTimeout( );
336
337 if( ticks < minValue )
338 {
339 ticks = minValue;
340 }
341
342 obj->Timestamp = ticks;
343 obj->ReloadValue = ticks;
344 }
345
TimerGetCurrentTime(void)346 TimerTime_t TimerGetCurrentTime( void )
347 {
348 uint32_t now = RtcGetTimerValue( );
349 return RtcTick2Ms( now );
350 }
351
TimerGetElapsedTime(TimerTime_t past)352 TimerTime_t TimerGetElapsedTime( TimerTime_t past )
353 {
354 if ( past == 0 )
355 {
356 return 0;
357 }
358 uint32_t nowInTicks = RtcGetTimerValue( );
359 uint32_t pastInTicks = RtcMs2Tick( past );
360
361 // Intentional wrap around. Works Ok if tick duration below 1ms
362 return RtcTick2Ms( nowInTicks - pastInTicks );
363 }
364
TimerSetTimeout(TimerEvent_t * obj)365 static void TimerSetTimeout( TimerEvent_t *obj )
366 {
367 int32_t minTicks= RtcGetMinimumTimeout( );
368 obj->IsNext2Expire = true;
369
370 // In case deadline too soon
371 if( obj->Timestamp < ( RtcGetTimerElapsedTime( ) + minTicks ) )
372 {
373 obj->Timestamp = RtcGetTimerElapsedTime( ) + minTicks;
374 }
375 RtcSetAlarm( obj->Timestamp );
376 }
377
TimerTempCompensation(TimerTime_t period,float temperature)378 TimerTime_t TimerTempCompensation( TimerTime_t period, float temperature )
379 {
380 return RtcTempCompensation( period, temperature );
381 }
382
TimerProcess(void)383 void TimerProcess( void )
384 {
385 RtcProcess( );
386 }
387