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