]> git.sur5r.net Git - freertos/blob - Demo/Common/Full/events.c
Ready for V5.1.1 release.
[freertos] / Demo / Common / Full / events.c
1 /*\r
2         FreeRTOS.org V5.1.1 - Copyright (C) 2003-2008 Richard Barry.\r
3 \r
4         This file is part of the FreeRTOS.org distribution.\r
5 \r
6         FreeRTOS.org is free software; you can redistribute it and/or modify\r
7         it under the terms of the GNU General Public License as published by\r
8         the Free Software Foundation; either version 2 of the License, or\r
9         (at your option) any later version.\r
10 \r
11         FreeRTOS.org is distributed in the hope that it will be useful,\r
12         but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14         GNU General Public License for more details.\r
15 \r
16         You should have received a copy of the GNU General Public License\r
17         along with FreeRTOS.org; if not, write to the Free Software\r
18         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19 \r
20         A special exception to the GPL can be applied should you wish to distribute\r
21         a combined work that includes FreeRTOS.org, without being obliged to provide\r
22         the source code for any proprietary components.  See the licensing section \r
23         of http://www.FreeRTOS.org for full details of how and when the exception\r
24         can be applied.\r
25 \r
26     ***************************************************************************\r
27     ***************************************************************************\r
28     *                                                                         *\r
29     * SAVE TIME AND MONEY!  We can port FreeRTOS.org to your own hardware,    *\r
30     * and even write all or part of your application on your behalf.          *\r
31     * See http://www.OpenRTOS.com for details of the services we provide to   *\r
32     * expedite your project.                                                  *\r
33     *                                                                         *\r
34     ***************************************************************************\r
35     ***************************************************************************\r
36 \r
37         Please ensure to read the configuration and relevant port sections of the\r
38         online documentation.\r
39 \r
40         http://www.FreeRTOS.org - Documentation, latest information, license and \r
41         contact details.\r
42 \r
43         http://www.SafeRTOS.com - A version that is certified for use in safety \r
44         critical systems.\r
45 \r
46         http://www.OpenRTOS.com - Commercial support, development, porting, \r
47         licensing and training services.\r
48 */\r
49 \r
50 /**\r
51  * This file exercises the event mechanism whereby more than one task is\r
52  * blocked waiting for the same event.\r
53  *\r
54  * The demo creates five tasks - four 'event' tasks, and a controlling task.\r
55  * The event tasks have various different priorities and all block on reading\r
56  * the same queue.  The controlling task writes data to the queue, then checks\r
57  * to see which of the event tasks read the data from the queue.  The\r
58  * controlling task has the lowest priority of all the tasks so is guaranteed\r
59  * to always get preempted immediately upon writing to the queue.\r
60  *\r
61  * By selectively suspending and resuming the event tasks the controlling task\r
62  * can check that the highest priority task that is blocked on the queue is the\r
63  * task that reads the posted data from the queue.\r
64  *\r
65  * Two of the event tasks share the same priority.  When neither of these tasks\r
66  * are suspended they should alternate - one reading one message from the queue,\r
67  * the other the next message, etc.\r
68  */\r
69 \r
70 /* Standard includes. */\r
71 #include <stdlib.h>\r
72 #include <stdio.h>\r
73 #include <string.h>\r
74 \r
75 /* Scheduler include files. */\r
76 #include "FreeRTOS.h"\r
77 #include "task.h"\r
78 #include "queue.h"\r
79 \r
80 /* Demo program include files. */\r
81 #include "mevents.h"\r
82 #include "print.h"\r
83 \r
84 /* Demo specific constants. */\r
85 #define evtSTACK_SIZE           ( ( unsigned portBASE_TYPE ) configMINIMAL_STACK_SIZE )\r
86 #define evtNUM_TASKS            ( 4 )\r
87 #define evtQUEUE_LENGTH         ( ( unsigned portBASE_TYPE ) 3 )\r
88 #define evtNO_DELAY                                             0\r
89 \r
90 /* Just indexes used to uniquely identify the tasks.  Note that two tasks are\r
91 'highest' priority. */\r
92 #define evtHIGHEST_PRIORITY_INDEX_2             3\r
93 #define evtHIGHEST_PRIORITY_INDEX_1             2\r
94 #define evtMEDIUM_PRIORITY_INDEX                1\r
95 #define evtLOWEST_PRIORITY_INDEX                0\r
96 \r
97 /* Each event task increments one of these counters each time it reads data\r
98 from the queue. */\r
99 static volatile portBASE_TYPE xTaskCounters[ evtNUM_TASKS ] = { 0, 0, 0, 0 };\r
100 \r
101 /* Each time the controlling task posts onto the queue it increments the \r
102 expected count of the task that it expected to read the data from the queue \r
103 (i.e. the task with the highest priority that should be blocked on the queue).  \r
104 \r
105 xExpectedTaskCounters are incremented from the controlling task, and \r
106 xTaskCounters are incremented from the individual event tasks - therefore\r
107 comparing xTaskCounters to xExpectedTaskCounters shows whether or not the \r
108 correct task was unblocked by the post. */\r
109 static portBASE_TYPE xExpectedTaskCounters[ evtNUM_TASKS ] = { 0, 0, 0, 0 };\r
110 \r
111 /* Handles to the four event tasks.  These are required to suspend and resume\r
112 the tasks. */\r
113 static xTaskHandle xCreatedTasks[ evtNUM_TASKS ];\r
114 \r
115 /* The single queue onto which the controlling task posts, and the four event\r
116 tasks block. */\r
117 static xQueueHandle xQueue;\r
118 \r
119 /* Flag used to indicate whether or not an error has occurred at any time.\r
120 An error is either the queue being full when not expected, or an unexpected\r
121 task reading data from the queue. */\r
122 static portBASE_TYPE xHealthStatus = pdPASS;\r
123 \r
124 /*-----------------------------------------------------------*/\r
125 \r
126 /* Function that implements the event task.  This is created four times. */\r
127 static void prvMultiEventTask( void *pvParameters );\r
128 \r
129 /* Function that implements the controlling task. */\r
130 static void prvEventControllerTask( void *pvParameters );\r
131 \r
132 /* This is a utility function that posts data to the queue, then compares \r
133 xExpectedTaskCounters with xTaskCounters to ensure everything worked as \r
134 expected.\r
135 \r
136 The event tasks all have higher priorities the controlling task.  Therefore\r
137 the controlling task will always get preempted between writhing to the queue\r
138 and checking the task counters. \r
139 \r
140 @param xExpectedTask  The index to the task that the controlling task thinks\r
141                       should be the highest priority task waiting for data, and\r
142                                           therefore the task that will unblock.\r
143                                           \r
144 @param  xIncrement    The number of items that should be written to the queue.\r
145 */\r
146 static void prvCheckTaskCounters( portBASE_TYPE xExpectedTask, portBASE_TYPE xIncrement );\r
147 \r
148 /* This is just incremented each cycle of the controlling tasks function so\r
149 the main application can ensure the test is still running. */\r
150 static portBASE_TYPE xCheckVariable = 0;\r
151 \r
152 /*-----------------------------------------------------------*/\r
153 \r
154 void vStartMultiEventTasks( void )\r
155 {\r
156         /* Create the queue to be used for all the communications. */\r
157         xQueue = xQueueCreate( evtQUEUE_LENGTH, ( unsigned portBASE_TYPE ) sizeof( unsigned portBASE_TYPE ) );\r
158 \r
159         /* Start the controlling task.  This has the idle priority to ensure it is\r
160         always preempted by the event tasks. */\r
161         xTaskCreate( prvEventControllerTask, "EvntCTRL", evtSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );\r
162 \r
163         /* Start the four event tasks.  Note that two have priority 3, one \r
164         priority 2 and the other priority 1. */\r
165         xTaskCreate( prvMultiEventTask, "Event0", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 0 ] ), 1, &( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] ) );\r
166         xTaskCreate( prvMultiEventTask, "Event1", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 1 ] ), 2, &( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] ) );\r
167         xTaskCreate( prvMultiEventTask, "Event2", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 2 ] ), 3, &( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] ) );\r
168         xTaskCreate( prvMultiEventTask, "Event3", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 3 ] ), 3, &( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] ) );\r
169 }\r
170 /*-----------------------------------------------------------*/\r
171 \r
172 static void prvMultiEventTask( void *pvParameters )\r
173 {\r
174 portBASE_TYPE *pxCounter;\r
175 unsigned portBASE_TYPE uxDummy;\r
176 const portCHAR * const pcTaskStartMsg = "Multi event task started.\r\n";\r
177 \r
178         /* The variable this task will increment is passed in as a parameter. */\r
179         pxCounter = ( portBASE_TYPE * ) pvParameters;\r
180 \r
181         vPrintDisplayMessage( &pcTaskStartMsg );\r
182 \r
183         for( ;; )\r
184         {\r
185                 /* Block on the queue. */\r
186                 if( xQueueReceive( xQueue, &uxDummy, portMAX_DELAY ) )\r
187                 {\r
188                         /* We unblocked by reading the queue - so simply increment\r
189                         the counter specific to this task instance. */\r
190                         ( *pxCounter )++;\r
191                 }\r
192                 else\r
193                 {\r
194                         xHealthStatus = pdFAIL;\r
195                 }\r
196         }\r
197 }\r
198 /*-----------------------------------------------------------*/\r
199 \r
200 static void prvEventControllerTask( void *pvParameters )\r
201 {\r
202 const portCHAR * const pcTaskStartMsg = "Multi event controller task started.\r\n";\r
203 portBASE_TYPE xDummy = 0;\r
204 \r
205         /* Just to stop warnings. */\r
206         ( void ) pvParameters;\r
207 \r
208         vPrintDisplayMessage( &pcTaskStartMsg );\r
209 \r
210         for( ;; )\r
211         {\r
212                 /* All tasks are blocked on the queue.  When a message is posted one of\r
213                 the two tasks that share the highest priority should unblock to read\r
214                 the queue.  The next message written should unblock the other task with\r
215                 the same high priority, and so on in order.   No other task should \r
216                 unblock to read data as they have lower priorities. */\r
217 \r
218                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );\r
219                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_2, 1 );\r
220                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );\r
221                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_2, 1 );\r
222                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );\r
223 \r
224                 /* For the rest of these tests we don't need the second 'highest' \r
225                 priority task - so it is suspended. */\r
226                 vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] );\r
227 \r
228 \r
229 \r
230                 /* Now suspend the other highest priority task.  The medium priority \r
231                 task will then be the task with the highest priority that remains \r
232                 blocked on the queue. */\r
233                 vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
234                 \r
235                 /* This time, when we post onto the queue we will expect the medium\r
236                 priority task to unblock and preempt us. */\r
237                 prvCheckTaskCounters( evtMEDIUM_PRIORITY_INDEX, 1 );\r
238 \r
239                 /* Now try resuming the highest priority task while the scheduler is\r
240                 suspended.  The task should start executing as soon as the scheduler\r
241                 is resumed - therefore when we post to the queue again, the highest\r
242                 priority task should again preempt us. */\r
243                 vTaskSuspendAll();\r
244                         vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
245                 xTaskResumeAll();\r
246                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );\r
247                 \r
248                 /* Now we are going to suspend the high and medium priority tasks.  The\r
249                 low priority task should then preempt us.  Again the task suspension is \r
250                 done with the whole scheduler suspended just for test purposes. */\r
251                 vTaskSuspendAll();\r
252                         vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
253                         vTaskSuspend( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );\r
254                 xTaskResumeAll();\r
255                 prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, 1 );\r
256                 \r
257                 /* Do the same basic test another few times - selectively suspending\r
258                 and resuming tasks and each time calling prvCheckTaskCounters() passing\r
259                 to the function the number of the task we expected to be unblocked by \r
260                 the     post. */\r
261 \r
262                 vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
263                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );\r
264                 \r
265                 vTaskSuspendAll(); /* Just for test. */\r
266                         vTaskSuspendAll(); /* Just for test. */\r
267                                 vTaskSuspendAll(); /* Just for even more test. */\r
268                                         vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
269                                 xTaskResumeAll();\r
270                         xTaskResumeAll();\r
271                 xTaskResumeAll();\r
272                 prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, 1 );\r
273                 \r
274                 vTaskResume( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );\r
275                 prvCheckTaskCounters( evtMEDIUM_PRIORITY_INDEX, 1 );\r
276                 \r
277                 vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
278                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );\r
279 \r
280                 /* Now a slight change, first suspend all tasks. */\r
281                 vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
282                 vTaskSuspend( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );\r
283                 vTaskSuspend( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );\r
284                 \r
285                 /* Now when we resume the low priority task and write to the queue 3 \r
286                 times.  We expect the low priority task to service the queue three\r
287                 times. */\r
288                 vTaskResume( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );\r
289                 prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, evtQUEUE_LENGTH );\r
290                 \r
291                 /* Again suspend all tasks (only the low priority task is not suspended\r
292                 already). */\r
293                 vTaskSuspend( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );\r
294                 \r
295                 /* This time we are going to suspend the scheduler, resume the low\r
296                 priority task, then resume the high priority task.  In this state we\r
297                 will write to the queue three times.  When the scheduler is resumed\r
298                 we expect the high priority task to service all three messages. */\r
299                 vTaskSuspendAll();\r
300                 {\r
301                         vTaskResume( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );\r
302                         vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
303                         \r
304                         for( xDummy = 0; xDummy < evtQUEUE_LENGTH; xDummy++ )\r
305                         {\r
306                                 if( xQueueSend( xQueue, &xDummy, evtNO_DELAY ) != pdTRUE )\r
307                                 {\r
308                                         xHealthStatus = pdFAIL;\r
309                                 }\r
310                         }                       \r
311                         \r
312                         /* The queue should not have been serviced yet!.  The scheduler\r
313                         is still suspended. */\r
314                         if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) )\r
315                         {\r
316                                 xHealthStatus = pdFAIL;\r
317                         }\r
318                 }\r
319                 xTaskResumeAll();\r
320 \r
321                 /* We should have been preempted by resuming the scheduler - so by the\r
322                 time we are running again we expect the high priority task to have \r
323                 removed three items from the queue. */\r
324                 xExpectedTaskCounters[ evtHIGHEST_PRIORITY_INDEX_1 ] += evtQUEUE_LENGTH;\r
325                 if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) )\r
326                 {\r
327                         xHealthStatus = pdFAIL;\r
328                 }\r
329                 \r
330                 /* The medium priority and second high priority tasks are still \r
331                 suspended.  Make sure to resume them before starting again. */\r
332                 vTaskResume( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );\r
333                 vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] );\r
334 \r
335                 /* Just keep incrementing to show the task is still executing. */\r
336                 xCheckVariable++;\r
337         }\r
338 }\r
339 /*-----------------------------------------------------------*/\r
340 \r
341 static void prvCheckTaskCounters( portBASE_TYPE xExpectedTask, portBASE_TYPE xIncrement )\r
342 {\r
343 portBASE_TYPE xDummy = 0;\r
344 \r
345         /* Write to the queue the requested number of times.  The data written is\r
346         not important. */\r
347         for( xDummy = 0; xDummy < xIncrement; xDummy++ )\r
348         {\r
349                 if( xQueueSend( xQueue, &xDummy, evtNO_DELAY ) != pdTRUE )\r
350                 {\r
351                         /* Did not expect to ever find the queue full. */\r
352                         xHealthStatus = pdFAIL;\r
353                 }\r
354         }\r
355 \r
356         /* All the tasks blocked on the queue have a priority higher than the \r
357         controlling task.  Writing to the queue will therefore have caused this\r
358         task to be preempted.  By the time this line executes the event task will\r
359         have executed and incremented its counter.  Increment the expected counter\r
360         to the same value. */\r
361         ( xExpectedTaskCounters[ xExpectedTask ] ) += xIncrement;\r
362 \r
363         /* Check the actual counts and expected counts really are the same. */\r
364         if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) )\r
365         {\r
366                 /* The counters were not the same.  This means a task we did not expect\r
367                 to unblock actually did unblock. */\r
368                 xHealthStatus = pdFAIL;\r
369         }\r
370 }\r
371 /*-----------------------------------------------------------*/\r
372 \r
373 portBASE_TYPE xAreMultiEventTasksStillRunning( void )\r
374 {\r
375 static portBASE_TYPE xPreviousCheckVariable = 0;\r
376 \r
377         /* Called externally to periodically check that this test is still\r
378         operational. */\r
379 \r
380         if( xPreviousCheckVariable == xCheckVariable )\r
381         {\r
382                 xHealthStatus = pdFAIL;\r
383         }\r
384         \r
385         xPreviousCheckVariable = xCheckVariable;\r
386         \r
387         return xHealthStatus;   \r
388 }\r
389 \r
390 \r