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