ChibiOS/RT  5.1.0
chvt.c
Go to the documentation of this file.
1 /*
2  ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio.
3 
4  This file is part of ChibiOS.
5 
6  ChibiOS is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 3 of the License, or
9  (at your option) any later version.
10 
11  ChibiOS is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 /**
21  * @file chvt.c
22  * @brief Time and Virtual Timers module code.
23  *
24  * @addtogroup time
25  * @details Time and Virtual Timers related APIs and services.
26  * @{
27  */
28 
29 #include "ch.h"
30 
31 /*===========================================================================*/
32 /* Module local definitions. */
33 /*===========================================================================*/
34 
35 /*===========================================================================*/
36 /* Module exported variables. */
37 /*===========================================================================*/
38 
39 /*===========================================================================*/
40 /* Module local types. */
41 /*===========================================================================*/
42 
43 /*===========================================================================*/
44 /* Module local variables. */
45 /*===========================================================================*/
46 
47 /*===========================================================================*/
48 /* Module local functions. */
49 /*===========================================================================*/
50 
51 /*===========================================================================*/
52 /* Module exported functions. */
53 /*===========================================================================*/
54 
55 /**
56  * @brief Virtual Timers initialization.
57  * @note Internal use only.
58  *
59  * @notapi
60  */
61 void _vt_init(void) {
62 
66 #if CH_CFG_ST_TIMEDELTA == 0
68 #else /* CH_CFG_ST_TIMEDELTA > 0 */
70 #endif /* CH_CFG_ST_TIMEDELTA > 0 */
71 }
72 
73 /**
74  * @brief Enables a virtual timer.
75  * @details The timer is enabled and programmed to trigger after the delay
76  * specified as parameter.
77  * @pre The timer must not be already armed before calling this function.
78  * @note The callback function is invoked from interrupt context.
79  *
80  * @param[out] vtp the @p virtual_timer_t structure pointer
81  * @param[in] delay the number of ticks before the operation timeouts, the
82  * special values are handled as follow:
83  * - @a TIME_INFINITE is allowed but interpreted as a
84  * normal time specification.
85  * - @a TIME_IMMEDIATE this value is not allowed.
86  * .
87  * @param[in] vtfunc the timer callback function. After invoking the
88  * callback the timer is disabled and the structure can
89  * be disposed or reused.
90  * @param[in] par a parameter that will be passed to the callback
91  * function
92  *
93  * @iclass
94  */
96  vtfunc_t vtfunc, void *par) {
97  virtual_timer_t *p;
98  sysinterval_t delta;
99 
101  chDbgCheck((vtp != NULL) && (vtfunc != NULL) && (delay != TIME_IMMEDIATE));
102 
103  vtp->par = par;
104  vtp->func = vtfunc;
105 
106 #if CH_CFG_ST_TIMEDELTA > 0
107  {
109 
110  /* If the requested delay is lower than the minimum safe delta then it
111  is raised to the minimum safe value.*/
112  if (delay < (sysinterval_t)CH_CFG_ST_TIMEDELTA) {
113  delay = (sysinterval_t)CH_CFG_ST_TIMEDELTA;
114  }
115 
116  /* Special case where the timers list is empty.*/
118 
119  /* The delta list is empty, the current time becomes the new
120  delta list base time, the timer is inserted.*/
121  ch.vtlist.lasttime = now;
122  ch.vtlist.next = vtp;
123  ch.vtlist.prev = vtp;
124  vtp->next = (virtual_timer_t *)&ch.vtlist;
125  vtp->prev = (virtual_timer_t *)&ch.vtlist;
126  vtp->delta = delay;
127 
128 #if CH_CFG_INTERVALS_SIZE > CH_CFG_ST_RESOLUTION
129  /* The delta could be too large for the physical timer to handle.*/
130  if (delay > (sysinterval_t)TIME_MAX_SYSTIME) {
132  }
133 #endif
134 
135  /* Being the first element in the list the alarm timer is started.*/
136  port_timer_start_alarm(chTimeAddX(ch.vtlist.lasttime, delay));
137 
138  return;
139  }
140 
141  /* Pointer to the first element in the delta list, which is non-empty.*/
142  p = ch.vtlist.next;
143 
144  /* Delay as delta from 'lasttime'. Note, it can overflow and the value
145  becomes lower than 'now'.*/
146  delta = chTimeDiffX(ch.vtlist.lasttime, now) + delay;
147 
148  if (delta < chTimeDiffX(ch.vtlist.lasttime, now)) {
149  /* Scenario where a very large delay excedeed the numeric range, it
150  requires a special handling. We need to skip the first element and
151  adjust the delta to wrap back in the previous numeric range.*/
152  delta -= p->delta;
153  p = p->next;
154  }
155  else if (delta < p->delta) {
156  sysinterval_t deadline_delta;
157 
158  /* A small delay that will become the first element in the delta list
159  and next deadline.*/
160  deadline_delta = delta;
161 #if CH_CFG_INTERVALS_SIZE > CH_CFG_ST_RESOLUTION
162  /* The delta could be too large for the physical timer to handle.*/
163  if (deadline_delta > (sysinterval_t)TIME_MAX_SYSTIME) {
164  deadline_delta = (sysinterval_t)TIME_MAX_SYSTIME;
165  }
166 #endif
167  port_timer_set_alarm(chTimeAddX(ch.vtlist.lasttime, deadline_delta));
168  }
169  }
170 #else /* CH_CFG_ST_TIMEDELTA == 0 */
171  /* Delta is initially equal to the specified delay.*/
172  delta = delay;
173 
174  /* Pointer to the first element in the delta list.*/
175  p = ch.vtlist.next;
176 #endif /* CH_CFG_ST_TIMEDELTA == 0 */
177 
178  /* The delta list is scanned in order to find the correct position for
179  this timer. */
180  while (p->delta < delta) {
181  /* Debug assert if the timer is already in the list.*/
182  chDbgAssert(p != vtp, "timer already armed");
183 
184  delta -= p->delta;
185  p = p->next;
186  }
187 
188  /* The timer is inserted in the delta list.*/
189  vtp->next = p;
190  vtp->prev = vtp->next->prev;
191  vtp->prev->next = vtp;
192  p->prev = vtp;
193  vtp->delta = delta;
194 
195  /* Calculate new delta for the following entry.*/
196  p->delta -= delta;
197 
198  /* Special case when the timer is in last position in the list, the
199  value in the header must be restored.*/
201 }
202 
203 /**
204  * @brief Disables a Virtual Timer.
205  * @pre The timer must be in armed state before calling this function.
206  *
207  * @param[in] vtp the @p virtual_timer_t structure pointer
208  *
209  * @iclass
210  */
212 
214  chDbgCheck(vtp != NULL);
215  chDbgAssert(vtp->func != NULL, "timer not set or already triggered");
216 
217 #if CH_CFG_ST_TIMEDELTA == 0
218 
219  /* The delta of the timer is added to the next timer.*/
220  vtp->next->delta += vtp->delta;
221 
222  /* Removing the element from the delta list.*/
223  vtp->prev->next = vtp->next;
224  vtp->next->prev = vtp->prev;
225  vtp->func = NULL;
226 
227  /* The above code changes the value in the header when the removed element
228  is the last of the list, restoring it.*/
230 #else /* CH_CFG_ST_TIMEDELTA > 0 */
231  sysinterval_t nowdelta, delta;
232 
233  /* If the timer is not the first of the list then it is simply unlinked
234  else the operation is more complex.*/
235  if (ch.vtlist.next != vtp) {
236  /* Removing the element from the delta list.*/
237  vtp->prev->next = vtp->next;
238  vtp->next->prev = vtp->prev;
239  vtp->func = NULL;
240 
241  /* Adding delta to the next element, if it is not the last one.*/
242  if (&ch.vtlist != (virtual_timers_list_t *)vtp->next)
243  vtp->next->delta += vtp->delta;
244 
245  return;
246  }
247 
248  /* Removing the first timer from the list.*/
249  ch.vtlist.next = vtp->next;
251  vtp->func = NULL;
252 
253  /* If the list become empty then the alarm timer is stopped and done.*/
255  port_timer_stop_alarm();
256 
257  return;
258  }
259 
260  /* The delta of the removed timer is added to the new first timer.*/
261  ch.vtlist.next->delta += vtp->delta;
262 
263  /* If the new first timer has a delta of zero then the alarm is not
264  modified, the already programmed alarm will serve it.*/
265 /* if (ch.vtlist.next->delta == 0) {
266  return;
267  }*/
268 
269  /* Distance in ticks between the last alarm event and current time.*/
271 
272  /* If the current time surpassed the time of the next element in list
273  then the event interrupt is already pending, just return.*/
274  if (nowdelta >= ch.vtlist.next->delta) {
275  return;
276  }
277 
278  /* Distance from the next scheduled event and now.*/
279  delta = ch.vtlist.next->delta - nowdelta;
280 
281  /* Making sure to not schedule an event closer than CH_CFG_ST_TIMEDELTA
282  ticks from now.*/
283  if (delta < (sysinterval_t)CH_CFG_ST_TIMEDELTA) {
284  delta = nowdelta + (sysinterval_t)CH_CFG_ST_TIMEDELTA;
285  }
286  else {
287  delta = nowdelta + delta;
288 #if CH_CFG_INTERVALS_SIZE > CH_CFG_ST_RESOLUTION
289  /* The delta could be too large for the physical timer to handle.*/
290  if (delta > (sysinterval_t)TIME_MAX_SYSTIME) {
292  }
293 #endif
294  }
295  port_timer_set_alarm(chTimeAddX(ch.vtlist.lasttime, delta));
296 #endif /* CH_CFG_ST_TIMEDELTA > 0 */
297 }
298 
299 /** @} */
static systime_t chVTGetSystemTimeX(void)
Current system time.
Definition: chvt.h:115
void * par
Timer callback function parameter.
Definition: chschd.h:330
uint64_t systime_t
Type of system time.
Definition: chtime.h:138
uint64_t sysinterval_t
Type of time interval.
Definition: chtime.h:150
sysinterval_t delta
Time delta before timeout.
Definition: chschd.h:327
virtual_timer_t * prev
Previous timer in the list.
Definition: chschd.h:326
static sysinterval_t chTimeDiffX(systime_t start, systime_t end)
Subtracts two system times returning an interval.
Definition: chtime.h:491
#define TIME_MAX_SYSTIME
Maximum system of system time before it wraps.
Definition: chtime.h:63
systime_t lasttime
System time of the last tick event.
Definition: chschd.h:353
virtual_timers_list_t vtlist
Virtual timers delta list header.
Definition: chschd.h:419
sysinterval_t delta
Must be initialized to -1.
Definition: chschd.h:345
void chDbgCheckClassI(void)
I-class functions context check.
Definition: chdebug.c:235
void chVTDoSetI(virtual_timer_t *vtp, sysinterval_t delay, vtfunc_t vtfunc, void *par)
Enables a virtual timer.
Definition: chvt.c:95
void(* vtfunc_t)(void *p)
Type of a Virtual Timer callback function.
Definition: chsystypes.h:81
void chVTDoResetI(virtual_timer_t *vtp)
Disables a Virtual Timer.
Definition: chvt.c:211
Virtual timers list header.
Definition: chschd.h:340
#define chDbgCheck(c)
Function parameters check.
Definition: chdebug.h:101
void _vt_init(void)
Virtual Timers initialization.
Definition: chvt.c:61
volatile systime_t systime
System Time counter.
Definition: chschd.h:347
ch_system_t ch
System data structures.
Definition: chschd.c:42
#define chDbgAssert(c, r)
Condition assertion.
Definition: chdebug.h:127
Virtual Timer descriptor structure.
Definition: chschd.h:324
virtual_timer_t * next
Next timer in the delta list.
Definition: chschd.h:341
#define TIME_IMMEDIATE
Zero interval specification for some functions with a timeout specification.
Definition: chtime.h:45
#define CH_CFG_ST_TIMEDELTA
Time delta constant for the tick-less mode.
Definition: chconf.h:83
virtual_timer_t * next
Next timer in the list.
Definition: chschd.h:325
static systime_t chTimeAddX(systime_t systime, sysinterval_t interval)
Adds an interval to a system time returning a system time.
Definition: chtime.h:472
virtual_timer_t * prev
Last timer in the delta list.
Definition: chschd.h:343
ChibiOS/RT main include file.
vtfunc_t func
Timer callback function pointer.
Definition: chschd.h:328