blob: 1b486bacd7e712257d2064cbb9826bc8fba7a668 [file] [log] [blame]
brians0ab60bb2013-01-31 02:21:51 +00001/*
2 FreeRTOS V6.0.5 - Copyright (C) 2010 Real Time Engineers Ltd.
3
4 ***************************************************************************
5 * *
6 * If you are: *
7 * *
8 * + New to FreeRTOS, *
9 * + Wanting to learn FreeRTOS or multitasking in general quickly *
10 * + Looking for basic training, *
11 * + Wanting to improve your FreeRTOS skills and productivity *
12 * *
13 * then take a look at the FreeRTOS eBook *
14 * *
15 * "Using the FreeRTOS Real Time Kernel - a Practical Guide" *
16 * http://www.FreeRTOS.org/Documentation *
17 * *
18 * A pdf reference manual is also available. Both are usually delivered *
19 * to your inbox within 20 minutes to two hours when purchased between 8am *
20 * and 8pm GMT (although please allow up to 24 hours in case of *
21 * exceptional circumstances). Thank you for your support! *
22 * *
23 ***************************************************************************
24
25 This file is part of the FreeRTOS distribution.
26
27 FreeRTOS is free software; you can redistribute it and/or modify it under
28 the terms of the GNU General Public License (version 2) as published by the
29 Free Software Foundation AND MODIFIED BY the FreeRTOS exception.
30 ***NOTE*** The exception to the GPL is included to allow you to distribute
31 a combined work that includes FreeRTOS without being obliged to provide the
32 source code for proprietary components outside of the FreeRTOS kernel.
33 FreeRTOS is distributed in the hope that it will be useful, but WITHOUT
34 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
35 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
36 more details. You should have received a copy of the GNU General Public
37 License and the FreeRTOS license exception along with FreeRTOS; if not it
38 can be viewed here: http://www.freertos.org/a00114.html and also obtained
39 by writing to Richard Barry, contact details for whom are available on the
40 FreeRTOS WEB site.
41
42 1 tab == 4 spaces!
43
44 http://www.FreeRTOS.org - Documentation, latest information, license and
45 contact details.
46
47 http://www.SafeRTOS.com - A version that is certified for use in safety
48 critical systems.
49
50 http://www.OpenRTOS.com - Commercial support, development, porting,
51 licensing and training services.
52*/
53
54
55/*
56 * Tests the extra queue functionality introduced in FreeRTOS.org V4.5.0 -
57 * including xQueueSendToFront(), xQueueSendToBack(), xQueuePeek() and
58 * mutex behaviour.
59 *
60 * See the comments above the prvSendFrontAndBackTest() and
61 * prvLowPriorityMutexTask() prototypes below for more information.
62 */
63
64
65#include <stdlib.h>
66
67/* Scheduler include files. */
68#include "FreeRTOS.h"
69#include "task.h"
70#include "queue.h"
71#include "semphr.h"
72
73/* Demo program include files. */
74#include "GenQTest.h"
75
76#define genqQUEUE_LENGTH ( 5 )
77#define genqNO_BLOCK ( 0 )
78
79#define genqMUTEX_LOW_PRIORITY ( tskIDLE_PRIORITY )
80#define genqMUTEX_TEST_PRIORITY ( tskIDLE_PRIORITY + 1 )
81#define genqMUTEX_MEDIUM_PRIORITY ( tskIDLE_PRIORITY + 2 )
82#define genqMUTEX_HIGH_PRIORITY ( tskIDLE_PRIORITY + 3 )
83
84/*-----------------------------------------------------------*/
85
86/*
87 * Tests the behaviour of the xQueueSendToFront() and xQueueSendToBack()
88 * macros by using both to fill a queue, then reading from the queue to
89 * check the resultant queue order is as expected. Queue data is also
90 * peeked.
91 */
92static void prvSendFrontAndBackTest(void *pvParameters);
93
94/*
95 * The following three tasks are used to demonstrate the mutex behaviour.
96 * Each task is given a different priority to demonstrate the priority
97 * inheritance mechanism.
98 *
99 * The low priority task obtains a mutex. After this a high priority task
100 * attempts to obtain the same mutex, causing its priority to be inherited
101 * by the low priority task. The task with the inherited high priority then
102 * resumes a medium priority task to ensure it is not blocked by the medium
103 * priority task while it holds the inherited high priority. Once the mutex
104 * is returned the task with the inherited priority returns to its original
105 * low priority, and is therefore immediately preempted by first the high
106 * priority task and then the medium prioroity task before it can continue.
107 */
108static void prvLowPriorityMutexTask(void *pvParameters);
109static void prvMediumPriorityMutexTask(void *pvParameters);
110static void prvHighPriorityMutexTask(void *pvParameters);
111
112/*-----------------------------------------------------------*/
113
114/* Flag that will be latched to pdTRUE should any unexpected behaviour be
115detected in any of the tasks. */
116static portBASE_TYPE xErrorDetected = pdFALSE;
117
118/* Counters that are incremented on each cycle of a test. This is used to
119detect a stalled task - a test that is no longer running. */
120static volatile unsigned portLONG ulLoopCounter = 0;
121static volatile unsigned portLONG ulLoopCounter2 = 0;
122
123/* The variable that is guarded by the mutex in the mutex demo tasks. */
124static volatile unsigned portLONG ulGuardedVariable = 0;
125
126/* Handles used in the mutext test to suspend and resume the high and medium
127priority mutex test tasks. */
128static xTaskHandle xHighPriorityMutexTask, xMediumPriorityMutexTask;
129
130/*-----------------------------------------------------------*/
131
132void vStartGenericQueueTasks(unsigned portBASE_TYPE uxPriority)
133{
134 xQueueHandle xQueue;
135 xSemaphoreHandle xMutex;
136
137 /* Create the queue that we are going to use for the
138 prvSendFrontAndBackTest demo. */
139 xQueue = xQueueCreate(genqQUEUE_LENGTH, sizeof(unsigned portLONG));
140
141 /* vQueueAddToRegistry() adds the queue to the queue registry, if one is
142 in use. The queue registry is provided as a means for kernel aware
143 debuggers to locate queues and has no purpose if a kernel aware debugger
144 is not being used. The call to vQueueAddToRegistry() will be removed
145 by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is
146 defined to be less than 1. */
147 vQueueAddToRegistry(xQueue, (signed portCHAR *) "Gen_Queue_Test");
148
149 /* Create the demo task and pass it the queue just created. We are
150 passing the queue handle by value so it does not matter that it is
151 declared on the stack here. */
152 xTaskCreate(prvSendFrontAndBackTest, (signed portCHAR *)"GenQ", configMINIMAL_STACK_SIZE, (void *) xQueue, uxPriority, NULL);
153
154 /* Create the mutex used by the prvMutexTest task. */
155 xMutex = xSemaphoreCreateMutex();
156
157 /* vQueueAddToRegistry() adds the mutex to the registry, if one is
158 in use. The registry is provided as a means for kernel aware
159 debuggers to locate mutexes and has no purpose if a kernel aware debugger
160 is not being used. The call to vQueueAddToRegistry() will be removed
161 by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is
162 defined to be less than 1. */
163 vQueueAddToRegistry((xQueueHandle) xMutex, (signed portCHAR *) "Gen_Queue_Mutex");
164
165 /* Create the mutex demo tasks and pass it the mutex just created. We are
166 passing the mutex handle by value so it does not matter that it is declared
167 on the stack here. */
168 xTaskCreate(prvLowPriorityMutexTask, (signed portCHAR *)"MuLow", configMINIMAL_STACK_SIZE, (void *) xMutex, genqMUTEX_LOW_PRIORITY, NULL);
169 xTaskCreate(prvMediumPriorityMutexTask, (signed portCHAR *)"MuMed", configMINIMAL_STACK_SIZE, NULL, genqMUTEX_MEDIUM_PRIORITY, &xMediumPriorityMutexTask);
170 xTaskCreate(prvHighPriorityMutexTask, (signed portCHAR *)"MuHigh", configMINIMAL_STACK_SIZE, (void *) xMutex, genqMUTEX_HIGH_PRIORITY, &xHighPriorityMutexTask);
171}
172/*-----------------------------------------------------------*/
173
174static void prvSendFrontAndBackTest(void *pvParameters)
175{
176 unsigned portLONG ulData, ulData2;
177 xQueueHandle xQueue;
178
179#ifdef USE_STDIO
180 void vPrintDisplayMessage(const portCHAR * const * ppcMessageToSend);
181
182 const portCHAR * const pcTaskStartMsg = "Queue SendToFront/SendToBack/Peek test started.\r\n";
183
184 /* Queue a message for printing to say the task has started. */
185 vPrintDisplayMessage(&pcTaskStartMsg);
186#endif
187
188 xQueue = (xQueueHandle) pvParameters;
189
190 for (;;) {
191 /* The queue is empty, so sending an item to the back of the queue
192 should have the same efect as sending it to the front of the queue.
193
194 First send to the front and check everything is as expected. */
195 xQueueSendToFront(xQueue, (void *) &ulLoopCounter, genqNO_BLOCK);
196
197 if (uxQueueMessagesWaiting(xQueue) != 1) {
198 xErrorDetected = pdTRUE;
199 }
200
201 if (xQueueReceive(xQueue, (void *) &ulData, genqNO_BLOCK) != pdPASS) {
202 xErrorDetected = pdTRUE;
203 }
204
205 /* The data we sent to the queue should equal the data we just received
206 from the queue. */
207 if (ulLoopCounter != ulData) {
208 xErrorDetected = pdTRUE;
209 }
210
211 /* Then do the same, sending the data to the back, checking everything
212 is as expected. */
213 if (uxQueueMessagesWaiting(xQueue) != 0) {
214 xErrorDetected = pdTRUE;
215 }
216
217 xQueueSendToBack(xQueue, (void *) &ulLoopCounter, genqNO_BLOCK);
218
219 if (uxQueueMessagesWaiting(xQueue) != 1) {
220 xErrorDetected = pdTRUE;
221 }
222
223 if (xQueueReceive(xQueue, (void *) &ulData, genqNO_BLOCK) != pdPASS) {
224 xErrorDetected = pdTRUE;
225 }
226
227 if (uxQueueMessagesWaiting(xQueue) != 0) {
228 xErrorDetected = pdTRUE;
229 }
230
231 /* The data we sent to the queue should equal the data we just received
232 from the queue. */
233 if (ulLoopCounter != ulData) {
234 xErrorDetected = pdTRUE;
235 }
236
237#if configUSE_PREEMPTION == 0
238 taskYIELD();
239#endif
240
241
242
243 /* Place 2, 3, 4 into the queue, adding items to the back of the queue. */
244 for (ulData = 2; ulData < 5; ulData++) {
245 xQueueSendToBack(xQueue, (void *) &ulData, genqNO_BLOCK);
246 }
247
248 /* Now the order in the queue should be 2, 3, 4, with 2 being the first
249 thing to be read out. Now add 1 then 0 to the front of the queue. */
250 if (uxQueueMessagesWaiting(xQueue) != 3) {
251 xErrorDetected = pdTRUE;
252 }
253 ulData = 1;
254 xQueueSendToFront(xQueue, (void *) &ulData, genqNO_BLOCK);
255 ulData = 0;
256 xQueueSendToFront(xQueue, (void *) &ulData, genqNO_BLOCK);
257
258 /* Now the queue should be full, and when we read the data out we
259 should receive 0, 1, 2, 3, 4. */
260 if (uxQueueMessagesWaiting(xQueue) != 5) {
261 xErrorDetected = pdTRUE;
262 }
263
264 if (xQueueSendToFront(xQueue, (void *) &ulData, genqNO_BLOCK) != errQUEUE_FULL) {
265 xErrorDetected = pdTRUE;
266 }
267
268 if (xQueueSendToBack(xQueue, (void *) &ulData, genqNO_BLOCK) != errQUEUE_FULL) {
269 xErrorDetected = pdTRUE;
270 }
271
272#if configUSE_PREEMPTION == 0
273 taskYIELD();
274#endif
275
276 /* Check the data we read out is in the expected order. */
277 for (ulData = 0; ulData < genqQUEUE_LENGTH; ulData++) {
278 /* Try peeking the data first. */
279 if (xQueuePeek(xQueue, &ulData2, genqNO_BLOCK) != pdPASS) {
280 xErrorDetected = pdTRUE;
281 }
282
283 if (ulData != ulData2) {
284 xErrorDetected = pdTRUE;
285 }
286
287
288 /* Now try receiving the data for real. The value should be the
289 same. Clobber the value first so we know we really received it. */
290 ulData2 = ~ulData2;
291 if (xQueueReceive(xQueue, &ulData2, genqNO_BLOCK) != pdPASS) {
292 xErrorDetected = pdTRUE;
293 }
294
295 if (ulData != ulData2) {
296 xErrorDetected = pdTRUE;
297 }
298 }
299
300 /* The queue should now be empty again. */
301 if (uxQueueMessagesWaiting(xQueue) != 0) {
302 xErrorDetected = pdTRUE;
303 }
304
305#if configUSE_PREEMPTION == 0
306 taskYIELD();
307#endif
308
309
310 /* Our queue is empty once more, add 10, 11 to the back. */
311 ulData = 10;
312 if (xQueueSend(xQueue, &ulData, genqNO_BLOCK) != pdPASS) {
313 xErrorDetected = pdTRUE;
314 }
315 ulData = 11;
316 if (xQueueSend(xQueue, &ulData, genqNO_BLOCK) != pdPASS) {
317 xErrorDetected = pdTRUE;
318 }
319
320 if (uxQueueMessagesWaiting(xQueue) != 2) {
321 xErrorDetected = pdTRUE;
322 }
323
324 /* Now we should have 10, 11 in the queue. Add 7, 8, 9 to the
325 front. */
326 for (ulData = 9; ulData >= 7; ulData--) {
327 if (xQueueSendToFront(xQueue, (void *) &ulData, genqNO_BLOCK) != pdPASS) {
328 xErrorDetected = pdTRUE;
329 }
330 }
331
332 /* Now check that the queue is full, and that receiving data provides
333 the expected sequence of 7, 8, 9, 10, 11. */
334 if (uxQueueMessagesWaiting(xQueue) != 5) {
335 xErrorDetected = pdTRUE;
336 }
337
338 if (xQueueSendToFront(xQueue, (void *) &ulData, genqNO_BLOCK) != errQUEUE_FULL) {
339 xErrorDetected = pdTRUE;
340 }
341
342 if (xQueueSendToBack(xQueue, (void *) &ulData, genqNO_BLOCK) != errQUEUE_FULL) {
343 xErrorDetected = pdTRUE;
344 }
345
346#if configUSE_PREEMPTION == 0
347 taskYIELD();
348#endif
349
350 /* Check the data we read out is in the expected order. */
351 for (ulData = 7; ulData < (7 + genqQUEUE_LENGTH); ulData++) {
352 if (xQueueReceive(xQueue, &ulData2, genqNO_BLOCK) != pdPASS) {
353 xErrorDetected = pdTRUE;
354 }
355
356 if (ulData != ulData2) {
357 xErrorDetected = pdTRUE;
358 }
359 }
360
361 if (uxQueueMessagesWaiting(xQueue) != 0) {
362 xErrorDetected = pdTRUE;
363 }
364
365 ulLoopCounter++;
366 }
367}
368/*-----------------------------------------------------------*/
369
370static void prvLowPriorityMutexTask(void *pvParameters)
371{
372 xSemaphoreHandle xMutex = (xSemaphoreHandle) pvParameters;
373
374#ifdef USE_STDIO
375 void vPrintDisplayMessage(const portCHAR * const * ppcMessageToSend);
376
377 const portCHAR * const pcTaskStartMsg = "Mutex with priority inheritance test started.\r\n";
378
379 /* Queue a message for printing to say the task has started. */
380 vPrintDisplayMessage(&pcTaskStartMsg);
381#endif
382
383 for (;;) {
384 /* Take the mutex. It should be available now. */
385 if (xSemaphoreTake(xMutex, genqNO_BLOCK) != pdPASS) {
386 xErrorDetected = pdTRUE;
387 }
388
389 /* Set our guarded variable to a known start value. */
390 ulGuardedVariable = 0;
391
392 /* Our priority should be as per that assigned when the task was
393 created. */
394 if (uxTaskPriorityGet(NULL) != genqMUTEX_LOW_PRIORITY) {
395 xErrorDetected = pdTRUE;
396 }
397
398 /* Now unsuspend the high priority task. This will attempt to take the
399 mutex, and block when it finds it cannot obtain it. */
400 vTaskResume(xHighPriorityMutexTask);
401
402 /* We should now have inherited the prioritoy of the high priority task,
403 as by now it will have attempted to get the mutex. */
404 if (uxTaskPriorityGet(NULL) != genqMUTEX_HIGH_PRIORITY) {
405 xErrorDetected = pdTRUE;
406 }
407
408 /* We can attempt to set our priority to the test priority - between the
409 idle priority and the medium/high test priorities, but our actual
410 prioroity should remain at the high priority. */
411 vTaskPrioritySet(NULL, genqMUTEX_TEST_PRIORITY);
412 if (uxTaskPriorityGet(NULL) != genqMUTEX_HIGH_PRIORITY) {
413 xErrorDetected = pdTRUE;
414 }
415
416 /* Now unsuspend the medium priority task. This should not run as our
417 inherited priority is above that of the medium priority task. */
418 vTaskResume(xMediumPriorityMutexTask);
419
420 /* If the did run then it will have incremented our guarded variable. */
421 if (ulGuardedVariable != 0) {
422 xErrorDetected = pdTRUE;
423 }
424
425 /* When we give back the semaphore our priority should be disinherited
426 back to the priority to which we attempted to set ourselves. This means
427 that when the high priority task next blocks, the medium priority task
428 should execute and increment the guarded variable. When we next run
429 both the high and medium priority tasks will have been suspended again. */
430 if (xSemaphoreGive(xMutex) != pdPASS) {
431 xErrorDetected = pdTRUE;
432 }
433
434 /* Check that the guarded variable did indeed increment... */
435 if (ulGuardedVariable != 1) {
436 xErrorDetected = pdTRUE;
437 }
438
439 /* ... and that our priority has been disinherited to
440 genqMUTEX_TEST_PRIORITY. */
441 if (uxTaskPriorityGet(NULL) != genqMUTEX_TEST_PRIORITY) {
442 xErrorDetected = pdTRUE;
443 }
444
445 /* Set our priority back to our original priority ready for the next
446 loop around this test. */
447 vTaskPrioritySet(NULL, genqMUTEX_LOW_PRIORITY);
448
449 /* Just to show we are still running. */
450 ulLoopCounter2++;
451
452#if configUSE_PREEMPTION == 0
453 taskYIELD();
454#endif
455 }
456}
457/*-----------------------------------------------------------*/
458
459static void prvMediumPriorityMutexTask(void *pvParameters)
460{
461 (void) pvParameters;
462
463 for (;;) {
464 /* The medium priority task starts by suspending itself. The low
465 priority task will unsuspend this task when required. */
466 vTaskSuspend(NULL);
467
468 /* When this task unsuspends all it does is increment the guarded
469 variable, this is so the low priority task knows that it has
470 executed. */
471 ulGuardedVariable++;
472 }
473}
474/*-----------------------------------------------------------*/
475
476static void prvHighPriorityMutexTask(void *pvParameters)
477{
478 xSemaphoreHandle xMutex = (xSemaphoreHandle) pvParameters;
479
480 for (;;) {
481 /* The high priority task starts by suspending itself. The low
482 priority task will unsuspend this task when required. */
483 vTaskSuspend(NULL);
484
485 /* When this task unsuspends all it does is attempt to obtain
486 the mutex. It should find the mutex is not available so a
487 block time is specified. */
488 if (xSemaphoreTake(xMutex, portMAX_DELAY) != pdPASS) {
489 xErrorDetected = pdTRUE;
490 }
491
492 /* When we eventually obtain the mutex we just give it back then
493 return to suspend ready for the next test. */
494 if (xSemaphoreGive(xMutex) != pdPASS) {
495 xErrorDetected = pdTRUE;
496 }
497 }
498}
499/*-----------------------------------------------------------*/
500
501/* This is called to check that all the created tasks are still running. */
502portBASE_TYPE xAreGenericQueueTasksStillRunning(void)
503{
504 static unsigned portLONG ulLastLoopCounter = 0, ulLastLoopCounter2 = 0;
505
506 /* If the demo task is still running then we expect the loopcounters to
507 have incremented since this function was last called. */
508 if (ulLastLoopCounter == ulLoopCounter) {
509 xErrorDetected = pdTRUE;
510 }
511
512 if (ulLastLoopCounter2 == ulLoopCounter2) {
513 xErrorDetected = pdTRUE;
514 }
515
516 ulLastLoopCounter = ulLoopCounter;
517 ulLastLoopCounter2 = ulLoopCounter2;
518
519 /* Errors detected in the task itself will have latched xErrorDetected
520 to true. */
521
522 return !xErrorDetected;
523}
524
525