LPCOpen Platform  v1.03
LPCOpen Platform for NXP LPC Microcontrollers
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
os_tick.c
Go to the documentation of this file.
1 /*
2 ***********************************************************************************************************************
3 * uC/OS-III
4 * The Real-Time Kernel
5 *
6 * (c) Copyright 2009-2012; Micrium, Inc.; Weston, FL
7 * All rights reserved. Protected by international copyright laws.
8 *
9 * TICK MANAGEMENT
10 *
11 * File : OS_TICK.C
12 * By : JJL
13 * Version : V3.03.00
14 *
15 * LICENSING TERMS:
16 * ---------------
17 * uC/OS-III is provided in source form for FREE short-term evaluation, for educational use or
18 * for peaceful research. If you plan or intend to use uC/OS-III in a commercial application/
19 * product then, you need to contact Micrium to properly license uC/OS-III for its use in your
20 * application/product. We provide ALL the source code for your convenience and to help you
21 * experience uC/OS-III. The fact that the source is provided does NOT mean that you can use
22 * it commercially without paying a licensing fee.
23 *
24 * Knowledge of the source code may NOT be used to develop a similar product.
25 *
26 * Please help us continue to provide the embedded community with the finest software available.
27 * Your honesty is greatly appreciated.
28 *
29 * You can contact us at www.micrium.com, or by phone at +1 (954) 217-2036.
30 ************************************************************************************************************************
31 */
32 
33 #define MICRIUM_SOURCE
34 #include <os.h>
35 
36 #ifdef VSC_INCLUDE_SOURCE_FILE_NAMES
37 const CPU_CHAR *os_tick__c = "$Id: $";
38 #endif
39 
40 /*
41 ************************************************************************************************************************
42 * LOCAL PROTOTYPES
43 ************************************************************************************************************************
44 */
45 
46 
47 /*
48 ************************************************************************************************************************
49 * TICK TASK
50 *
51 * Description: This task is internal to uC/OS-III and is triggered by the tick interrupt.
52 *
53 * Arguments : p_arg is an argument passed to the task when the task is created (unused).
54 *
55 * Returns : none
56 *
57 * Note(s) : This function is INTERNAL to uC/OS-III and your application should not call it.
58 ************************************************************************************************************************
59 */
60 
61 void OS_TickTask (void *p_arg)
62 {
63  OS_ERR err;
64  CPU_TS ts;
65 
66 
67  p_arg = p_arg; /* Prevent compiler warning */
68 
69  while (DEF_ON) {
70  (void)OSTaskSemPend((OS_TICK )0,
72  (CPU_TS *)&ts,
73  (OS_ERR *)&err); /* Wait for signal from tick interrupt */
74  if (err == OS_ERR_NONE) {
76  OS_TickListUpdate(); /* Update all tasks waiting for time */
77  }
78  }
79  }
80 }
81 
82 /*$PAGE*/
83 /*
84 ************************************************************************************************************************
85 * INITIALIZE TICK TASK
86 *
87 * Description: This function is called by OSInit() to create the tick task.
88 *
89 * Arguments : p_err is a pointer to a variable that will hold the value of an error code:
90 *
91 * OS_ERR_TICK_STK_INVALID if the pointer to the tick task stack is a NULL pointer
92 * OS_ERR_TICK_STK_SIZE indicates that the specified stack size
93 * OS_ERR_PRIO_INVALID if the priority you specified in the configuration is invalid
94 * (There could be only one task at the Idle Task priority)
95 * (Maybe the priority you specified is higher than OS_CFG_PRIO_MAX-1
96 * OS_ERR_?? other error code returned by OSTaskCreate()
97 *
98 * Returns : none
99 *
100 * Note(s) : This function is INTERNAL to uC/OS-III and your application should not call it.
101 ************************************************************************************************************************
102 */
103 
104 void OS_TickTaskInit (OS_ERR *p_err)
105 {
106 #ifdef OS_SAFETY_CRITICAL
107  if (p_err == (OS_ERR *)0) {
108  OS_SAFETY_CRITICAL_EXCEPTION();
109  return;
110  }
111 #endif
112 
113  OSTickCtr = (OS_TICK)0u; /* Clear the tick counter */
114 
116 
117 
118  OS_TickListInit(); /* Initialize the tick list data structures */
119 
120  /* ---------------- CREATE THE TICK TASK ---------------- */
121  if (OSCfg_TickTaskStkBasePtr == (CPU_STK *)0) {
122  *p_err = OS_ERR_TICK_STK_INVALID;
123  return;
124  }
125 
128  return;
129  }
130 
131  if (OSCfg_TickTaskPrio >= (OS_CFG_PRIO_MAX - 1u)) { /* Only one task at the 'Idle Task' priority */
132  *p_err = OS_ERR_TICK_PRIO_INVALID;
133  return;
134  }
135 
136  OSTaskCreate((OS_TCB *)&OSTickTaskTCB,
137  (CPU_CHAR *)((void *)"uC/OS-III Tick Task"),
139  (void *)0,
144  (OS_MSG_QTY )0u,
145  (OS_TICK )0u,
146  (void *)0,
148  (OS_ERR *)p_err);
149 }
150 
151 /*$PAGE*/
152 /*
153 ************************************************************************************************************************
154 * INITIALIZE THE TICK LIST
155 *
156 * Description: This function initializes the tick handling data structures of uC/OS-III.
157 *
158 * Arguments : none
159 *
160 * Returns : None
161 *
162 * Note(s) : This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
163 ************************************************************************************************************************
164 */
165 
166 void OS_TickListInit (void)
167 {
169  OS_TICK_SPOKE *p_spoke;
170 
171 
172 
173  for (i = 0u; i < OSCfg_TickWheelSize; i++) {
174  p_spoke = (OS_TICK_SPOKE *)&OSCfg_TickWheel[i];
175  p_spoke->FirstPtr = (OS_TCB *)0;
176  p_spoke->NbrEntries = (OS_OBJ_QTY )0u;
177  p_spoke->NbrEntriesMax = (OS_OBJ_QTY )0u;
178  }
179 }
180 
181 /*$PAGE*/
182 /*
183 ************************************************************************************************************************
184 * ADD TASK TO TICK LIST
185 *
186 * Description: This function is called to place a task in a list of task waiting for either time to expire or waiting to
187 * timeout on a pend call.
188 *
189 * Arguments : p_tcb is a pointer to the OS_TCB of the task to add to the tick list
190 * -----
191 *
192 * time represents either the 'match' value of OSTickCtr or a relative time from the current
193 * value of OSTickCtr as specified by the 'opt' argument..
194 *
195 * relative when 'opt' is set to OS_OPT_TIME_DLY
196 * relative when 'opt' is set to OS_OPT_TIME_TIMEOUT
197 * match when 'opt' is set to OS_OPT_TIME_MATCH
198 * periodic when 'opt' is set to OS_OPT_TIME_PERIODIC
199 *
200 * opt is an option specifying how to calculate time. The valid values are:
201 * ---
202 * OS_OPT_TIME_DLY
203 * OS_OPT_TIME_TIMEOUT
204 * OS_OPT_TIME_PERIODIC
205 * OS_OPT_TIME_MATCH
206 *
207 * p_err is a pointer to a variable that will contain an error code returned by this function.
208 * -----
209 * OS_ERR_NONE the call was successful and the time delay was scheduled.
210 * OS_ERR_TIME_ZERO_DLY if delay is zero or already occurred.
211 *
212 * Returns : None
213 *
214 * Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
215 *
216 * 2) This function is assumed to be called with interrupts disabled.
217 ************************************************************************************************************************
218 */
219 
220 void OS_TickListInsert (OS_TCB *p_tcb,
221  OS_TICK time,
222  OS_OPT opt,
223  OS_ERR *p_err)
224 {
225  OS_TICK tick_delta;
226  OS_TICK tick_next;
227  OS_TICK_SPOKE *p_spoke;
228  OS_TCB *p_tcb0;
229  OS_TCB *p_tcb1;
230  OS_TICK_SPOKE_IX spoke;
231 
232 
233 
234  if (opt == OS_OPT_TIME_MATCH) { /* Task time is absolute. */
235  tick_delta = time - OSTickCtr - 1u;
236  if (tick_delta > OS_TICK_TH_RDY) { /* If delay already occurred, ... */
237  p_tcb->TickCtrMatch = (OS_TICK )0u;
238  p_tcb->TickRemain = (OS_TICK )0u;
239  p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;
240  *p_err = OS_ERR_TIME_ZERO_DLY; /* ... do NOT delay. */
241  return;
242  }
243  p_tcb->TickCtrMatch = time;
244  p_tcb->TickRemain = tick_delta + 1u;
245 
246  } else if (time > (OS_TICK)0u) {
247  if (opt == OS_OPT_TIME_PERIODIC) { /* Task time is periodic. */
248  tick_next = p_tcb->TickCtrPrev + time;
249  tick_delta = tick_next - OSTickCtr - 1u;
250  if (tick_delta < time) { /* If next periodic delay did NOT already occur, ... */
251  p_tcb->TickCtrMatch = tick_next; /* ... set next periodic delay; ... */
252  } else {
253  p_tcb->TickCtrMatch = OSTickCtr + time; /* ... else reset periodic delay. */
254  }
255  p_tcb->TickRemain = p_tcb->TickCtrMatch - OSTickCtr;
256  p_tcb->TickCtrPrev = p_tcb->TickCtrMatch;
257 
258  } else { /* Task time is relative to current. */
259  p_tcb->TickCtrMatch = OSTickCtr + time;
260  p_tcb->TickRemain = time;
261  }
262 
263  } else { /* Zero time delay; ... */
264  p_tcb->TickCtrMatch = (OS_TICK )0u;
265  p_tcb->TickRemain = (OS_TICK )0u;
266  p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;
267  *p_err = OS_ERR_TIME_ZERO_DLY; /* ... do NOT delay. */
268  return;
269  }
270 
271 
272  spoke = (OS_TICK_SPOKE_IX)(p_tcb->TickCtrMatch % OSCfg_TickWheelSize);
273  p_spoke = &OSCfg_TickWheel[spoke];
274 
275  if (p_spoke->NbrEntries == (OS_OBJ_QTY)0u) { /* First entry in the spoke */
276  p_tcb->TickNextPtr = (OS_TCB *)0;
277  p_tcb->TickPrevPtr = (OS_TCB *)0;
278  p_spoke->FirstPtr = p_tcb;
279  p_spoke->NbrEntries = (OS_OBJ_QTY)1u;
280  } else {
281  p_tcb1 = p_spoke->FirstPtr; /* Point to current first TCB in the list */
282  while (p_tcb1 != (OS_TCB *)0) {
283  p_tcb1->TickRemain = p_tcb1->TickCtrMatch /* Compute time remaining of current TCB in list */
284  - OSTickCtr;
285  if (p_tcb->TickRemain > p_tcb1->TickRemain) { /* Do we need to insert AFTER current TCB in list? */
286  if (p_tcb1->TickNextPtr != (OS_TCB *)0) { /* Yes, are we pointing at the last TCB in the list? */
287  p_tcb1 = p_tcb1->TickNextPtr; /* No, Point to next TCB in the list */
288  } else {
289  p_tcb->TickNextPtr = (OS_TCB *)0;
290  p_tcb->TickPrevPtr = p_tcb1;
291  p_tcb1->TickNextPtr = p_tcb; /* Yes, TCB to add is now new last entry in the list */
292  p_tcb1 = (OS_TCB *)0; /* Break loop */
293  }
294  } else { /* Insert before the current TCB */
295  if (p_tcb1->TickPrevPtr == (OS_TCB *)0) { /* Are we inserting before the first TCB? */
296  p_tcb->TickPrevPtr = (OS_TCB *)0;
297  p_tcb->TickNextPtr = p_tcb1;
298  p_tcb1->TickPrevPtr = p_tcb;
299  p_spoke->FirstPtr = p_tcb;
300  } else { /* Insert in between 2 TCBs already in the list */
301  p_tcb0 = p_tcb1->TickPrevPtr;
302  p_tcb->TickPrevPtr = p_tcb0;
303  p_tcb->TickNextPtr = p_tcb1;
304  p_tcb0->TickNextPtr = p_tcb;
305  p_tcb1->TickPrevPtr = p_tcb;
306  }
307  p_tcb1 = (OS_TCB *)0; /* Break loop */
308  }
309  }
310  p_spoke->NbrEntries++;
311  }
312  if (p_spoke->NbrEntriesMax < p_spoke->NbrEntries) { /* Keep track of maximum # of entries in each spoke */
313  p_spoke->NbrEntriesMax = p_spoke->NbrEntries;
314  }
315  p_tcb->TickSpokePtr = p_spoke; /* Link back to tick spoke */
316  *p_err = OS_ERR_NONE;
317 }
318 
319 /*$PAGE*/
320 /*
321 ************************************************************************************************************************
322 * REMOVE A TASK FROM THE TICK LIST
323 *
324 * Description: This function is called to remove a task from the tick list
325 *
326 * Arguments : p_tcb Is a pointer to the OS_TCB to remove.
327 * -----
328 *
329 * Returns : none
330 *
331 * Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
332 *
333 * 2) This function is assumed to be called with interrupts disabled.
334 ************************************************************************************************************************
335 */
336 
337 void OS_TickListRemove (OS_TCB *p_tcb)
338 {
339  OS_TICK_SPOKE *p_spoke;
340  OS_TCB *p_tcb1;
341  OS_TCB *p_tcb2;
342 
343 
344 
345  p_spoke = p_tcb->TickSpokePtr;
346  if (p_spoke != (OS_TICK_SPOKE *)0) { /* Confirm that task is in tick list */
347  p_tcb->TickRemain = (OS_TICK)0u;
348  if (p_spoke->FirstPtr == p_tcb) { /* Is timer to remove at the beginning of list? */
349  p_tcb1 = (OS_TCB *)p_tcb->TickNextPtr; /* Yes */
350  p_spoke->FirstPtr = p_tcb1;
351  if (p_tcb1 != (OS_TCB *)0) {
352  p_tcb1->TickPrevPtr = (OS_TCB *)0;
353  }
354  } else {
355  p_tcb1 = p_tcb->TickPrevPtr; /* No, remove timer from somewhere in the list */
356  p_tcb2 = p_tcb->TickNextPtr;
357  p_tcb1->TickNextPtr = p_tcb2;
358  if (p_tcb2 != (OS_TCB *)0) {
359  p_tcb2->TickPrevPtr = p_tcb1;
360  }
361  }
362  p_tcb->TickNextPtr = (OS_TCB *)0;
363  p_tcb->TickPrevPtr = (OS_TCB *)0;
364  p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;
365  p_tcb->TickCtrMatch = (OS_TICK )0u;
366  p_spoke->NbrEntries--;
367  }
368 }
369 
370 /*$PAGE*/
371 /*
372 ************************************************************************************************************************
373 * RESET TICK LIST PEAK DETECTOR
374 *
375 * Description: This function is used to reset the peak detector for the number of entries in each spoke.
376 *
377 * Arguments : void
378 *
379 * Returns : none
380 *
381 * Note(s) : This function is INTERNAL to uC/OS-III and your application should not call it.
382 ************************************************************************************************************************
383 */
384 
385 
387 {
389  OS_TICK_SPOKE *p_spoke;
390 
391 
392 
393  for (i = 0u; i < OSCfg_TickWheelSize; i++) {
394  p_spoke = (OS_TICK_SPOKE *)&OSCfg_TickWheel[i];
395  p_spoke->NbrEntriesMax = (OS_OBJ_QTY )0u;
396  }
397 }
398 
399 /*$PAGE*/
400 /*
401 ************************************************************************************************************************
402 * UPDATE THE TICK LIST
403 *
404 * Description: This function is called when a tick occurs and determines if the timeout waiting for a kernel object has
405 * expired or a delay has expired.
406 *
407 * Arguments : non
408 *
409 * Returns : none
410 *
411 * Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
412 ************************************************************************************************************************
413 */
414 
415 void OS_TickListUpdate (void)
416 {
417  CPU_BOOLEAN done;
418  OS_TICK_SPOKE *p_spoke;
419  OS_TCB *p_tcb;
420  OS_TCB *p_tcb_next;
421  OS_TICK_SPOKE_IX spoke;
422  CPU_TS ts_start;
423  CPU_TS ts_end;
424  CPU_SR_ALLOC();
425 
426 
428  ts_start = OS_TS_GET();
429  OSTickCtr++; /* Keep track of the number of ticks */
431  p_spoke = &OSCfg_TickWheel[spoke];
432  p_tcb = p_spoke->FirstPtr;
433  done = DEF_FALSE;
434  while (done == DEF_FALSE) {
435  if (p_tcb != (OS_TCB *)0) {
436  p_tcb_next = p_tcb->TickNextPtr; /* Point to next TCB to update */
437  switch (p_tcb->TaskState) {
438  case OS_TASK_STATE_RDY:
439  case OS_TASK_STATE_PEND:
442  break;
443 
444  case OS_TASK_STATE_DLY:
445  p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
446  - OSTickCtr;
447  if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
448  p_tcb->TaskState = OS_TASK_STATE_RDY;
449  OS_TaskRdy(p_tcb); /* Make task ready to run */
450  } else {
451  done = DEF_TRUE; /* Don't find a match, we're done! */
452  }
453  break;
454 
456  p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
457  - OSTickCtr;
458  if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
459 #if (OS_MSG_EN > 0u)
460  p_tcb->MsgPtr = (void *)0;
461  p_tcb->MsgSize = (OS_MSG_SIZE)0u;
462 #endif
463  p_tcb->TS = OS_TS_GET();
464  OS_PendListRemove(p_tcb); /* Remove from wait list */
465  OS_TaskRdy(p_tcb);
466  p_tcb->TaskState = OS_TASK_STATE_RDY;
467  p_tcb->PendStatus = OS_STATUS_PEND_TIMEOUT; /* Indicate pend timed out */
468  p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */
469  } else {
470  done = DEF_TRUE; /* Don't find a match, we're done! */
471  }
472  break;
473 
475  p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
476  - OSTickCtr;
477  if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
478  p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
479  OS_TickListRemove(p_tcb); /* Remove from current wheel spoke */
480  } else {
481  done = DEF_TRUE; /* Don't find a match, we're done! */
482  }
483  break;
484 
486  p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
487  - OSTickCtr;
488  if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
489 #if (OS_MSG_EN > 0u)
490  p_tcb->MsgPtr = (void *)0;
491  p_tcb->MsgSize = (OS_MSG_SIZE)0u;
492 #endif
493  p_tcb->TS = OS_TS_GET();
494  OS_PendListRemove(p_tcb); /* Remove from wait list */
495  OS_TickListRemove(p_tcb); /* Remove from current wheel spoke */
496  p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
497  p_tcb->PendStatus = OS_STATUS_PEND_TIMEOUT; /* Indicate pend timed out */
498  p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */
499  } else {
500  done = DEF_TRUE; /* Don't find a match, we're done! */
501  }
502  break;
503 
504  default:
505  break;
506  }
507  p_tcb = p_tcb_next;
508  } else {
509  done = DEF_TRUE;
510  }
511  }
512  ts_end = OS_TS_GET() - ts_start; /* Measure execution time of tick task */
513  if (OSTickTaskTimeMax < ts_end) {
514  OSTickTaskTimeMax = ts_end;
515  }
517 }