1
|
/*
|
2
|
FreeRTOS.org V4.2.1 - Copyright (C) 2003-2007 Richard Barry.
|
3
|
|
4
|
This file is part of the FreeRTOS.org distribution.
|
5
|
|
6
|
FreeRTOS.org 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 2 of the License, or
|
9
|
(at your option) any later version.
|
10
|
|
11
|
FreeRTOS.org 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 FreeRTOS.org; if not, write to the Free Software
|
18
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
19
|
|
20
|
A special exception to the GPL can be applied should you wish to distribute
|
21
|
a combined work that includes FreeRTOS.org, without being obliged to provide
|
22
|
the source code for any proprietary components. See the licensing section
|
23
|
of http://www.FreeRTOS.org for full details of how and when the exception
|
24
|
can be applied.
|
25
|
|
26
|
***************************************************************************
|
27
|
See http://www.FreeRTOS.org for documentation, latest information, license
|
28
|
and contact details. Please ensure to read the configuration and relevant
|
29
|
port sections of the online documentation.
|
30
|
|
31
|
Also see http://www.SafeRTOS.com for an IEC 61508 compliant version, along
|
32
|
with development and support options.
|
33
|
***************************************************************************
|
34
|
*/
|
35
|
|
36
|
/*
|
37
|
Changes from V1.00:
|
38
|
|
39
|
+ Call to portRESTORE_CONTEXT has been removed. The first context
|
40
|
switch is now performed within sPortStartScheduler().
|
41
|
|
42
|
Changes from V1.01:
|
43
|
|
44
|
+ More use of 8bit data types.
|
45
|
+ Function name prefixes changed where the data type returned has changed.
|
46
|
+ configUSE_TRACE_FACILITY is no longer defined by default.
|
47
|
|
48
|
Changes from V1.2.0
|
49
|
|
50
|
+ Introduced ucTopReadyPriority. This tracks the highest priority ready
|
51
|
queue that contains a valid TCB and thus makes the context switch
|
52
|
slightly faster.
|
53
|
|
54
|
+ prvAddTaskToReadyQueue() has been made a macro.
|
55
|
|
56
|
Changes from V1.2.6
|
57
|
|
58
|
+ Added conditional compilation directives.
|
59
|
+ Extended API.
|
60
|
+ Rearranged function order.
|
61
|
+ Creating a task now causes a context switch if the task being created
|
62
|
has a higher priority than the calling task - assuming the kernel is
|
63
|
running.
|
64
|
+ vTaskDelete() now only causes a context switch if the calling task is
|
65
|
the task being deleted.
|
66
|
|
67
|
Changes from V2.0.0
|
68
|
|
69
|
+ Allow the type of the tick count to be 16 or 32 bits.
|
70
|
+ Introduce xPendingReadyList feature to allow the time interrupts have to
|
71
|
be disabled to be minimised.
|
72
|
+ Remove the #if( INCLUDE_vTaskSuspendAll ) statements. vTaskSuspendAll()
|
73
|
is now always included as it is used by the scheduler itself.
|
74
|
|
75
|
Changes from V2.1.0
|
76
|
|
77
|
+ Bug fix - pxCurrentTCB is now initialised before the call to
|
78
|
prvInitializeTaskLists(). Previously pxCurrentTCB could be accessed
|
79
|
while null.
|
80
|
|
81
|
Changed from V2.1.1
|
82
|
|
83
|
+ Change to where lStackSize is declared within sTaskCreate() to prevent
|
84
|
compiler warnings with 8051 port.
|
85
|
|
86
|
Changes from V2.2.0
|
87
|
|
88
|
+ Explicit use of 'signed' qualifier on portCHAR types added.
|
89
|
+ Changed odd calculation of initial pxTopOfStack value when
|
90
|
portSTACK_GROWTH < 0.
|
91
|
+ Removed pcVersionNumber definition.
|
92
|
|
93
|
Changes from V2.5.3
|
94
|
|
95
|
+ cTaskResumeAll() modified to ensure it can be called prior to the task
|
96
|
lists being initialised.
|
97
|
|
98
|
Changes from V2.5.5
|
99
|
|
100
|
+ Added API function vTaskDelayUntil().
|
101
|
+ Added INCLUDE_vTaskDelay conditional compilation.
|
102
|
|
103
|
Changes from V2.6.0
|
104
|
|
105
|
+ Updated the vWriteTraceToBuffer macro to always be 4 byte aligned so it
|
106
|
can be used on ARM architectures.
|
107
|
+ tskMAX_TASK_NAME_LEN definition replaced with the port specific
|
108
|
configMAX_TASK_NAME_LEN definition.
|
109
|
+ Removed the call to strcpy when copying across the task name into the
|
110
|
TCB.
|
111
|
+ Added ucTasksDeleted variable to prevent vTaskSuspendAll() being called
|
112
|
too often in the idle task.
|
113
|
|
114
|
Changes between V3.0.0 and V2.6.1
|
115
|
|
116
|
+ When resuming the scheduler a yield is performed if either a tick has
|
117
|
been missed, or a task is moved from the pending ready list into a ready
|
118
|
list. Previously a yield was not performed on this second condition.
|
119
|
+ Introduced the type portBASE_TYPE. This necessitates several API
|
120
|
changes.
|
121
|
+ Removed the sUsingPreemption variable. The constant defined in
|
122
|
portmacro.h is now used directly.
|
123
|
+ The idle task can now include an optional hook function - and no longer
|
124
|
completes its time slice if other tasks with equal priority to it are
|
125
|
ready to run.
|
126
|
+ See the FreeRTOS.org documentation for more information on V2.x.x to
|
127
|
V3.x.x modifications.
|
128
|
|
129
|
Changes from V3.1.1
|
130
|
|
131
|
+ Modified vTaskPrioritySet() and vTaskResume() to allow these functions to
|
132
|
be called while the scheduler is suspended.
|
133
|
+ Corrected the task ordering within event lists.
|
134
|
|
135
|
Changes from V3.2.0
|
136
|
|
137
|
+ Added function xTaskGetCurrentTaskHandle().
|
138
|
|
139
|
Changes from V3.2.4
|
140
|
|
141
|
+ Changed the volatile declarations on some variables to reflect the
|
142
|
changes to the list definitions.
|
143
|
+ Changed the order of the TCB definition so there is commonality between
|
144
|
the task control block and a co-routine control block.
|
145
|
+ Allow the scheduler to be started even if no tasks other than the idle
|
146
|
task has been created. This allows co-routines to run even when no tasks
|
147
|
have been created.
|
148
|
+ The need for a context switch is now signalled if a task woken by an
|
149
|
event has a priority greater or equal to the currently running task.
|
150
|
Previously this was only greater than.
|
151
|
|
152
|
Changes from V4.0.0
|
153
|
|
154
|
+ Added the xMissedYield handling.
|
155
|
|
156
|
Changes from V4.0.1
|
157
|
|
158
|
+ The function vTaskList() now suspends the scheduler rather than disabling
|
159
|
interrupts during the creation of the task list.
|
160
|
+ Allow a task to delete itself by passing in its own handle. Previously
|
161
|
this could only be done by passing in NULL.
|
162
|
+ The tick hook function is now called only within a tick isr. Previously
|
163
|
it was also called when the tick function was called during the scheduler
|
164
|
unlocking process.
|
165
|
|
166
|
Changes from V4.0.3
|
167
|
|
168
|
+ Extra checks have been placed in vTaskPrioritySet() to avoid unnecessary
|
169
|
yields.
|
170
|
|
171
|
Changed from V4.0.4
|
172
|
|
173
|
+ Bug fix: The 'value' of the event list item is updated when the priority
|
174
|
of a task is changed. Previously only the priority of the TCB itself was
|
175
|
changed.
|
176
|
+ When resuming a task a check is first made to see if the task is actually
|
177
|
suspended.
|
178
|
+ vTaskPrioritySet() and vTaskResume() no longer use the event list item.
|
179
|
This has not been necessary since V4.0.1 when the xMissedYield handling
|
180
|
was added.
|
181
|
+ Implement xTaskResumeFromISR().
|
182
|
|
183
|
Changes from V4.0.5
|
184
|
|
185
|
+ Added utility functions and xOverflowCount variable to facilitate the
|
186
|
queue.c changes.
|
187
|
|
188
|
Changes from V4.1.2
|
189
|
|
190
|
+ Tasks that block on events with a timeout of portMAX_DELAY are now
|
191
|
blocked indefinitely if configINCLUDE_vTaskSuspend is defined.
|
192
|
Previously portMAX_DELAY was just the longest block time possible.
|
193
|
|
194
|
Changes from V4.1.3
|
195
|
|
196
|
+ Very small change made to xTaskCheckForTimeout() as a result of the
|
197
|
SafeRTOS testing. This corrects the case where the function can return an
|
198
|
invalid value - but only in an extremely unlikely scenario.
|
199
|
*/
|
200
|
|
201
|
#include <stdio.h>
|
202
|
#include <stdlib.h>
|
203
|
#include <string.h>
|
204
|
|
205
|
#include "FreeRTOS.h"
|
206
|
#include "task.h"
|
207
|
|
208
|
/*
|
209
|
* Macro to define the amount of stack available to the idle task.
|
210
|
*/
|
211
|
#define tskIDLE_STACK_SIZE configMINIMAL_STACK_SIZE
|
212
|
|
213
|
|
214
|
/*
|
215
|
* Default a definitions for backwards compatibility with old
|
216
|
* portmacro.h files.
|
217
|
*/
|
218
|
#ifndef configMAX_TASK_NAME_LEN
|
219
|
#define configMAX_TASK_NAME_LEN 16
|
220
|
#endif
|
221
|
|
222
|
#ifndef INCLUDE_xTaskGetCurrentTaskHandle
|
223
|
#define INCLUDE_xTaskGetCurrentTaskHandle 0
|
224
|
#endif
|
225
|
|
226
|
#ifndef configIDLE_SHOULD_YIELD
|
227
|
#define configIDLE_SHOULD_YIELD 1
|
228
|
#endif
|
229
|
|
230
|
#if configMAX_TASK_NAME_LEN < 1
|
231
|
#undef configMAX_TASK_NAME_LEN
|
232
|
#define configMAX_TASK_NAME_LEN 1
|
233
|
#endif
|
234
|
|
235
|
#ifndef INCLUDE_xTaskResumeFromISR
|
236
|
#define INCLUDE_xTaskResumeFromISR 1
|
237
|
#endif
|
238
|
|
239
|
/*
|
240
|
* Task control block. A task control block (TCB) is allocated to each task,
|
241
|
* and stores the context of the task.
|
242
|
*/
|
243
|
typedef struct tskTaskControlBlock
|
244
|
{
|
245
|
volatile portSTACK_TYPE *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE STRUCT. */
|
246
|
xListItem xGenericListItem; /*< List item used to place the TCB in ready and blocked queues. */
|
247
|
xListItem xEventListItem; /*< List item used to place the TCB in event lists. */
|
248
|
unsigned portBASE_TYPE uxPriority; /*< The priority of the task where 0 is the lowest priority. */
|
249
|
portSTACK_TYPE *pxStack; /*< Points to the start of the stack. */
|
250
|
unsigned portBASE_TYPE uxTCBNumber; /*< This is used for tracing the scheduler and making debugging easier only. */
|
251
|
signed portCHAR pcTaskName[configMAX_TASK_NAME_LEN]; /*< Descriptive name given to the task when created. Facilitates debugging only. */
|
252
|
unsigned portSHORT usStackDepth; /*< Total depth of the stack (when empty). This is defined as the number of variables the stack can hold, not the number of bytes. */
|
253
|
} tskTCB;
|
254
|
|
255
|
/*lint -e956 */
|
256
|
|
257
|
tskTCB *volatile pxCurrentTCB = NULL;
|
258
|
|
259
|
/* Lists for ready and blocked tasks. --------------------*/
|
260
|
|
261
|
static xList pxReadyTasksLists[configMAX_PRIORITIES]; /*< Prioritised ready tasks. */
|
262
|
static xList xDelayedTaskList1; /*< Delayed tasks. */
|
263
|
static xList xDelayedTaskList2; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */
|
264
|
static xList *volatile pxDelayedTaskList; /*< Points to the delayed task list currently being used. */
|
265
|
static xList *volatile pxOverflowDelayedTaskList; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */
|
266
|
static xList xPendingReadyList; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready queue when the scheduler is resumed. */
|
267
|
|
268
|
#if ( INCLUDE_vTaskDelete == 1 )
|
269
|
|
270
|
static volatile xList xTasksWaitingTermination; /*< Tasks that have been deleted - but the their memory not yet freed. */
|
271
|
static volatile unsigned portBASE_TYPE uxTasksDeleted =
|
272
|
(unsigned portBASE_TYPE) 0;
|
273
|
|
274
|
#endif
|
275
|
|
276
|
#if ( INCLUDE_vTaskSuspend == 1 )
|
277
|
|
278
|
static xList xSuspendedTaskList; /*< Tasks that are currently suspended. */
|
279
|
|
280
|
#endif
|
281
|
|
282
|
/* File private variables. --------------------------------*/
|
283
|
static volatile unsigned portBASE_TYPE uxCurrentNumberOfTasks =
|
284
|
(unsigned portBASE_TYPE) 0;
|
285
|
static volatile portTickType xTickCount = (portTickType) 0;
|
286
|
static unsigned portBASE_TYPE uxTopUsedPriority = tskIDLE_PRIORITY;
|
287
|
static volatile unsigned portBASE_TYPE uxTopReadyPriority = tskIDLE_PRIORITY;
|
288
|
static volatile signed portBASE_TYPE xSchedulerRunning = pdFALSE;
|
289
|
static volatile unsigned portBASE_TYPE uxSchedulerSuspended =
|
290
|
(unsigned portBASE_TYPE) pdFALSE;
|
291
|
static volatile unsigned portBASE_TYPE uxMissedTicks =
|
292
|
(unsigned portBASE_TYPE) 0;
|
293
|
static volatile portBASE_TYPE xMissedYield = (portBASE_TYPE) pdFALSE;
|
294
|
static volatile portBASE_TYPE xNumOfOverflows = (portBASE_TYPE) 0;
|
295
|
/* Debugging and trace facilities private variables and macros. ------------*/
|
296
|
|
297
|
/*
|
298
|
* The value used to fill the stack of a task when the task is created. This
|
299
|
* is used purely for checking the high water mark for tasks.
|
300
|
*/
|
301
|
#define tskSTACK_FILL_BYTE ( 0xa5 )
|
302
|
|
303
|
/*
|
304
|
* Macros used by vListTask to indicate which state a task is in.
|
305
|
*/
|
306
|
#define tskBLOCKED_CHAR ( ( signed portCHAR ) 'B' )
|
307
|
#define tskREADY_CHAR ( ( signed portCHAR ) 'R' )
|
308
|
#define tskDELETED_CHAR ( ( signed portCHAR ) 'D' )
|
309
|
#define tskSUSPENDED_CHAR ( ( signed portCHAR ) 'S' )
|
310
|
|
311
|
/*
|
312
|
* Macros and private variables used by the trace facility.
|
313
|
*/
|
314
|
#if ( configUSE_TRACE_FACILITY == 1 )
|
315
|
|
316
|
#define tskSIZE_OF_EACH_TRACE_LINE ( ( unsigned portLONG ) ( sizeof( unsigned portLONG ) + sizeof( unsigned portLONG ) ) )
|
317
|
static volatile signed portCHAR *volatile pcTraceBuffer;
|
318
|
static signed portCHAR *pcTraceBufferStart;
|
319
|
static signed portCHAR *pcTraceBufferEnd;
|
320
|
static signed portBASE_TYPE xTracing = pdFALSE;
|
321
|
|
322
|
#endif
|
323
|
|
324
|
/*
|
325
|
* Macro that writes a trace of scheduler activity to a buffer. This trace
|
326
|
* shows which task is running when and is very useful as a debugging tool.
|
327
|
* As this macro is called each context switch it is a good idea to undefine
|
328
|
* it if not using the facility.
|
329
|
*/
|
330
|
#if ( configUSE_TRACE_FACILITY == 1 )
|
331
|
|
332
|
#define vWriteTraceToBuffer() \
|
333
|
{ \
|
334
|
if( xTracing ) \
|
335
|
{ \
|
336
|
static unsigned portBASE_TYPE uxPreviousTask = 255; \
|
337
|
\
|
338
|
if( uxPreviousTask != pxCurrentTCB->uxTCBNumber ) \
|
339
|
{ \
|
340
|
if( ( pcTraceBuffer + tskSIZE_OF_EACH_TRACE_LINE ) < pcTraceBufferEnd ) \
|
341
|
{ \
|
342
|
uxPreviousTask = pxCurrentTCB->uxTCBNumber; \
|
343
|
*( unsigned portLONG * ) pcTraceBuffer = ( unsigned portLONG ) xTickCount; \
|
344
|
pcTraceBuffer += sizeof( unsigned portLONG ); \
|
345
|
*( unsigned portLONG * ) pcTraceBuffer = ( unsigned portLONG ) uxPreviousTask; \
|
346
|
pcTraceBuffer += sizeof( unsigned portLONG ); \
|
347
|
} \
|
348
|
else \
|
349
|
{ \
|
350
|
xTracing = pdFALSE; \
|
351
|
} \
|
352
|
} \
|
353
|
} \
|
354
|
}
|
355
|
|
356
|
#else
|
357
|
|
358
|
#define vWriteTraceToBuffer()
|
359
|
|
360
|
#endif
|
361
|
|
362
|
|
363
|
/*
|
364
|
* Place the task represented by pxTCB into the appropriate ready queue for
|
365
|
* the task. It is inserted at the end of the list. One quirk of this is
|
366
|
* that if the task being inserted is at the same priority as the currently
|
367
|
* executing task, then it will only be rescheduled after the currently
|
368
|
* executing task has been rescheduled.
|
369
|
*/
|
370
|
#define prvAddTaskToReadyQueue( pxTCB ) \
|
371
|
{ \
|
372
|
if( pxTCB->uxPriority > uxTopReadyPriority ) \
|
373
|
{ \
|
374
|
uxTopReadyPriority = pxTCB->uxPriority; \
|
375
|
} \
|
376
|
vListInsertEnd( ( xList * ) &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xGenericListItem ) ); \
|
377
|
}
|
378
|
|
379
|
/*
|
380
|
* Macro that looks at the list of tasks that are currently delayed to see if
|
381
|
* any require waking.
|
382
|
*
|
383
|
* Tasks are stored in the queue in the order of their wake time - meaning
|
384
|
* once one tasks has been found whose timer has not expired we need not look
|
385
|
* any further down the list.
|
386
|
*/
|
387
|
#define prvCheckDelayedTasks() \
|
388
|
{ \
|
389
|
register tskTCB *pxTCB; \
|
390
|
\
|
391
|
while( ( pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ) ) != NULL ) \
|
392
|
{ \
|
393
|
if( xTickCount < listGET_LIST_ITEM_VALUE( &( pxTCB->xGenericListItem ) ) ) \
|
394
|
{ \
|
395
|
break; \
|
396
|
} \
|
397
|
vListRemove( &( pxTCB->xGenericListItem ) ); \
|
398
|
/* Is the task waiting on an event also? */ \
|
399
|
if( pxTCB->xEventListItem.pvContainer ) \
|
400
|
{ \
|
401
|
vListRemove( &( pxTCB->xEventListItem ) ); \
|
402
|
} \
|
403
|
prvAddTaskToReadyQueue( pxTCB ); \
|
404
|
} \
|
405
|
}
|
406
|
|
407
|
/*
|
408
|
* Several functions take an xTaskHandle parameter that can optionally be NULL,
|
409
|
* where NULL is used to indicate that the handle of the currently executing
|
410
|
* task should be used in place of the parameter. This macro simply checks to
|
411
|
* see if the parameter is NULL and returns a pointer to the appropriate TCB.
|
412
|
*/
|
413
|
#define prvGetTCBFromHandle( pxHandle ) ( ( pxHandle == NULL ) ? ( tskTCB * ) pxCurrentTCB : ( tskTCB * ) pxHandle )
|
414
|
|
415
|
|
416
|
/* File private functions. --------------------------------*/
|
417
|
|
418
|
/*
|
419
|
* Utility to ready a TCB for a given task. Mainly just copies the parameters
|
420
|
* into the TCB structure.
|
421
|
*/
|
422
|
static void prvInitialiseTCBVariables (tskTCB * pxTCB,
|
423
|
unsigned portSHORT usStackDepth,
|
424
|
const signed portCHAR * const pcName,
|
425
|
unsigned portBASE_TYPE uxPriority);
|
426
|
|
427
|
/*
|
428
|
* Utility to ready all the lists used by the scheduler. This is called
|
429
|
* automatically upon the creation of the first task.
|
430
|
*/
|
431
|
static void prvInitialiseTaskLists (void);
|
432
|
|
433
|
/*
|
434
|
* The idle task, which as all tasks is implemented as a never ending loop.
|
435
|
* The idle task is automatically created and added to the ready lists upon
|
436
|
* creation of the first user task.
|
437
|
*
|
438
|
* The portTASK_FUNCTION_PROTO() macro is used to allow port/compiler specific
|
439
|
* language extensions. The equivalent prototype for this function is:
|
440
|
*
|
441
|
* void prvIdleTask( void *pvParameters );
|
442
|
*
|
443
|
*/
|
444
|
static portTASK_FUNCTION_PROTO (prvIdleTask, pvParameters);
|
445
|
|
446
|
/*
|
447
|
* Utility to free all memory allocated by the scheduler to hold a TCB,
|
448
|
* including the stack pointed to by the TCB.
|
449
|
*
|
450
|
* This does not free memory allocated by the task itself (i.e. memory
|
451
|
* allocated by calls to pvPortMalloc from within the tasks application code).
|
452
|
*/
|
453
|
#if ( ( INCLUDE_vTaskDelete == 1 ) || ( INCLUDE_vTaskCleanUpResources == 1 ) )
|
454
|
static void prvDeleteTCB (tskTCB * pxTCB);
|
455
|
#endif
|
456
|
|
457
|
/*
|
458
|
* Used only by the idle task. This checks to see if anything has been placed
|
459
|
* in the list of tasks waiting to be deleted. If so the task is cleaned up
|
460
|
* and its TCB deleted.
|
461
|
*/
|
462
|
static void prvCheckTasksWaitingTermination (void);
|
463
|
|
464
|
/*
|
465
|
* Allocates memory from the heap for a TCB and associated stack. Checks the
|
466
|
* allocation was successful.
|
467
|
*/
|
468
|
static tskTCB *prvAllocateTCBAndStack (unsigned portSHORT usStackDepth);
|
469
|
|
470
|
/*
|
471
|
* Called from vTaskList. vListTasks details all the tasks currently under
|
472
|
* control of the scheduler. The tasks may be in one of a number of lists.
|
473
|
* prvListTaskWithinSingleList accepts a list and details the tasks from
|
474
|
* within just that list.
|
475
|
*
|
476
|
* THIS FUNCTION IS INTENDED FOR DEBUGGING ONLY, AND SHOULD NOT BE CALLED FROM
|
477
|
* NORMAL APPLICATION CODE.
|
478
|
*/
|
479
|
#if ( configUSE_TRACE_FACILITY == 1 )
|
480
|
|
481
|
static void prvListTaskWithinSingleList (signed portCHAR * pcWriteBuffer,
|
482
|
xList * pxList,
|
483
|
signed portCHAR cStatus);
|
484
|
|
485
|
#endif
|
486
|
|
487
|
/*
|
488
|
* When a task is created, the stack of the task is filled with a known value.
|
489
|
* This function determines the 'high water mark' of the task stack by
|
490
|
* determining how much of the stack remains at the original preset value.
|
491
|
*/
|
492
|
#if ( configUSE_TRACE_FACILITY == 1 )
|
493
|
|
494
|
unsigned portSHORT usTaskCheckFreeStackSpace (const unsigned portCHAR *
|
495
|
pucStackByte);
|
496
|
|
497
|
#endif
|
498
|
|
499
|
/*lint +e956 */
|
500
|
|
501
|
|
502
|
|
503
|
|
504
|
|
505
|
/*-----------------------------------------------------------
|
506
|
* TASK CREATION API documented in task.h
|
507
|
*----------------------------------------------------------*/
|
508
|
|
509
|
signed portBASE_TYPE
|
510
|
xTaskCreate (pdTASK_CODE pvTaskCode, const signed portCHAR * const pcName,
|
511
|
unsigned portSHORT usStackDepth, void *pvParameters,
|
512
|
unsigned portBASE_TYPE uxPriority, xTaskHandle * pxCreatedTask)
|
513
|
{
|
514
|
signed portBASE_TYPE xReturn;
|
515
|
tskTCB *pxNewTCB;
|
516
|
static unsigned portBASE_TYPE uxTaskNumber = 0; /*lint !e956 Static is deliberate - this is guarded before use. */
|
517
|
|
518
|
/* Allocate the memory required by the TCB and stack for the new task.
|
519
|
checking that the allocation was successful. */
|
520
|
pxNewTCB = prvAllocateTCBAndStack (usStackDepth);
|
521
|
|
522
|
if (pxNewTCB != NULL)
|
523
|
{
|
524
|
portSTACK_TYPE *pxTopOfStack;
|
525
|
|
526
|
/* Setup the newly allocated TCB with the initial state of the task. */
|
527
|
prvInitialiseTCBVariables (pxNewTCB, usStackDepth, pcName, uxPriority);
|
528
|
|
529
|
/* Calculate the top of stack address. This depends on whether the
|
530
|
stack grows from high memory to low (as per the 80x86) or visa versa.
|
531
|
portSTACK_GROWTH is used to make the result positive or negative as
|
532
|
required by the port. */
|
533
|
#if portSTACK_GROWTH < 0
|
534
|
{
|
535
|
pxTopOfStack = pxNewTCB->pxStack + (pxNewTCB->usStackDepth - 1);
|
536
|
}
|
537
|
#else
|
538
|
{
|
539
|
pxTopOfStack = pxNewTCB->pxStack;
|
540
|
}
|
541
|
#endif
|
542
|
|
543
|
/* Initialize the TCB stack to look as if the task was already running,
|
544
|
but had been interrupted by the scheduler. The return address is set
|
545
|
to the start of the task function. Once the stack has been initialised
|
546
|
the top of stack variable is updated. */
|
547
|
pxNewTCB->pxTopOfStack =
|
548
|
pxPortInitialiseStack (pxTopOfStack, pvTaskCode, pvParameters);
|
549
|
|
550
|
/* We are going to manipulate the task queues to add this task to a
|
551
|
ready list, so must make sure no interrupts occur. */
|
552
|
portENTER_CRITICAL ();
|
553
|
{
|
554
|
uxCurrentNumberOfTasks++;
|
555
|
if (uxCurrentNumberOfTasks == (unsigned portBASE_TYPE) 1)
|
556
|
{
|
557
|
/* As this is the first task it must also be the current task. */
|
558
|
pxCurrentTCB = pxNewTCB;
|
559
|
|
560
|
/* This is the first task to be created so do the preliminary
|
561
|
initialisation required. We will not recover if this call
|
562
|
fails, but we will report the failure. */
|
563
|
prvInitialiseTaskLists ();
|
564
|
}
|
565
|
else
|
566
|
{
|
567
|
/* If the scheduler is not already running, make this task the
|
568
|
current task if it is the highest priority task to be created
|
569
|
so far. */
|
570
|
if (xSchedulerRunning == pdFALSE)
|
571
|
{
|
572
|
if (pxCurrentTCB->uxPriority <= uxPriority)
|
573
|
{
|
574
|
pxCurrentTCB = pxNewTCB;
|
575
|
}
|
576
|
}
|
577
|
}
|
578
|
|
579
|
/* Remember the top priority to make context switching faster. Use
|
580
|
the priority in pxNewTCB as this has been capped to a valid value. */
|
581
|
if (pxNewTCB->uxPriority > uxTopUsedPriority)
|
582
|
{
|
583
|
uxTopUsedPriority = pxNewTCB->uxPriority;
|
584
|
}
|
585
|
|
586
|
/* Add a counter into the TCB for tracing only. */
|
587
|
pxNewTCB->uxTCBNumber = uxTaskNumber;
|
588
|
uxTaskNumber++;
|
589
|
|
590
|
prvAddTaskToReadyQueue (pxNewTCB);
|
591
|
|
592
|
xReturn = pdPASS;
|
593
|
}
|
594
|
portEXIT_CRITICAL ();
|
595
|
}
|
596
|
else
|
597
|
{
|
598
|
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
|
599
|
}
|
600
|
|
601
|
if (xReturn == pdPASS)
|
602
|
{
|
603
|
if ((void *) pxCreatedTask != NULL)
|
604
|
{
|
605
|
/* Pass the TCB out - in an anonymous way. The calling function/
|
606
|
task can use this as a handle to delete the task later if
|
607
|
required. */
|
608
|
*pxCreatedTask = (xTaskHandle) pxNewTCB;
|
609
|
}
|
610
|
|
611
|
if (xSchedulerRunning != pdFALSE)
|
612
|
{
|
613
|
/* If the created task is of a higher priority than the current task
|
614
|
then it should run now. */
|
615
|
if (pxCurrentTCB->uxPriority < uxPriority)
|
616
|
{
|
617
|
taskYIELD ();
|
618
|
}
|
619
|
}
|
620
|
}
|
621
|
|
622
|
return xReturn;
|
623
|
}
|
624
|
|
625
|
/*-----------------------------------------------------------*/
|
626
|
|
627
|
#if ( INCLUDE_vTaskDelete == 1 )
|
628
|
|
629
|
void
|
630
|
vTaskDelete (xTaskHandle pxTaskToDelete)
|
631
|
{
|
632
|
tskTCB *pxTCB;
|
633
|
|
634
|
taskENTER_CRITICAL ();
|
635
|
{
|
636
|
/* Ensure a yield is performed if the current task is being
|
637
|
deleted. */
|
638
|
if (pxTaskToDelete == pxCurrentTCB)
|
639
|
{
|
640
|
pxTaskToDelete = NULL;
|
641
|
}
|
642
|
|
643
|
/* If null is passed in here then we are deleting ourselves. */
|
644
|
pxTCB = prvGetTCBFromHandle (pxTaskToDelete);
|
645
|
|
646
|
/* Remove task from the ready list and place in the termination list.
|
647
|
This will stop the task from be scheduled. The idle task will check
|
648
|
the termination list and free up any memory allocated by the
|
649
|
scheduler for the TCB and stack. */
|
650
|
vListRemove (&(pxTCB->xGenericListItem));
|
651
|
|
652
|
/* Is the task waiting on an event also? */
|
653
|
if (pxTCB->xEventListItem.pvContainer)
|
654
|
{
|
655
|
vListRemove (&(pxTCB->xEventListItem));
|
656
|
}
|
657
|
|
658
|
vListInsertEnd ((xList *) & xTasksWaitingTermination,
|
659
|
&(pxTCB->xGenericListItem));
|
660
|
|
661
|
/* Increment the ucTasksDeleted variable so the idle task knows
|
662
|
there is a task that has been deleted and that it should therefore
|
663
|
check the xTasksWaitingTermination list. */
|
664
|
++uxTasksDeleted;
|
665
|
}
|
666
|
taskEXIT_CRITICAL ();
|
667
|
|
668
|
/* Force a reschedule if we have just deleted the current task. */
|
669
|
if (xSchedulerRunning != pdFALSE)
|
670
|
{
|
671
|
if ((void *) pxTaskToDelete == NULL)
|
672
|
{
|
673
|
taskYIELD ();
|
674
|
}
|
675
|
}
|
676
|
}
|
677
|
|
678
|
#endif
|
679
|
|
680
|
|
681
|
|
682
|
|
683
|
|
684
|
|
685
|
/*-----------------------------------------------------------
|
686
|
* TASK CONTROL API documented in task.h
|
687
|
*----------------------------------------------------------*/
|
688
|
|
689
|
#if ( INCLUDE_vTaskDelayUntil == 1 )
|
690
|
|
691
|
void
|
692
|
vTaskDelayUntil (portTickType * pxPreviousWakeTime,
|
693
|
portTickType xTimeIncrement)
|
694
|
{
|
695
|
portTickType xTimeToWake;
|
696
|
portBASE_TYPE xAlreadyYielded, xShouldDelay = pdFALSE;
|
697
|
|
698
|
vTaskSuspendAll ();
|
699
|
{
|
700
|
/* Generate the tick time at which the task wants to wake. */
|
701
|
xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;
|
702
|
|
703
|
if (xTickCount < *pxPreviousWakeTime)
|
704
|
{
|
705
|
/* The tick count has overflowed since this function was
|
706
|
lasted called. In this case the only time we should ever
|
707
|
actually delay is if the wake time has also overflowed,
|
708
|
and the wake time is greater than the tick time. When this
|
709
|
is the case it is as if neither time had overflowed. */
|
710
|
if ((xTimeToWake < *pxPreviousWakeTime) && (xTimeToWake > xTickCount))
|
711
|
{
|
712
|
xShouldDelay = pdTRUE;
|
713
|
}
|
714
|
}
|
715
|
else
|
716
|
{
|
717
|
/* The tick time has not overflowed. In this case we will
|
718
|
delay if either the wake time has overflowed, and/or the
|
719
|
tick time is less than the wake time. */
|
720
|
if ((xTimeToWake < *pxPreviousWakeTime) || (xTimeToWake > xTickCount))
|
721
|
{
|
722
|
xShouldDelay = pdTRUE;
|
723
|
}
|
724
|
}
|
725
|
|
726
|
/* Update the wake time ready for the next call. */
|
727
|
*pxPreviousWakeTime = xTimeToWake;
|
728
|
|
729
|
if (xShouldDelay)
|
730
|
{
|
731
|
/* We must remove ourselves from the ready list before adding
|
732
|
ourselves to the blocked list as the same list item is used for
|
733
|
both lists. */
|
734
|
vListRemove ((xListItem *) & (pxCurrentTCB->xGenericListItem));
|
735
|
|
736
|
/* The list item will be inserted in wake time order. */
|
737
|
listSET_LIST_ITEM_VALUE (&(pxCurrentTCB->xGenericListItem),
|
738
|
xTimeToWake);
|
739
|
|
740
|
if (xTimeToWake < xTickCount)
|
741
|
{
|
742
|
/* Wake time has overflowed. Place this item in the
|
743
|
overflow list. */
|
744
|
vListInsert ((xList *) pxOverflowDelayedTaskList,
|
745
|
(xListItem *) & (pxCurrentTCB->xGenericListItem));
|
746
|
}
|
747
|
else
|
748
|
{
|
749
|
/* The wake time has not overflowed, so we can use the
|
750
|
current block list. */
|
751
|
vListInsert ((xList *) pxDelayedTaskList,
|
752
|
(xListItem *) & (pxCurrentTCB->xGenericListItem));
|
753
|
}
|
754
|
}
|
755
|
}
|
756
|
xAlreadyYielded = xTaskResumeAll ();
|
757
|
|
758
|
/* Force a reschedule if xTaskResumeAll has not already done so, we may
|
759
|
have put ourselves to sleep. */
|
760
|
if (!xAlreadyYielded)
|
761
|
{
|
762
|
taskYIELD ();
|
763
|
}
|
764
|
}
|
765
|
|
766
|
#endif
|
767
|
/*-----------------------------------------------------------*/
|
768
|
|
769
|
#if ( INCLUDE_vTaskDelay == 1 )
|
770
|
|
771
|
void
|
772
|
vTaskDelay (portTickType xTicksToDelay)
|
773
|
{
|
774
|
portTickType xTimeToWake;
|
775
|
signed portBASE_TYPE xAlreadyYielded = pdFALSE;
|
776
|
|
777
|
/* A delay time of zero just forces a reschedule. */
|
778
|
if (xTicksToDelay > (portTickType) 0)
|
779
|
{
|
780
|
vTaskSuspendAll ();
|
781
|
{
|
782
|
/* A task that is removed from the event list while the
|
783
|
scheduler is suspended will not get placed in the ready
|
784
|
list or removed from the blocked list until the scheduler
|
785
|
is resumed.
|
786
|
|
787
|
This task cannot be in an event list as it is the currently
|
788
|
executing task. */
|
789
|
|
790
|
/* Calculate the time to wake - this may overflow but this is
|
791
|
not a problem. */
|
792
|
xTimeToWake = xTickCount + xTicksToDelay;
|
793
|
|
794
|
/* We must remove ourselves from the ready list before adding
|
795
|
ourselves to the blocked list as the same list item is used for
|
796
|
both lists. */
|
797
|
vListRemove ((xListItem *) & (pxCurrentTCB->xGenericListItem));
|
798
|
|
799
|
/* The list item will be inserted in wake time order. */
|
800
|
listSET_LIST_ITEM_VALUE (&(pxCurrentTCB->xGenericListItem),
|
801
|
xTimeToWake);
|
802
|
|
803
|
if (xTimeToWake < xTickCount)
|
804
|
{
|
805
|
/* Wake time has overflowed. Place this item in the
|
806
|
overflow list. */
|
807
|
vListInsert ((xList *) pxOverflowDelayedTaskList,
|
808
|
(xListItem *) & (pxCurrentTCB->xGenericListItem));
|
809
|
}
|
810
|
else
|
811
|
{
|
812
|
/* The wake time has not overflowed, so we can use the
|
813
|
current block list. */
|
814
|
vListInsert ((xList *) pxDelayedTaskList,
|
815
|
(xListItem *) & (pxCurrentTCB->xGenericListItem));
|
816
|
}
|
817
|
}
|
818
|
xAlreadyYielded = xTaskResumeAll ();
|
819
|
}
|
820
|
|
821
|
/* Force a reschedule if xTaskResumeAll has not already done so, we may
|
822
|
have put ourselves to sleep. */
|
823
|
if (!xAlreadyYielded)
|
824
|
{
|
825
|
taskYIELD ();
|
826
|
}
|
827
|
}
|
828
|
|
829
|
#endif
|
830
|
/*-----------------------------------------------------------*/
|
831
|
|
832
|
#if ( INCLUDE_uxTaskPriorityGet == 1 )
|
833
|
|
834
|
unsigned portBASE_TYPE
|
835
|
uxTaskPriorityGet (xTaskHandle pxTask)
|
836
|
{
|
837
|
tskTCB *pxTCB;
|
838
|
unsigned portBASE_TYPE uxReturn;
|
839
|
|
840
|
taskENTER_CRITICAL ();
|
841
|
{
|
842
|
/* If null is passed in here then we are changing the
|
843
|
priority of the calling function. */
|
844
|
pxTCB = prvGetTCBFromHandle (pxTask);
|
845
|
uxReturn = pxTCB->uxPriority;
|
846
|
}
|
847
|
taskEXIT_CRITICAL ();
|
848
|
|
849
|
return uxReturn;
|
850
|
}
|
851
|
|
852
|
#endif
|
853
|
/*-----------------------------------------------------------*/
|
854
|
|
855
|
#if ( INCLUDE_vTaskPrioritySet == 1 )
|
856
|
|
857
|
void
|
858
|
vTaskPrioritySet (xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority)
|
859
|
{
|
860
|
tskTCB *pxTCB;
|
861
|
unsigned portBASE_TYPE uxCurrentPriority, xYieldRequired = pdFALSE;
|
862
|
|
863
|
/* Ensure the new priority is valid. */
|
864
|
if (uxNewPriority >= configMAX_PRIORITIES)
|
865
|
{
|
866
|
uxNewPriority = configMAX_PRIORITIES - 1;
|
867
|
}
|
868
|
|
869
|
taskENTER_CRITICAL ();
|
870
|
{
|
871
|
/* If null is passed in here then we are changing the
|
872
|
priority of the calling function. */
|
873
|
pxTCB = prvGetTCBFromHandle (pxTask);
|
874
|
uxCurrentPriority = pxTCB->uxPriority;
|
875
|
|
876
|
if (uxCurrentPriority != uxNewPriority)
|
877
|
{
|
878
|
/* The priority change may have readied a task of higher
|
879
|
priority than the calling task. */
|
880
|
if (uxNewPriority > pxCurrentTCB->uxPriority)
|
881
|
{
|
882
|
if (pxTask != NULL)
|
883
|
{
|
884
|
/* The priority of another task is being raised. If we
|
885
|
were raising the priority of the currently running task
|
886
|
there would be no need to switch as it must have already
|
887
|
been the highest priority task. */
|
888
|
xYieldRequired = pdTRUE;
|
889
|
}
|
890
|
}
|
891
|
else if (pxTask == NULL)
|
892
|
{
|
893
|
/* Setting our own priority down means there may now be another
|
894
|
task of higher priority that is ready to execute. */
|
895
|
xYieldRequired = pdTRUE;
|
896
|
}
|
897
|
|
898
|
pxTCB->uxPriority = uxNewPriority;
|
899
|
listSET_LIST_ITEM_VALUE (&(pxTCB->xEventListItem),
|
900
|
configMAX_PRIORITIES -
|
901
|
(portTickType) uxNewPriority);
|
902
|
|
903
|
/* If the task is in the blocked or suspended list we need do
|
904
|
nothing more than change it's priority variable. However, if
|
905
|
the task is in a ready list it needs to be removed and placed
|
906
|
in the queue appropriate to its new priority. */
|
907
|
if (listIS_CONTAINED_WITHIN
|
908
|
(&(pxReadyTasksLists[uxCurrentPriority]),
|
909
|
&(pxTCB->xGenericListItem)))
|
910
|
{
|
911
|
/* The task is currently in its ready list - remove before adding
|
912
|
it to it's new ready list. As we are in a critical section we
|
913
|
can do this even if the scheduler is suspended. */
|
914
|
vListRemove (&(pxTCB->xGenericListItem));
|
915
|
prvAddTaskToReadyQueue (pxTCB);
|
916
|
}
|
917
|
|
918
|
if (xYieldRequired == pdTRUE)
|
919
|
{
|
920
|
taskYIELD ();
|
921
|
}
|
922
|
}
|
923
|
}
|
924
|
taskEXIT_CRITICAL ();
|
925
|
}
|
926
|
|
927
|
#endif
|
928
|
/*-----------------------------------------------------------*/
|
929
|
|
930
|
#if ( INCLUDE_vTaskSuspend == 1 )
|
931
|
|
932
|
void
|
933
|
vTaskSuspend (xTaskHandle pxTaskToSuspend)
|
934
|
{
|
935
|
tskTCB *pxTCB;
|
936
|
|
937
|
taskENTER_CRITICAL ();
|
938
|
{
|
939
|
/* Ensure a yield is performed if the current task is being
|
940
|
suspended. */
|
941
|
if (pxTaskToSuspend == pxCurrentTCB)
|
942
|
{
|
943
|
pxTaskToSuspend = NULL;
|
944
|
}
|
945
|
|
946
|
/* If null is passed in here then we are suspending ourselves. */
|
947
|
pxTCB = prvGetTCBFromHandle (pxTaskToSuspend);
|
948
|
|
949
|
/* Remove task from the ready/delayed list and place in the suspended list. */
|
950
|
vListRemove (&(pxTCB->xGenericListItem));
|
951
|
|
952
|
/* Is the task waiting on an event also? */
|
953
|
if (pxTCB->xEventListItem.pvContainer)
|
954
|
{
|
955
|
vListRemove (&(pxTCB->xEventListItem));
|
956
|
}
|
957
|
|
958
|
vListInsertEnd ((xList *) & xSuspendedTaskList,
|
959
|
&(pxTCB->xGenericListItem));
|
960
|
}
|
961
|
taskEXIT_CRITICAL ();
|
962
|
|
963
|
/* We may have just suspended the current task. */
|
964
|
if ((void *) pxTaskToSuspend == NULL)
|
965
|
{
|
966
|
taskYIELD ();
|
967
|
}
|
968
|
}
|
969
|
|
970
|
#endif
|
971
|
/*-----------------------------------------------------------*/
|
972
|
|
973
|
#if ( INCLUDE_vTaskSuspend == 1 )
|
974
|
|
975
|
void
|
976
|
vTaskResume (xTaskHandle pxTaskToResume)
|
977
|
{
|
978
|
tskTCB *pxTCB;
|
979
|
|
980
|
/* Remove the task from whichever list it is currently in, and place
|
981
|
it in the ready list. */
|
982
|
pxTCB = (tskTCB *) pxTaskToResume;
|
983
|
|
984
|
/* The parameter cannot be NULL as it is impossible to resume the
|
985
|
currently executing task. */
|
986
|
if (pxTCB != NULL)
|
987
|
{
|
988
|
taskENTER_CRITICAL ();
|
989
|
{
|
990
|
/* Is the task we are attempting to resume actually suspended? */
|
991
|
if (listIS_CONTAINED_WITHIN
|
992
|
(&xSuspendedTaskList, &(pxTCB->xGenericListItem)) != pdFALSE)
|
993
|
{
|
994
|
/* Has the task already been resumed from within an ISR? */
|
995
|
if (listIS_CONTAINED_WITHIN
|
996
|
(&xPendingReadyList, &(pxTCB->xEventListItem)) != pdTRUE)
|
997
|
{
|
998
|
/* As we are in a critical section we can access the ready
|
999
|
lists even if the scheduler is suspended. */
|
1000
|
vListRemove (&(pxTCB->xGenericListItem));
|
1001
|
prvAddTaskToReadyQueue (pxTCB);
|
1002
|
|
1003
|
/* We may have just resumed a higher priority task. */
|
1004
|
if (pxTCB->uxPriority >= pxCurrentTCB->uxPriority)
|
1005
|
{
|
1006
|
/* This yield may not cause the task just resumed to run, but
|
1007
|
will leave the lists in the correct state for the next yield. */
|
1008
|
taskYIELD ();
|
1009
|
}
|
1010
|
}
|
1011
|
}
|
1012
|
}
|
1013
|
taskEXIT_CRITICAL ();
|
1014
|
}
|
1015
|
}
|
1016
|
|
1017
|
#endif
|
1018
|
|
1019
|
/*-----------------------------------------------------------*/
|
1020
|
|
1021
|
#if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) )
|
1022
|
|
1023
|
portBASE_TYPE
|
1024
|
xTaskResumeFromISR (xTaskHandle pxTaskToResume)
|
1025
|
{
|
1026
|
portBASE_TYPE xYieldRequired = pdFALSE;
|
1027
|
tskTCB *pxTCB;
|
1028
|
|
1029
|
pxTCB = (tskTCB *) pxTaskToResume;
|
1030
|
|
1031
|
/* Is the task we are attempting to resume actually suspended? */
|
1032
|
if (listIS_CONTAINED_WITHIN
|
1033
|
(&xSuspendedTaskList, &(pxTCB->xGenericListItem)) != pdFALSE)
|
1034
|
{
|
1035
|
/* Has the task already been resumed from within an ISR? */
|
1036
|
if (listIS_CONTAINED_WITHIN
|
1037
|
(&xPendingReadyList, &(pxTCB->xEventListItem)) != pdTRUE)
|
1038
|
{
|
1039
|
if (uxSchedulerSuspended == (unsigned portBASE_TYPE) pdFALSE)
|
1040
|
{
|
1041
|
xYieldRequired =
|
1042
|
(pxTCB->uxPriority >= pxCurrentTCB->uxPriority);
|
1043
|
vListRemove (&(pxTCB->xGenericListItem));
|
1044
|
prvAddTaskToReadyQueue (pxTCB);
|
1045
|
}
|
1046
|
else
|
1047
|
{
|
1048
|
/* We cannot access the delayed or ready lists, so will hold this
|
1049
|
task pending until the scheduler is resumed, at which point a
|
1050
|
yield will be preformed if necessary. */
|
1051
|
vListInsertEnd ((xList *) & (xPendingReadyList),
|
1052
|
&(pxTCB->xEventListItem));
|
1053
|
}
|
1054
|
}
|
1055
|
}
|
1056
|
|
1057
|
return xYieldRequired;
|
1058
|
}
|
1059
|
|
1060
|
#endif
|
1061
|
|
1062
|
|
1063
|
|
1064
|
|
1065
|
/*-----------------------------------------------------------
|
1066
|
* PUBLIC SCHEDULER CONTROL documented in task.h
|
1067
|
*----------------------------------------------------------*/
|
1068
|
|
1069
|
|
1070
|
void
|
1071
|
vTaskStartScheduler (void)
|
1072
|
{
|
1073
|
portBASE_TYPE xReturn;
|
1074
|
|
1075
|
/* Add the idle task at the lowest priority. */
|
1076
|
xReturn =
|
1077
|
xTaskCreate (prvIdleTask, (signed portCHAR *) "IDLE", tskIDLE_STACK_SIZE,
|
1078
|
(void *) NULL, tskIDLE_PRIORITY, (xTaskHandle *) NULL);
|
1079
|
|
1080
|
if (xReturn == pdPASS)
|
1081
|
{
|
1082
|
/* Interrupts are turned off here, to ensure a tick does not occur
|
1083
|
before or during the call to xPortStartScheduler(). The stacks of
|
1084
|
the created tasks contain a status word with interrupts switched on
|
1085
|
so interrupts will automatically get re-enabled when the first task
|
1086
|
starts to run.
|
1087
|
|
1088
|
STEPPING THROUGH HERE USING A DEBUGGER CAN CAUSE BIG PROBLEMS IF THE
|
1089
|
DEBUGGER ALLOWS INTERRUPTS TO BE PROCESSED. */
|
1090
|
portDISABLE_INTERRUPTS ();
|
1091
|
|
1092
|
xSchedulerRunning = pdTRUE;
|
1093
|
xTickCount = (portTickType) 0;
|
1094
|
|
1095
|
/* Setting up the timer tick is hardware specific and thus in the
|
1096
|
portable interface. */
|
1097
|
if (xPortStartScheduler ())
|
1098
|
{
|
1099
|
/* Should not reach here as if the scheduler is running the
|
1100
|
function will not return. */
|
1101
|
}
|
1102
|
else
|
1103
|
{
|
1104
|
/* Should only reach here if a task calls xTaskEndScheduler(). */
|
1105
|
}
|
1106
|
}
|
1107
|
}
|
1108
|
|
1109
|
/*-----------------------------------------------------------*/
|
1110
|
|
1111
|
void
|
1112
|
vTaskEndScheduler (void)
|
1113
|
{
|
1114
|
/* Stop the scheduler interrupts and call the portable scheduler end
|
1115
|
routine so the original ISRs can be restored if necessary. The port
|
1116
|
layer must ensure interrupts enable bit is left in the correct state. */
|
1117
|
portDISABLE_INTERRUPTS ();
|
1118
|
xSchedulerRunning = pdFALSE;
|
1119
|
vPortEndScheduler ();
|
1120
|
}
|
1121
|
|
1122
|
/*----------------------------------------------------------*/
|
1123
|
|
1124
|
void
|
1125
|
vTaskSuspendAll (void)
|
1126
|
{
|
1127
|
portENTER_CRITICAL ();
|
1128
|
++uxSchedulerSuspended;
|
1129
|
portEXIT_CRITICAL ();
|
1130
|
}
|
1131
|
|
1132
|
/*----------------------------------------------------------*/
|
1133
|
|
1134
|
signed portBASE_TYPE
|
1135
|
xTaskResumeAll (void)
|
1136
|
{
|
1137
|
register tskTCB *pxTCB;
|
1138
|
signed portBASE_TYPE xAlreadyYielded = pdFALSE;
|
1139
|
|
1140
|
/* It is possible that an ISR caused a task to be removed from an event
|
1141
|
list while the scheduler was suspended. If this was the case then the
|
1142
|
removed task will have been added to the xPendingReadyList. Once the
|
1143
|
scheduler has been resumed it is safe to move all the pending ready
|
1144
|
tasks from this list into their appropriate ready list. */
|
1145
|
portENTER_CRITICAL ();
|
1146
|
{
|
1147
|
--uxSchedulerSuspended;
|
1148
|
|
1149
|
if (uxSchedulerSuspended == (unsigned portBASE_TYPE) pdFALSE)
|
1150
|
{
|
1151
|
if (uxCurrentNumberOfTasks > (unsigned portBASE_TYPE) 0)
|
1152
|
{
|
1153
|
portBASE_TYPE xYieldRequired = pdFALSE;
|
1154
|
|
1155
|
/* Move any readied tasks from the pending list into the
|
1156
|
appropriate ready list. */
|
1157
|
while ((pxTCB =
|
1158
|
(tskTCB *)
|
1159
|
listGET_OWNER_OF_HEAD_ENTRY (((xList *) &
|
1160
|
xPendingReadyList))) !=
|
1161
|
NULL)
|
1162
|
{
|
1163
|
vListRemove (&(pxTCB->xEventListItem));
|
1164
|
vListRemove (&(pxTCB->xGenericListItem));
|
1165
|
prvAddTaskToReadyQueue (pxTCB);
|
1166
|
|
1167
|
/* If we have moved a task that has a priority higher than
|
1168
|
the current task then we should yield. */
|
1169
|
if (pxTCB->uxPriority >= pxCurrentTCB->uxPriority)
|
1170
|
{
|
1171
|
xYieldRequired = pdTRUE;
|
1172
|
}
|
1173
|
}
|
1174
|
|
1175
|
/* If any ticks occurred while the scheduler was suspended then
|
1176
|
they should be processed now. This ensures the tick count does not
|
1177
|
slip, and that any delayed tasks are resumed at the correct time. */
|
1178
|
if (uxMissedTicks > (unsigned portBASE_TYPE) 0)
|
1179
|
{
|
1180
|
while (uxMissedTicks > (unsigned portBASE_TYPE) 0)
|
1181
|
{
|
1182
|
vTaskIncrementTick ();
|
1183
|
--uxMissedTicks;
|
1184
|
}
|
1185
|
|
1186
|
/* As we have processed some ticks it is appropriate to yield
|
1187
|
to ensure the highest priority task that is ready to run is
|
1188
|
the task actually running. */
|
1189
|
xYieldRequired = pdTRUE;
|
1190
|
}
|
1191
|
|
1192
|
if ((xYieldRequired == pdTRUE) || (xMissedYield == pdTRUE))
|
1193
|
{
|
1194
|
xAlreadyYielded = pdTRUE;
|
1195
|
xMissedYield = pdFALSE;
|
1196
|
taskYIELD ();
|
1197
|
}
|
1198
|
}
|
1199
|
}
|
1200
|
}
|
1201
|
portEXIT_CRITICAL ();
|
1202
|
|
1203
|
return xAlreadyYielded;
|
1204
|
}
|
1205
|
|
1206
|
|
1207
|
|
1208
|
|
1209
|
|
1210
|
|
1211
|
/*-----------------------------------------------------------
|
1212
|
* PUBLIC TASK UTILITIES documented in task.h
|
1213
|
*----------------------------------------------------------*/
|
1214
|
|
1215
|
|
1216
|
|
1217
|
portTickType
|
1218
|
xTaskGetTickCount (void)
|
1219
|
{
|
1220
|
portTickType xTicks;
|
1221
|
|
1222
|
/* Critical section required if running on a 16 bit processor. */
|
1223
|
taskENTER_CRITICAL ();
|
1224
|
{
|
1225
|
xTicks = xTickCount;
|
1226
|
}
|
1227
|
taskEXIT_CRITICAL ();
|
1228
|
|
1229
|
return xTicks;
|
1230
|
}
|
1231
|
|
1232
|
/*-----------------------------------------------------------*/
|
1233
|
|
1234
|
unsigned portBASE_TYPE
|
1235
|
uxTaskGetNumberOfTasks (void)
|
1236
|
{
|
1237
|
unsigned portBASE_TYPE uxNumberOfTasks;
|
1238
|
|
1239
|
taskENTER_CRITICAL ();
|
1240
|
uxNumberOfTasks = uxCurrentNumberOfTasks;
|
1241
|
taskEXIT_CRITICAL ();
|
1242
|
|
1243
|
return uxNumberOfTasks;
|
1244
|
}
|
1245
|
|
1246
|
/*-----------------------------------------------------------*/
|
1247
|
|
1248
|
#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_vTaskDelete == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) )
|
1249
|
|
1250
|
void
|
1251
|
vTaskList (signed portCHAR * pcWriteBuffer)
|
1252
|
{
|
1253
|
unsigned portBASE_TYPE uxQueue;
|
1254
|
|
1255
|
/* This is a VERY costly function that should be used for debug only.
|
1256
|
It leaves interrupts disabled for a LONG time. */
|
1257
|
|
1258
|
vTaskSuspendAll ();
|
1259
|
{
|
1260
|
/* Run through all the lists that could potentially contain a TCB and
|
1261
|
report the task name, state and stack high water mark. */
|
1262
|
|
1263
|
pcWriteBuffer[0] = (signed portCHAR) 0x00;
|
1264
|
strcat ((portCHAR *) pcWriteBuffer, (const portCHAR *) "\r\n");
|
1265
|
|
1266
|
uxQueue = uxTopUsedPriority + 1;
|
1267
|
|
1268
|
do
|
1269
|
{
|
1270
|
uxQueue--;
|
1271
|
|
1272
|
if (!listLIST_IS_EMPTY (&(pxReadyTasksLists[uxQueue])))
|
1273
|
{
|
1274
|
prvListTaskWithinSingleList (pcWriteBuffer,
|
1275
|
(xList *) &
|
1276
|
(pxReadyTasksLists[uxQueue]),
|
1277
|
tskREADY_CHAR);
|
1278
|
}
|
1279
|
}
|
1280
|
while (uxQueue > (unsigned portSHORT) tskIDLE_PRIORITY);
|
1281
|
|
1282
|
if (!listLIST_IS_EMPTY (pxDelayedTaskList))
|
1283
|
{
|
1284
|
prvListTaskWithinSingleList (pcWriteBuffer,
|
1285
|
(xList *) pxDelayedTaskList,
|
1286
|
tskBLOCKED_CHAR);
|
1287
|
}
|
1288
|
|
1289
|
if (!listLIST_IS_EMPTY (pxOverflowDelayedTaskList))
|
1290
|
{
|
1291
|
prvListTaskWithinSingleList (pcWriteBuffer,
|
1292
|
(xList *) pxOverflowDelayedTaskList,
|
1293
|
tskBLOCKED_CHAR);
|
1294
|
}
|
1295
|
|
1296
|
if (!listLIST_IS_EMPTY (&xTasksWaitingTermination))
|
1297
|
{
|
1298
|
prvListTaskWithinSingleList (pcWriteBuffer,
|
1299
|
(xList *) & xTasksWaitingTermination,
|
1300
|
tskDELETED_CHAR);
|
1301
|
}
|
1302
|
|
1303
|
if (!listLIST_IS_EMPTY (&xSuspendedTaskList))
|
1304
|
{
|
1305
|
prvListTaskWithinSingleList (pcWriteBuffer,
|
1306
|
(xList *) & xSuspendedTaskList,
|
1307
|
tskSUSPENDED_CHAR);
|
1308
|
}
|
1309
|
}
|
1310
|
xTaskResumeAll ();
|
1311
|
}
|
1312
|
|
1313
|
#endif
|
1314
|
/*----------------------------------------------------------*/
|
1315
|
|
1316
|
#if ( configUSE_TRACE_FACILITY == 1 )
|
1317
|
|
1318
|
void
|
1319
|
vTaskStartTrace (signed portCHAR * pcBuffer, unsigned portLONG ulBufferSize)
|
1320
|
{
|
1321
|
portENTER_CRITICAL ();
|
1322
|
{
|
1323
|
pcTraceBuffer = (volatile signed portCHAR * volatile) pcBuffer;
|
1324
|
pcTraceBufferStart = pcBuffer;
|
1325
|
pcTraceBufferEnd = pcBuffer + (ulBufferSize - tskSIZE_OF_EACH_TRACE_LINE);
|
1326
|
xTracing = pdTRUE;
|
1327
|
}
|
1328
|
portEXIT_CRITICAL ();
|
1329
|
}
|
1330
|
|
1331
|
#endif
|
1332
|
/*----------------------------------------------------------*/
|
1333
|
|
1334
|
#if ( configUSE_TRACE_FACILITY == 1 )
|
1335
|
|
1336
|
unsigned portLONG
|
1337
|
ulTaskEndTrace (void)
|
1338
|
{
|
1339
|
unsigned portLONG ulBufferLength;
|
1340
|
|
1341
|
portENTER_CRITICAL ();
|
1342
|
xTracing = pdFALSE;
|
1343
|
portEXIT_CRITICAL ();
|
1344
|
|
1345
|
ulBufferLength = (unsigned portLONG) (pcTraceBuffer - pcTraceBufferStart);
|
1346
|
|
1347
|
return ulBufferLength;
|
1348
|
}
|
1349
|
|
1350
|
#endif
|
1351
|
|
1352
|
|
1353
|
|
1354
|
/*-----------------------------------------------------------
|
1355
|
* SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES
|
1356
|
* documented in task.h
|
1357
|
*----------------------------------------------------------*/
|
1358
|
|
1359
|
|
1360
|
inline void
|
1361
|
vTaskIncrementTick (void)
|
1362
|
{
|
1363
|
/* Called by the portable layer each time a tick interrupt occurs.
|
1364
|
Increments the tick then checks to see if the new tick value will cause any
|
1365
|
tasks to be unblocked. */
|
1366
|
if (uxSchedulerSuspended == (unsigned portBASE_TYPE) pdFALSE)
|
1367
|
{
|
1368
|
++xTickCount;
|
1369
|
if (xTickCount == (portTickType) 0)
|
1370
|
{
|
1371
|
xList *pxTemp;
|
1372
|
|
1373
|
/* Tick count has overflowed so we need to swap the delay lists.
|
1374
|
If there are any items in pxDelayedTaskList here then there is
|
1375
|
an error! */
|
1376
|
pxTemp = pxDelayedTaskList;
|
1377
|
pxDelayedTaskList = pxOverflowDelayedTaskList;
|
1378
|
pxOverflowDelayedTaskList = pxTemp;
|
1379
|
xNumOfOverflows++;
|
1380
|
}
|
1381
|
|
1382
|
/* See if this tick has made a timeout expire. */
|
1383
|
prvCheckDelayedTasks ();
|
1384
|
}
|
1385
|
else
|
1386
|
{
|
1387
|
++uxMissedTicks;
|
1388
|
|
1389
|
/* The tick hook gets called at regular intervals, even if the
|
1390
|
scheduler is locked. */
|
1391
|
#if ( configUSE_TICK_HOOK == 1 )
|
1392
|
{
|
1393
|
extern void vApplicationTickHook (void);
|
1394
|
|
1395
|
vApplicationTickHook ();
|
1396
|
}
|
1397
|
#endif
|
1398
|
}
|
1399
|
|
1400
|
#if ( configUSE_TICK_HOOK == 1 )
|
1401
|
{
|
1402
|
extern void vApplicationTickHook (void);
|
1403
|
|
1404
|
/* Guard against the tick hook being called when the missed tick
|
1405
|
count is being unwound (when the scheduler is being unlocked. */
|
1406
|
if (uxMissedTicks == 0)
|
1407
|
{
|
1408
|
vApplicationTickHook ();
|
1409
|
}
|
1410
|
}
|
1411
|
#endif
|
1412
|
}
|
1413
|
|
1414
|
/*-----------------------------------------------------------*/
|
1415
|
|
1416
|
#if ( ( INCLUDE_vTaskCleanUpResources == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) )
|
1417
|
|
1418
|
void
|
1419
|
vTaskCleanUpResources (void)
|
1420
|
{
|
1421
|
unsigned portSHORT usQueue;
|
1422
|
volatile tskTCB *pxTCB;
|
1423
|
|
1424
|
usQueue = (unsigned portSHORT) uxTopUsedPriority + (unsigned portSHORT) 1;
|
1425
|
|
1426
|
/* Remove any TCB's from the ready queues. */
|
1427
|
do
|
1428
|
{
|
1429
|
usQueue--;
|
1430
|
|
1431
|
while (!listLIST_IS_EMPTY (&(pxReadyTasksLists[usQueue])))
|
1432
|
{
|
1433
|
listGET_OWNER_OF_NEXT_ENTRY (pxTCB, &(pxReadyTasksLists[usQueue]));
|
1434
|
vListRemove ((xListItem *) & (pxTCB->xGenericListItem));
|
1435
|
|
1436
|
prvDeleteTCB ((tskTCB *) pxTCB);
|
1437
|
}
|
1438
|
}
|
1439
|
while (usQueue > (unsigned portSHORT) tskIDLE_PRIORITY);
|
1440
|
|
1441
|
/* Remove any TCB's from the delayed queue. */
|
1442
|
while (!listLIST_IS_EMPTY (&xDelayedTaskList1))
|
1443
|
{
|
1444
|
listGET_OWNER_OF_NEXT_ENTRY (pxTCB, &xDelayedTaskList1);
|
1445
|
vListRemove ((xListItem *) & (pxTCB->xGenericListItem));
|
1446
|
|
1447
|
prvDeleteTCB ((tskTCB *) pxTCB);
|
1448
|
}
|
1449
|
|
1450
|
/* Remove any TCB's from the overflow delayed queue. */
|
1451
|
while (!listLIST_IS_EMPTY (&xDelayedTaskList2))
|
1452
|
{
|
1453
|
listGET_OWNER_OF_NEXT_ENTRY (pxTCB, &xDelayedTaskList2);
|
1454
|
vListRemove ((xListItem *) & (pxTCB->xGenericListItem));
|
1455
|
|
1456
|
prvDeleteTCB ((tskTCB *) pxTCB);
|
1457
|
}
|
1458
|
|
1459
|
while (!listLIST_IS_EMPTY (&xSuspendedTaskList))
|
1460
|
{
|
1461
|
listGET_OWNER_OF_NEXT_ENTRY (pxTCB, &xSuspendedTaskList);
|
1462
|
vListRemove ((xListItem *) & (pxTCB->xGenericListItem));
|
1463
|
|
1464
|
prvDeleteTCB ((tskTCB *) pxTCB);
|
1465
|
}
|
1466
|
|
1467
|
while (!listLIST_IS_EMPTY (&xPendingReadyList))
|
1468
|
{
|
1469
|
listGET_OWNER_OF_NEXT_ENTRY (pxTCB, &xPendingReadyList);
|
1470
|
vListRemove ((xListItem *) & (pxTCB->xGenericListItem));
|
1471
|
|
1472
|
prvDeleteTCB ((tskTCB *) pxTCB);
|
1473
|
}
|
1474
|
}
|
1475
|
|
1476
|
#endif
|
1477
|
/*-----------------------------------------------------------*/
|
1478
|
|
1479
|
void
|
1480
|
vTaskSwitchContext (void)
|
1481
|
{
|
1482
|
if (uxSchedulerSuspended != (unsigned portBASE_TYPE) pdFALSE)
|
1483
|
{
|
1484
|
/* The scheduler is currently suspended - do not allow a context
|
1485
|
switch. */
|
1486
|
xMissedYield = pdTRUE;
|
1487
|
return;
|
1488
|
}
|
1489
|
|
1490
|
/* Find the highest priority queue that contains ready tasks. */
|
1491
|
while (listLIST_IS_EMPTY (&(pxReadyTasksLists[uxTopReadyPriority])))
|
1492
|
{
|
1493
|
--uxTopReadyPriority;
|
1494
|
}
|
1495
|
|
1496
|
/* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the tasks of the
|
1497
|
same priority get an equal share of the processor time. */
|
1498
|
listGET_OWNER_OF_NEXT_ENTRY (pxCurrentTCB,
|
1499
|
&(pxReadyTasksLists[uxTopReadyPriority]));
|
1500
|
vWriteTraceToBuffer ();
|
1501
|
}
|
1502
|
|
1503
|
/*-----------------------------------------------------------*/
|
1504
|
|
1505
|
void
|
1506
|
vTaskPlaceOnEventList (xList * pxEventList, portTickType xTicksToWait)
|
1507
|
{
|
1508
|
portTickType xTimeToWake;
|
1509
|
|
1510
|
/* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED OR THE
|
1511
|
SCHEDULER SUSPENDED. */
|
1512
|
|
1513
|
/* Place the event list item of the TCB in the appropriate event list.
|
1514
|
This is placed in the list in priority order so the highest priority task
|
1515
|
is the first to be woken by the event. */
|
1516
|
vListInsert ((xList *) pxEventList,
|
1517
|
(xListItem *) & (pxCurrentTCB->xEventListItem));
|
1518
|
|
1519
|
/* We must remove ourselves from the ready list before adding ourselves
|
1520
|
to the blocked list as the same list item is used for both lists. We have
|
1521
|
exclusive access to the ready lists as the scheduler is locked. */
|
1522
|
vListRemove ((xListItem *) & (pxCurrentTCB->xGenericListItem));
|
1523
|
|
1524
|
|
1525
|
#if ( INCLUDE_vTaskSuspend == 1 )
|
1526
|
{
|
1527
|
if (xTicksToWait == portMAX_DELAY)
|
1528
|
{
|
1529
|
/* Add ourselves to the suspended task list instead of a delayed task
|
1530
|
list to ensure we are not woken by a timing event. We will block
|
1531
|
indefinitely. */
|
1532
|
vListInsertEnd ((xList *) & xSuspendedTaskList,
|
1533
|
(xListItem *) & (pxCurrentTCB->xGenericListItem));
|
1534
|
}
|
1535
|
else
|
1536
|
{
|
1537
|
/* Calculate the time at which the task should be woken if the event does
|
1538
|
not occur. This may overflow but this doesn't matter. */
|
1539
|
xTimeToWake = xTickCount + xTicksToWait;
|
1540
|
|
1541
|
listSET_LIST_ITEM_VALUE (&(pxCurrentTCB->xGenericListItem),
|
1542
|
xTimeToWake);
|
1543
|
|
1544
|
if (xTimeToWake < xTickCount)
|
1545
|
{
|
1546
|
/* Wake time has overflowed. Place this item in the overflow list. */
|
1547
|
vListInsert ((xList *) pxOverflowDelayedTaskList,
|
1548
|
(xListItem *) & (pxCurrentTCB->xGenericListItem));
|
1549
|
}
|
1550
|
else
|
1551
|
{
|
1552
|
/* The wake time has not overflowed, so we can use the current block list. */
|
1553
|
vListInsert ((xList *) pxDelayedTaskList,
|
1554
|
(xListItem *) & (pxCurrentTCB->xGenericListItem));
|
1555
|
}
|
1556
|
}
|
1557
|
}
|
1558
|
#else
|
1559
|
{
|
1560
|
/* Calculate the time at which the task should be woken if the event does
|
1561
|
not occur. This may overflow but this doesn't matter. */
|
1562
|
xTimeToWake = xTickCount + xTicksToWait;
|
1563
|
|
1564
|
listSET_LIST_ITEM_VALUE (&(pxCurrentTCB->xGenericListItem), xTimeToWake);
|
1565
|
|
1566
|
if (xTimeToWake < xTickCount)
|
1567
|
{
|
1568
|
/* Wake time has overflowed. Place this item in the overflow list. */
|
1569
|
vListInsert ((xList *) pxOverflowDelayedTaskList,
|
1570
|
(xListItem *) & (pxCurrentTCB->xGenericListItem));
|
1571
|
}
|
1572
|
else
|
1573
|
{
|
1574
|
/* The wake time has not overflowed, so we can use the current block list. */
|
1575
|
vListInsert ((xList *) pxDelayedTaskList,
|
1576
|
(xListItem *) & (pxCurrentTCB->xGenericListItem));
|
1577
|
}
|
1578
|
}
|
1579
|
#endif
|
1580
|
}
|
1581
|
|
1582
|
/*-----------------------------------------------------------*/
|
1583
|
|
1584
|
signed portBASE_TYPE
|
1585
|
xTaskRemoveFromEventList (const xList * pxEventList)
|
1586
|
{
|
1587
|
tskTCB *pxUnblockedTCB;
|
1588
|
portBASE_TYPE xReturn;
|
1589
|
|
1590
|
/* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED OR THE
|
1591
|
SCHEDULER SUSPENDED. It can also be called from within an ISR. */
|
1592
|
|
1593
|
/* The event list is sorted in priority order, so we can remove the
|
1594
|
first in the list, remove the TCB from the delayed list, and add
|
1595
|
it to the ready list.
|
1596
|
|
1597
|
If an event is for a queue that is locked then this function will never
|
1598
|
get called - the lock count on the queue will get modified instead. This
|
1599
|
means we can always expect exclusive access to the event list here. */
|
1600
|
pxUnblockedTCB = (tskTCB *) listGET_OWNER_OF_HEAD_ENTRY (pxEventList);
|
1601
|
vListRemove (&(pxUnblockedTCB->xEventListItem));
|
1602
|
|
1603
|
if (uxSchedulerSuspended == (unsigned portBASE_TYPE) pdFALSE)
|
1604
|
{
|
1605
|
vListRemove (&(pxUnblockedTCB->xGenericListItem));
|
1606
|
prvAddTaskToReadyQueue (pxUnblockedTCB);
|
1607
|
}
|
1608
|
else
|
1609
|
{
|
1610
|
/* We cannot access the delayed or ready lists, so will hold this
|
1611
|
task pending until the scheduler is resumed. */
|
1612
|
vListInsertEnd ((xList *) & (xPendingReadyList),
|
1613
|
&(pxUnblockedTCB->xEventListItem));
|
1614
|
}
|
1615
|
|
1616
|
if (pxUnblockedTCB->uxPriority >= pxCurrentTCB->uxPriority)
|
1617
|
{
|
1618
|
/* Return true if the task removed from the event list has
|
1619
|
a higher priority than the calling task. This allows
|
1620
|
the calling task to know if it should force a context
|
1621
|
switch now. */
|
1622
|
xReturn = pdTRUE;
|
1623
|
}
|
1624
|
else
|
1625
|
{
|
1626
|
xReturn = pdFALSE;
|
1627
|
}
|
1628
|
|
1629
|
return xReturn;
|
1630
|
}
|
1631
|
|
1632
|
/*-----------------------------------------------------------*/
|
1633
|
|
1634
|
void
|
1635
|
vTaskSetTimeOutState (xTimeOutType * pxTimeOut)
|
1636
|
{
|
1637
|
pxTimeOut->xOverflowCount = xNumOfOverflows;
|
1638
|
pxTimeOut->xTimeOnEntering = xTickCount;
|
1639
|
}
|
1640
|
|
1641
|
/*-----------------------------------------------------------*/
|
1642
|
|
1643
|
portBASE_TYPE
|
1644
|
xTaskCheckForTimeOut (xTimeOutType * pxTimeOut, portTickType * pxTicksToWait)
|
1645
|
{
|
1646
|
portBASE_TYPE xReturn;
|
1647
|
|
1648
|
if ((xNumOfOverflows != pxTimeOut->xOverflowCount)
|
1649
|
&& (xTickCount >= pxTimeOut->xTimeOnEntering))
|
1650
|
{
|
1651
|
/* The tick count is greater than the time at which vTaskSetTimeout()
|
1652
|
was called, but has also overflowed since vTaskSetTimeOut() was called.
|
1653
|
It must have wrapped all the way around and gone past us again. This
|
1654
|
passed since vTaskSetTimeout() was called. */
|
1655
|
xReturn = pdTRUE;
|
1656
|
}
|
1657
|
else if ((xTickCount - pxTimeOut->xTimeOnEntering) < *pxTicksToWait)
|
1658
|
{
|
1659
|
/* Not a genuine timeout. Adjust parameters for time remaining. */
|
1660
|
*pxTicksToWait -= (xTickCount - pxTimeOut->xTimeOnEntering);
|
1661
|
vTaskSetTimeOutState (pxTimeOut);
|
1662
|
xReturn = pdFALSE;
|
1663
|
}
|
1664
|
else
|
1665
|
{
|
1666
|
xReturn = pdTRUE;
|
1667
|
}
|
1668
|
|
1669
|
return xReturn;
|
1670
|
}
|
1671
|
|
1672
|
/*-----------------------------------------------------------*/
|
1673
|
|
1674
|
void
|
1675
|
vTaskMissedYield (void)
|
1676
|
{
|
1677
|
xMissedYield = pdTRUE;
|
1678
|
}
|
1679
|
|
1680
|
/*
|
1681
|
* -----------------------------------------------------------
|
1682
|
* The Idle task.
|
1683
|
* ----------------------------------------------------------
|
1684
|
*
|
1685
|
* The portTASK_FUNCTION() macro is used to allow port/compiler specific
|
1686
|
* language extensions. The equivalent prototype for this function is:
|
1687
|
*
|
1688
|
* void prvIdleTask( void *pvParameters );
|
1689
|
*
|
1690
|
*/
|
1691
|
static
|
1692
|
portTASK_FUNCTION (prvIdleTask, pvParameters)
|
1693
|
{
|
1694
|
/* Stop warnings. */
|
1695
|
(void) pvParameters;
|
1696
|
|
1697
|
for (;;)
|
1698
|
{
|
1699
|
/* See if any tasks have been deleted. */
|
1700
|
prvCheckTasksWaitingTermination ();
|
1701
|
|
1702
|
#if ( configUSE_PREEMPTION == 0 )
|
1703
|
{
|
1704
|
/* If we are not using preemption we keep forcing a task switch to
|
1705
|
see if any other task has become available. If we are using
|
1706
|
preemption we don't need to do this as any task becoming available
|
1707
|
will automatically get the processor anyway. */
|
1708
|
taskYIELD ();
|
1709
|
}
|
1710
|
#endif
|
1711
|
|
1712
|
#if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) )
|
1713
|
{
|
1714
|
/* When using preemption tasks of equal priority will be
|
1715
|
timesliced. If a task that is sharing the idle priority is ready
|
1716
|
to run then the idle task should yield before the end of the
|
1717
|
timeslice.
|
1718
|
|
1719
|
A critical region is not required here as we are just reading from
|
1720
|
the list, and an occasional incorrect value will not matter. If
|
1721
|
the ready list at the idle priority contains more than one task
|
1722
|
then a task other than the idle task is ready to execute. */
|
1723
|
if (listCURRENT_LIST_LENGTH (&(pxReadyTasksLists[tskIDLE_PRIORITY])) >
|
1724
|
(unsigned portBASE_TYPE) 1)
|
1725
|
{
|
1726
|
taskYIELD ();
|
1727
|
}
|
1728
|
}
|
1729
|
#endif
|
1730
|
|
1731
|
#if ( configUSE_IDLE_HOOK == 1 )
|
1732
|
{
|
1733
|
extern void vApplicationIdleHook (void);
|
1734
|
|
1735
|
/* Call the user defined function from within the idle task. This
|
1736
|
allows the application designer to add background functionality
|
1737
|
without the overhead of a separate task.
|
1738
|
NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,
|
1739
|
CALL A FUNCTION THAT MIGHT BLOCK. */
|
1740
|
vApplicationIdleHook ();
|
1741
|
}
|
1742
|
#endif
|
1743
|
}
|
1744
|
} /*lint !e715 pvParameters is not accessed but all task functions require the same prototype. */
|
1745
|
|
1746
|
|
1747
|
|
1748
|
|
1749
|
|
1750
|
|
1751
|
|
1752
|
/*-----------------------------------------------------------
|
1753
|
* File private functions documented at the top of the file.
|
1754
|
*----------------------------------------------------------*/
|
1755
|
|
1756
|
|
1757
|
|
1758
|
static void
|
1759
|
prvInitialiseTCBVariables (tskTCB * pxTCB, unsigned portSHORT usStackDepth,
|
1760
|
const signed portCHAR * const pcName,
|
1761
|
unsigned portBASE_TYPE uxPriority)
|
1762
|
{
|
1763
|
pxTCB->usStackDepth = usStackDepth;
|
1764
|
|
1765
|
/* Store the function name in the TCB. */
|
1766
|
strncpy ((char *) pxTCB->pcTaskName, (const char *) pcName,
|
1767
|
(unsigned portSHORT) configMAX_TASK_NAME_LEN);
|
1768
|
pxTCB->pcTaskName[(unsigned portSHORT) configMAX_TASK_NAME_LEN -
|
1769
|
(unsigned portSHORT) 1] = '\0';
|
1770
|
|
1771
|
/* This is used as an array index so must ensure it's not too large. */
|
1772
|
if (uxPriority >= configMAX_PRIORITIES)
|
1773
|
{
|
1774
|
uxPriority = configMAX_PRIORITIES - 1;
|
1775
|
}
|
1776
|
|
1777
|
pxTCB->uxPriority = uxPriority;
|
1778
|
|
1779
|
vListInitialiseItem (&(pxTCB->xGenericListItem));
|
1780
|
vListInitialiseItem (&(pxTCB->xEventListItem));
|
1781
|
|
1782
|
/* Set the pxTCB as a link back from the xListItem. This is so we can get
|
1783
|
back to the containing TCB from a generic item in a list. */
|
1784
|
listSET_LIST_ITEM_OWNER (&(pxTCB->xGenericListItem), pxTCB);
|
1785
|
|
1786
|
/* Event lists are always in priority order. */
|
1787
|
listSET_LIST_ITEM_VALUE (&(pxTCB->xEventListItem),
|
1788
|
configMAX_PRIORITIES - (portTickType) uxPriority);
|
1789
|
listSET_LIST_ITEM_OWNER (&(pxTCB->xEventListItem), pxTCB);
|
1790
|
}
|
1791
|
|
1792
|
/*-----------------------------------------------------------*/
|
1793
|
|
1794
|
static void
|
1795
|
prvInitialiseTaskLists (void)
|
1796
|
{
|
1797
|
unsigned portBASE_TYPE uxPriority;
|
1798
|
|
1799
|
for (uxPriority = 0; uxPriority < configMAX_PRIORITIES; uxPriority++)
|
1800
|
{
|
1801
|
vListInitialise ((xList *) & (pxReadyTasksLists[uxPriority]));
|
1802
|
}
|
1803
|
|
1804
|
vListInitialise ((xList *) & xDelayedTaskList1);
|
1805
|
vListInitialise ((xList *) & xDelayedTaskList2);
|
1806
|
vListInitialise ((xList *) & xPendingReadyList);
|
1807
|
|
1808
|
#if ( INCLUDE_vTaskDelete == 1 )
|
1809
|
{
|
1810
|
vListInitialise ((xList *) & xTasksWaitingTermination);
|
1811
|
}
|
1812
|
#endif
|
1813
|
|
1814
|
#if ( INCLUDE_vTaskSuspend == 1 )
|
1815
|
{
|
1816
|
vListInitialise ((xList *) & xSuspendedTaskList);
|
1817
|
}
|
1818
|
#endif
|
1819
|
|
1820
|
/* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList
|
1821
|
using list2. */
|
1822
|
pxDelayedTaskList = &xDelayedTaskList1;
|
1823
|
pxOverflowDelayedTaskList = &xDelayedTaskList2;
|
1824
|
}
|
1825
|
|
1826
|
/*-----------------------------------------------------------*/
|
1827
|
|
1828
|
static void
|
1829
|
prvCheckTasksWaitingTermination (void)
|
1830
|
{
|
1831
|
#if ( INCLUDE_vTaskDelete == 1 )
|
1832
|
{
|
1833
|
portBASE_TYPE xListIsEmpty;
|
1834
|
|
1835
|
/* ucTasksDeleted is used to prevent vTaskSuspendAll() being called
|
1836
|
too often in the idle task. */
|
1837
|
if (uxTasksDeleted > (unsigned portBASE_TYPE) 0)
|
1838
|
{
|
1839
|
vTaskSuspendAll ();
|
1840
|
xListIsEmpty = listLIST_IS_EMPTY (&xTasksWaitingTermination);
|
1841
|
xTaskResumeAll ();
|
1842
|
|
1843
|
if (!xListIsEmpty)
|
1844
|
{
|
1845
|
tskTCB *pxTCB;
|
1846
|
|
1847
|
portENTER_CRITICAL ();
|
1848
|
{
|
1849
|
pxTCB =
|
1850
|
(tskTCB *)
|
1851
|
listGET_OWNER_OF_HEAD_ENTRY (((xList *) &
|
1852
|
xTasksWaitingTermination));
|
1853
|
vListRemove (&(pxTCB->xGenericListItem));
|
1854
|
--uxCurrentNumberOfTasks;
|
1855
|
--uxTasksDeleted;
|
1856
|
}
|
1857
|
portEXIT_CRITICAL ();
|
1858
|
|
1859
|
prvDeleteTCB (pxTCB);
|
1860
|
}
|
1861
|
}
|
1862
|
}
|
1863
|
#endif
|
1864
|
}
|
1865
|
|
1866
|
/*-----------------------------------------------------------*/
|
1867
|
|
1868
|
static tskTCB *
|
1869
|
prvAllocateTCBAndStack (unsigned portSHORT usStackDepth)
|
1870
|
{
|
1871
|
tskTCB *pxNewTCB;
|
1872
|
|
1873
|
/* Allocate space for the TCB. Where the memory comes from depends on
|
1874
|
the implementation of the port malloc function. */
|
1875
|
pxNewTCB = (tskTCB *) pvPortMalloc (sizeof (tskTCB));
|
1876
|
|
1877
|
if (pxNewTCB != NULL)
|
1878
|
{
|
1879
|
/* Allocate space for the stack used by the task being created.
|
1880
|
The base of the stack memory stored in the TCB so the task can
|
1881
|
be deleted later if required. */
|
1882
|
pxNewTCB->pxStack =
|
1883
|
(portSTACK_TYPE *) pvPortMalloc (((size_t) usStackDepth) *
|
1884
|
sizeof (portSTACK_TYPE));
|
1885
|
|
1886
|
if (pxNewTCB->pxStack == NULL)
|
1887
|
{
|
1888
|
/* Could not allocate the stack. Delete the allocated TCB. */
|
1889
|
vPortFree (pxNewTCB);
|
1890
|
pxNewTCB = NULL;
|
1891
|
}
|
1892
|
else
|
1893
|
{
|
1894
|
/* Just to help debugging. */
|
1895
|
memset (pxNewTCB->pxStack, tskSTACK_FILL_BYTE,
|
1896
|
usStackDepth * sizeof (portSTACK_TYPE));
|
1897
|
}
|
1898
|
}
|
1899
|
|
1900
|
return pxNewTCB;
|
1901
|
}
|
1902
|
|
1903
|
/*-----------------------------------------------------------*/
|
1904
|
|
1905
|
#if ( configUSE_TRACE_FACILITY == 1 )
|
1906
|
|
1907
|
static void
|
1908
|
prvListTaskWithinSingleList (signed portCHAR * pcWriteBuffer, xList * pxList,
|
1909
|
signed portCHAR cStatus)
|
1910
|
{
|
1911
|
volatile tskTCB *pxNextTCB, *pxFirstTCB;
|
1912
|
static portCHAR pcStatusString[50];
|
1913
|
unsigned portSHORT usStackRemaining;
|
1914
|
|
1915
|
/* Write the details of all the TCB's in pxList into the buffer. */
|
1916
|
listGET_OWNER_OF_NEXT_ENTRY (pxFirstTCB, pxList);
|
1917
|
do
|
1918
|
{
|
1919
|
listGET_OWNER_OF_NEXT_ENTRY (pxNextTCB, pxList);
|
1920
|
usStackRemaining =
|
1921
|
usTaskCheckFreeStackSpace ((unsigned portCHAR *) pxNextTCB->pxStack);
|
1922
|
sprintf (pcStatusString, (portCHAR *) "%s\t\t%c\t%u\t%u\t%u\r\n",
|
1923
|
pxNextTCB->pcTaskName, cStatus,
|
1924
|
(unsigned int) pxNextTCB->uxPriority, usStackRemaining,
|
1925
|
(unsigned int) pxNextTCB->uxTCBNumber);
|
1926
|
strcat ((portCHAR *) pcWriteBuffer, (portCHAR *) pcStatusString);
|
1927
|
|
1928
|
}
|
1929
|
while (pxNextTCB != pxFirstTCB);
|
1930
|
}
|
1931
|
|
1932
|
#endif
|
1933
|
/*-----------------------------------------------------------*/
|
1934
|
|
1935
|
#if ( configUSE_TRACE_FACILITY == 1 )
|
1936
|
unsigned portSHORT
|
1937
|
usTaskCheckFreeStackSpace (const unsigned portCHAR * pucStackByte)
|
1938
|
{
|
1939
|
register unsigned portSHORT usCount = 0;
|
1940
|
|
1941
|
while (*pucStackByte == tskSTACK_FILL_BYTE)
|
1942
|
{
|
1943
|
pucStackByte -= portSTACK_GROWTH;
|
1944
|
usCount++;
|
1945
|
}
|
1946
|
|
1947
|
usCount /= sizeof (portSTACK_TYPE);
|
1948
|
|
1949
|
return usCount;
|
1950
|
}
|
1951
|
#endif
|
1952
|
/*-----------------------------------------------------------*/
|
1953
|
|
1954
|
|
1955
|
|
1956
|
#if ( ( INCLUDE_vTaskDelete == 1 ) || ( INCLUDE_vTaskCleanUpResources == 1 ) )
|
1957
|
|
1958
|
static void
|
1959
|
prvDeleteTCB (tskTCB * pxTCB)
|
1960
|
{
|
1961
|
/* Free up the memory allocated by the scheduler for the task. It is up to
|
1962
|
the task to free any memory allocated at the application level. */
|
1963
|
vPortFree (pxTCB->pxStack);
|
1964
|
vPortFree (pxTCB);
|
1965
|
}
|
1966
|
|
1967
|
#endif
|
1968
|
|
1969
|
|
1970
|
/*-----------------------------------------------------------*/
|
1971
|
|
1972
|
#if ( INCLUDE_xTaskGetCurrentTaskHandle == 1 )
|
1973
|
|
1974
|
xTaskHandle
|
1975
|
xTaskGetCurrentTaskHandle (void)
|
1976
|
{
|
1977
|
xTaskHandle xReturn;
|
1978
|
|
1979
|
portENTER_CRITICAL ();
|
1980
|
{
|
1981
|
xReturn = (xTaskHandle) pxCurrentTCB;
|
1982
|
}
|
1983
|
portEXIT_CRITICAL ();
|
1984
|
|
1985
|
return xReturn;
|
1986
|
}
|
1987
|
|
1988
|
#endif
|