| LPCOpen Platform v1.03 LPCOpen Platform for NXP LPC Microcontrollers

- Main Page
- Modules
- Data Structures
- Files
- Related Pages

- File List
- Globals

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,
71 (OS_OPT )OS_OPT_PEND_BLOCKING,
72 (CPU_TS *)&ts,
73 (OS_ERR *)&err); /* Wait for signal from tick interrupt */
74 if (err == OS_ERR_NONE) {
75 if (OSRunning == OS_STATE_OS_RUNNING) {
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
115 OSTickTaskTimeMax = (CPU_TS)0u;
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
126 if (OSCfg_TickTaskStkSize < OSCfg_StkSizeMin) {
127 *p_err = OS_ERR_TICK_STK_SIZE_INVALID;
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"),
138 (OS_TASK_PTR )OS_TickTask,
139 (void *)0,
140 (OS_PRIO )OSCfg_TickTaskPrio,
141 (CPU_STK *)OSCfg_TickTaskStkBasePtr,
142 (CPU_STK_SIZE)OSCfg_TickTaskStkLimit,
143 (CPU_STK_SIZE)OSCfg_TickTaskStkSize,
144 (OS_MSG_QTY )0u,
145 (OS_TICK )0u,
146 (void *)0,
147 (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_NO_TLS),
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 {
168 OS_TICK_SPOKE_IX i;
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
386 void OS_TickListResetPeak (void)
387 {
388 OS_TICK_SPOKE_IX i;
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
427 OS_CRITICAL_ENTER();
428 ts_start = OS_TS_GET();
429 OSTickCtr++; /* Keep track of the number of ticks */
430 spoke = (OS_TICK_SPOKE_IX)(OSTickCtr % OSCfg_TickWheelSize);
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:
440 case OS_TASK_STATE_SUSPENDED:
441 case OS_TASK_STATE_PEND_SUSPENDED:
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
455 case OS_TASK_STATE_PEND_TIMEOUT:
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
474 case OS_TASK_STATE_DLY_SUSPENDED:
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
485 case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
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 }
516 OS_CRITICAL_EXIT();
517 }