]> git.sur5r.net Git - freertos/blob - Demo/Common/Minimal/AltBlock.c
Correct task names in BlockQ.c.
[freertos] / Demo / Common / Minimal / AltBlock.c
1 /*\r
2     FreeRTOS V7.0.1 - Copyright (C) 2011 Real Time Engineers Ltd.\r
3         \r
4 \r
5     ***************************************************************************\r
6      *                                                                       *\r
7      *    FreeRTOS tutorial books are available in pdf and paperback.        *\r
8      *    Complete, revised, and edited pdf reference manuals are also       *\r
9      *    available.                                                         *\r
10      *                                                                       *\r
11      *    Purchasing FreeRTOS documentation will not only help you, by       *\r
12      *    ensuring you get running as quickly as possible and with an        *\r
13      *    in-depth knowledge of how to use FreeRTOS, it will also help       *\r
14      *    the FreeRTOS project to continue with its mission of providing     *\r
15      *    professional grade, cross platform, de facto standard solutions    *\r
16      *    for microcontrollers - completely free of charge!                  *\r
17      *                                                                       *\r
18      *    >>> See http://www.FreeRTOS.org/Documentation for details. <<<     *\r
19      *                                                                       *\r
20      *    Thank you for using FreeRTOS, and thank you for your support!      *\r
21      *                                                                       *\r
22     ***************************************************************************\r
23 \r
24 \r
25     This file is part of the FreeRTOS distribution.\r
26 \r
27     FreeRTOS is free software; you can redistribute it and/or modify it under\r
28     the terms of the GNU General Public License (version 2) as published by the\r
29     Free Software Foundation AND MODIFIED BY the FreeRTOS exception.\r
30     >>>NOTE<<< The modification to the GPL is included to allow you to\r
31     distribute a combined work that includes FreeRTOS without being obliged to\r
32     provide the source code for proprietary components outside of the FreeRTOS\r
33     kernel.  FreeRTOS is distributed in the hope that it will be useful, but\r
34     WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\r
35     or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for\r
36     more details. You should have received a copy of the GNU General Public\r
37     License and the FreeRTOS license exception along with FreeRTOS; if not it\r
38     can be viewed here: http://www.freertos.org/a00114.html and also obtained\r
39     by writing to Richard Barry, contact details for whom are available on the\r
40     FreeRTOS WEB site.\r
41 \r
42     1 tab == 4 spaces!\r
43 \r
44     http://www.FreeRTOS.org - Documentation, latest information, license and\r
45     contact details.\r
46 \r
47     http://www.SafeRTOS.com - A version that is certified for use in safety\r
48     critical systems.\r
49 \r
50     http://www.OpenRTOS.com - Commercial support, development, porting,\r
51     licensing and training services.\r
52 */\r
53 \r
54 /*\r
55  * This is a version of BlockTim.c that uses the light weight API.\r
56  *\r
57  * This file contains some test scenarios that ensure tasks do not exit queue\r
58  * send or receive functions prematurely.  A description of the tests is\r
59  * included within the code.\r
60  */\r
61 \r
62 /* Kernel includes. */\r
63 #include "FreeRTOS.h"\r
64 #include "task.h"\r
65 #include "queue.h"\r
66 \r
67 /* Demo includes. */\r
68 #include "AltBlock.h"\r
69 \r
70 /* Task priorities. */\r
71 #define bktPRIMARY_PRIORITY                     ( 3 )\r
72 #define bktSECONDARY_PRIORITY           ( 2 )\r
73 \r
74 /* Task behaviour. */\r
75 #define bktQUEUE_LENGTH                         ( 5 )\r
76 #define bktSHORT_WAIT                           ( ( ( portTickType ) 20 ) / portTICK_RATE_MS )\r
77 #define bktPRIMARY_BLOCK_TIME           ( 10 )\r
78 #define bktALLOWABLE_MARGIN                     ( 12 )\r
79 #define bktTIME_TO_BLOCK                        ( 175 )\r
80 #define bktDONT_BLOCK                           ( ( portTickType ) 0 )\r
81 #define bktRUN_INDICATOR                        ( ( unsigned portBASE_TYPE ) 0x55 )\r
82 \r
83 /* The queue on which the tasks block. */\r
84 static xQueueHandle xTestQueue;\r
85 \r
86 /* Handle to the secondary task is required by the primary task for calls\r
87 to vTaskSuspend/Resume(). */\r
88 static xTaskHandle xSecondary;\r
89 \r
90 /* Used to ensure that tasks are still executing without error. */\r
91 static portBASE_TYPE xPrimaryCycles = 0, xSecondaryCycles = 0;\r
92 static portBASE_TYPE xErrorOccurred = pdFALSE;\r
93 \r
94 /* Provides a simple mechanism for the primary task to know when the\r
95 secondary task has executed. */\r
96 static volatile unsigned portBASE_TYPE xRunIndicator;\r
97 \r
98 /* The two test tasks.  Their behaviour is commented within the files. */\r
99 static void vPrimaryBlockTimeTestTask( void *pvParameters );\r
100 static void vSecondaryBlockTimeTestTask( void *pvParameters );\r
101 \r
102 /*-----------------------------------------------------------*/\r
103 \r
104 void vCreateAltBlockTimeTasks( void )\r
105 {\r
106         /* Create the queue on which the two tasks block. */\r
107     xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( portBASE_TYPE ) );\r
108 \r
109         /* vQueueAddToRegistry() adds the queue to the queue registry, if one is\r
110         in use.  The queue registry is provided as a means for kernel aware \r
111         debuggers to locate queues and has no purpose if a kernel aware debugger\r
112         is not being used.  The call to vQueueAddToRegistry() will be removed\r
113         by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is \r
114         defined to be less than 1. */\r
115         vQueueAddToRegistry( xTestQueue, ( signed portCHAR * ) "AltBlockQueue" );\r
116 \r
117 \r
118         /* Create the two test tasks. */\r
119         xTaskCreate( vPrimaryBlockTimeTestTask, ( signed portCHAR * )"FBTest1", configMINIMAL_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL );\r
120         xTaskCreate( vSecondaryBlockTimeTestTask, ( signed portCHAR * )"FBTest2", configMINIMAL_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary );\r
121 }\r
122 /*-----------------------------------------------------------*/\r
123 \r
124 static void vPrimaryBlockTimeTestTask( void *pvParameters )\r
125 {\r
126 portBASE_TYPE xItem, xData;\r
127 portTickType xTimeWhenBlocking;\r
128 portTickType xTimeToBlock, xBlockedTime;\r
129 \r
130         #ifdef USE_STDIO\r
131         void vPrintDisplayMessage( const portCHAR * const * ppcMessageToSend );\r
132         \r
133                 const portCHAR * const pcTaskStartMsg = "Alt primary block time test started.\r\n";\r
134 \r
135                 /* Queue a message for printing to say the task has started. */\r
136                 vPrintDisplayMessage( &pcTaskStartMsg );\r
137         #endif\r
138 \r
139         ( void ) pvParameters;\r
140 \r
141         for( ;; )\r
142         {\r
143                 /*********************************************************************\r
144         Test 1\r
145 \r
146         Simple block time wakeup test on queue receives. */\r
147                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
148                 {\r
149                         /* The queue is empty. Attempt to read from the queue using a block\r
150                         time.  When we wake, ensure the delta in time is as expected. */\r
151                         xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;\r
152 \r
153                         /* A critical section is used to minimise the jitter in the time\r
154                         measurements. */\r
155                         portENTER_CRITICAL();\r
156                         {\r
157                                 xTimeWhenBlocking = xTaskGetTickCount();\r
158                                 \r
159                                 /* We should unblock after xTimeToBlock having not received\r
160                                 anything on the queue. */\r
161                                 if( xQueueAltReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY )\r
162                                 {\r
163                                         xErrorOccurred = pdTRUE;\r
164                                 }\r
165 \r
166                                 /* How long were we blocked for? */\r
167                                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
168                         }\r
169                         portEXIT_CRITICAL();\r
170 \r
171                         if( xBlockedTime < xTimeToBlock )\r
172                         {\r
173                                 /* Should not have blocked for less than we requested. */\r
174                                 xErrorOccurred = pdTRUE;\r
175                         }\r
176 \r
177                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
178                         {\r
179                                 /* Should not have blocked for longer than we requested,\r
180                                 although we would not necessarily run as soon as we were\r
181                                 unblocked so a margin is allowed. */\r
182                                 xErrorOccurred = pdTRUE;\r
183                         }\r
184                 }\r
185 \r
186 \r
187                 #if configUSE_PREEMPTION == 0\r
188                         taskYIELD();\r
189                 #endif\r
190 \r
191 \r
192                 /*********************************************************************\r
193         Test 2\r
194 \r
195         Simple block time wakeup test on queue sends.\r
196 \r
197                 First fill the queue.  It should be empty so all sends should pass. */\r
198                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
199                 {\r
200                         if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
201                         {\r
202                                 xErrorOccurred = pdTRUE;\r
203                         }\r
204                 }\r
205 \r
206                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
207                 {\r
208                         /* The queue is full. Attempt to write to the queue using a block\r
209                         time.  When we wake, ensure the delta in time is as expected. */\r
210                         xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;\r
211 \r
212                         portENTER_CRITICAL();\r
213                         {\r
214                                 xTimeWhenBlocking = xTaskGetTickCount();\r
215                                 \r
216                                 /* We should unblock after xTimeToBlock having not received\r
217                                 anything on the queue. */\r
218                                 if( xQueueAltSendToBack( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL )\r
219                                 {\r
220                                         xErrorOccurred = pdTRUE;\r
221                                 }\r
222 \r
223                                 /* How long were we blocked for? */\r
224                                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
225                         }\r
226                         portEXIT_CRITICAL();\r
227 \r
228                         if( xBlockedTime < xTimeToBlock )\r
229                         {\r
230                                 /* Should not have blocked for less than we requested. */\r
231                                 xErrorOccurred = pdTRUE;\r
232                         }\r
233 \r
234                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
235                         {\r
236                                 /* Should not have blocked for longer than we requested,\r
237                                 although we would not necessarily run as soon as we were\r
238                                 unblocked so a margin is allowed. */\r
239                                 xErrorOccurred = pdTRUE;\r
240                         }\r
241                 }\r
242 \r
243                 #if configUSE_PREEMPTION == 0\r
244                         taskYIELD();\r
245                 #endif\r
246 \r
247                 \r
248                 /*********************************************************************\r
249         Test 3\r
250 \r
251                 Wake the other task, it will block attempting to post to the queue.\r
252                 When we read from the queue the other task will wake, but before it\r
253                 can run we will post to the queue again.  When the other task runs it\r
254                 will find the queue still full, even though it was woken.  It should\r
255                 recognise that its block time has not expired and return to block for\r
256                 the remains of its block time.\r
257 \r
258                 Wake the other task so it blocks attempting to post to the already\r
259                 full queue. */\r
260                 xRunIndicator = 0;\r
261                 vTaskResume( xSecondary );\r
262 \r
263                 /* We need to wait a little to ensure the other task executes. */\r
264                 while( xRunIndicator != bktRUN_INDICATOR )\r
265                 {\r
266                         /* The other task has not yet executed. */\r
267                         vTaskDelay( bktSHORT_WAIT );\r
268                 }\r
269                 /* Make sure the other task is blocked on the queue. */\r
270                 vTaskDelay( bktSHORT_WAIT );\r
271                 xRunIndicator = 0;\r
272 \r
273                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
274                 {\r
275                         /* Now when we make space on the queue the other task should wake\r
276                         but not execute as this task has higher priority. */                            \r
277                         if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
278                         {\r
279                                 xErrorOccurred = pdTRUE;\r
280                         }\r
281 \r
282                         /* Now fill the queue again before the other task gets a chance to\r
283                         execute.  If the other task had executed we would find the queue\r
284                         full ourselves, and the other task have set xRunIndicator. */\r
285                         if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
286                         {\r
287                                 xErrorOccurred = pdTRUE;\r
288                         }\r
289 \r
290                         if( xRunIndicator == bktRUN_INDICATOR )\r
291                         {\r
292                                 /* The other task should not have executed. */\r
293                                 xErrorOccurred = pdTRUE;\r
294                         }\r
295 \r
296                         /* Raise the priority of the other task so it executes and blocks\r
297                         on the queue again. */\r
298                         vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );\r
299 \r
300                         /* The other task should now have re-blocked without exiting the\r
301                         queue function. */\r
302                         if( xRunIndicator == bktRUN_INDICATOR )\r
303                         {\r
304                                 /* The other task should not have executed outside of the\r
305                                 queue function. */\r
306                                 xErrorOccurred = pdTRUE;\r
307                         }\r
308 \r
309                         /* Set the priority back down. */\r
310                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );                  \r
311                 }\r
312 \r
313                 /* Let the other task timeout.  When it unblockes it will check that it\r
314                 unblocked at the correct time, then suspend itself. */\r
315                 while( xRunIndicator != bktRUN_INDICATOR )\r
316                 {\r
317                         vTaskDelay( bktSHORT_WAIT );\r
318                 }\r
319                 vTaskDelay( bktSHORT_WAIT );\r
320                 xRunIndicator = 0;\r
321 \r
322                 #if configUSE_PREEMPTION == 0\r
323                         taskYIELD();\r
324                 #endif\r
325 \r
326                 /*********************************************************************\r
327         Test 4\r
328 \r
329                 As per test 3 - but with the send and receive the other way around.\r
330                 The other task blocks attempting to read from the queue.\r
331 \r
332                 Empty the queue.  We should find that it is full. */\r
333                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
334                 {\r
335                         if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
336                         {\r
337                                 xErrorOccurred = pdTRUE;\r
338                         }\r
339                 }\r
340                 \r
341                 /* Wake the other task so it blocks attempting to read from  the\r
342                 already empty queue. */\r
343                 vTaskResume( xSecondary );\r
344 \r
345                 /* We need to wait a little to ensure the other task executes. */\r
346                 while( xRunIndicator != bktRUN_INDICATOR )\r
347                 {\r
348                         vTaskDelay( bktSHORT_WAIT );\r
349                 }\r
350                 vTaskDelay( bktSHORT_WAIT );\r
351                 xRunIndicator = 0;\r
352 \r
353                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
354                 {\r
355                         /* Now when we place an item on the queue the other task should\r
356                         wake but not execute as this task has higher priority. */                               \r
357                         if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
358                         {\r
359                                 xErrorOccurred = pdTRUE;\r
360                         }\r
361 \r
362                         /* Now empty the queue again before the other task gets a chance to\r
363                         execute.  If the other task had executed we would find the queue\r
364                         empty ourselves, and the other task would be suspended. */\r
365                         if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
366                         {\r
367                                 xErrorOccurred = pdTRUE;\r
368                         }\r
369 \r
370                         if( xRunIndicator == bktRUN_INDICATOR )\r
371                         {\r
372                                 /* The other task should not have executed. */\r
373                                 xErrorOccurred = pdTRUE;\r
374                         }\r
375 \r
376                         /* Raise the priority of the other task so it executes and blocks\r
377                         on the queue again. */\r
378                         vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );\r
379 \r
380                         /* The other task should now have re-blocked without exiting the\r
381                         queue function. */\r
382                         if( xRunIndicator == bktRUN_INDICATOR )\r
383                         {\r
384                                 /* The other task should not have executed outside of the\r
385                                 queue function. */\r
386                                 xErrorOccurred = pdTRUE;\r
387                         }\r
388                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );                  \r
389                 }\r
390 \r
391                 /* Let the other task timeout.  When it unblockes it will check that it\r
392                 unblocked at the correct time, then suspend itself. */\r
393                 while( xRunIndicator != bktRUN_INDICATOR )\r
394                 {\r
395                         vTaskDelay( bktSHORT_WAIT );\r
396                 }\r
397                 vTaskDelay( bktSHORT_WAIT );\r
398 \r
399                 xPrimaryCycles++;\r
400         }\r
401 }\r
402 /*-----------------------------------------------------------*/\r
403 \r
404 static void vSecondaryBlockTimeTestTask( void *pvParameters )\r
405 {\r
406 portTickType xTimeWhenBlocking, xBlockedTime;\r
407 portBASE_TYPE xData;\r
408 \r
409         #ifdef USE_STDIO\r
410         void vPrintDisplayMessage( const portCHAR * const * ppcMessageToSend );\r
411         \r
412                 const portCHAR * const pcTaskStartMsg = "Alt secondary block time test started.\r\n";\r
413 \r
414                 /* Queue a message for printing to say the task has started. */\r
415                 vPrintDisplayMessage( &pcTaskStartMsg );\r
416         #endif\r
417 \r
418         ( void ) pvParameters;\r
419 \r
420         for( ;; )\r
421         {\r
422                 /*********************************************************************\r
423         Test 1 and 2\r
424 \r
425                 This task does does not participate in these tests. */\r
426                 vTaskSuspend( NULL );\r
427 \r
428                 /*********************************************************************\r
429         Test 3\r
430 \r
431                 The first thing we do is attempt to read from the queue.  It should be\r
432                 full so we block.  Note the time before we block so we can check the\r
433                 wake time is as per that expected. */\r
434                 portENTER_CRITICAL();\r
435                 {\r
436                         xTimeWhenBlocking = xTaskGetTickCount();\r
437                         \r
438                         /* We should unblock after bktTIME_TO_BLOCK having not received\r
439                         anything on the queue. */\r
440                         xData = 0;\r
441                         xRunIndicator = bktRUN_INDICATOR;\r
442                         if( xQueueAltSendToBack( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )\r
443                         {\r
444                                 xErrorOccurred = pdTRUE;\r
445                         }\r
446 \r
447                         /* How long were we inside the send function? */\r
448                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
449                 }\r
450                 portEXIT_CRITICAL();\r
451 \r
452                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
453                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
454                 {\r
455                         xErrorOccurred = pdTRUE;\r
456                 }\r
457 \r
458                 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN\r
459                 either.  A margin is permitted as we would not necessarily run as\r
460                 soon as we unblocked. */\r
461                 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )\r
462                 {\r
463                         xErrorOccurred = pdTRUE;\r
464                 }\r
465 \r
466                 /* Suspend ready for test 3. */\r
467                 xRunIndicator = bktRUN_INDICATOR;\r
468                 vTaskSuspend( NULL );\r
469 \r
470                 /*********************************************************************\r
471         Test 4\r
472 \r
473                 As per test three, but with the send and receive reversed. */\r
474                 portENTER_CRITICAL();\r
475                 {\r
476                         xTimeWhenBlocking = xTaskGetTickCount();\r
477                         \r
478                         /* We should unblock after bktTIME_TO_BLOCK having not received\r
479                         anything on the queue. */\r
480                         xRunIndicator = bktRUN_INDICATOR;\r
481                         if( xQueueAltReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY )\r
482                         {\r
483                                 xErrorOccurred = pdTRUE;\r
484                         }\r
485 \r
486                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
487                 }\r
488                 portEXIT_CRITICAL();\r
489 \r
490                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
491                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
492                 {\r
493                         xErrorOccurred = pdTRUE;\r
494                 }\r
495 \r
496                 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN\r
497                 either.  A margin is permitted as we would not necessarily run as soon\r
498                 as we unblocked. */\r
499                 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )\r
500                 {\r
501                         xErrorOccurred = pdTRUE;\r
502                 }\r
503 \r
504                 xRunIndicator = bktRUN_INDICATOR;\r
505 \r
506                 xSecondaryCycles++;\r
507         }\r
508 }\r
509 /*-----------------------------------------------------------*/\r
510 \r
511 portBASE_TYPE xAreAltBlockTimeTestTasksStillRunning( void )\r
512 {\r
513 static portBASE_TYPE xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;\r
514 portBASE_TYPE xReturn = pdPASS;\r
515 \r
516         /* Have both tasks performed at least one cycle since this function was\r
517         last called? */\r
518         if( xPrimaryCycles == xLastPrimaryCycleCount )\r
519         {\r
520                 xReturn = pdFAIL;\r
521         }\r
522 \r
523         if( xSecondaryCycles == xLastSecondaryCycleCount )\r
524         {\r
525                 xReturn = pdFAIL;\r
526         }\r
527 \r
528         if( xErrorOccurred == pdTRUE )\r
529         {\r
530                 xReturn = pdFAIL;\r
531         }\r
532 \r
533         xLastSecondaryCycleCount = xSecondaryCycles;\r
534         xLastPrimaryCycleCount = xPrimaryCycles;\r
535 \r
536         return xReturn;\r
537 }\r