]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/Common/Minimal/TaskNotify.c
b6a5ebf0f89c0620696e509439342c166d6be434
[freertos] / FreeRTOS / Demo / Common / Minimal / TaskNotify.c
1 /*\r
2     FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd.\r
3     All rights reserved\r
4 \r
5     VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.\r
6 \r
7     ***************************************************************************\r
8      *                                                                       *\r
9      *    FreeRTOS provides completely free yet professionally developed,    *\r
10      *    robust, strictly quality controlled, supported, and cross          *\r
11      *    platform software that has become a de facto standard.             *\r
12      *                                                                       *\r
13      *    Help yourself get started quickly and support the FreeRTOS         *\r
14      *    project by purchasing a FreeRTOS tutorial book, reference          *\r
15      *    manual, or both from: http://www.FreeRTOS.org/Documentation        *\r
16      *                                                                       *\r
17      *    Thank you!                                                         *\r
18      *                                                                       *\r
19     ***************************************************************************\r
20 \r
21     This file is part of the FreeRTOS distribution.\r
22 \r
23     FreeRTOS is free software; you can redistribute it and/or modify it under\r
24     the terms of the GNU General Public License (version 2) as published by the\r
25     Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.\r
26 \r
27     >>!   NOTE: The modification to the GPL is included to allow you to     !<<\r
28     >>!   distribute a combined work that includes FreeRTOS without being   !<<\r
29     >>!   obliged to provide the source code for proprietary components     !<<\r
30     >>!   outside of the FreeRTOS kernel.                                   !<<\r
31 \r
32     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY\r
33     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\r
34     FOR A PARTICULAR PURPOSE.  Full license text is available from the following\r
35     link: http://www.freertos.org/a00114.html\r
36 \r
37     1 tab == 4 spaces!\r
38 \r
39     ***************************************************************************\r
40      *                                                                       *\r
41      *    Having a problem?  Start by reading the FAQ "My application does   *\r
42      *    not run, what could be wrong?"                                     *\r
43      *                                                                       *\r
44      *    http://www.FreeRTOS.org/FAQHelp.html                               *\r
45      *                                                                       *\r
46     ***************************************************************************\r
47 \r
48     http://www.FreeRTOS.org - Documentation, books, training, latest versions,\r
49     license and Real Time Engineers Ltd. contact details.\r
50 \r
51     http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,\r
52     including FreeRTOS+Trace - an indispensable productivity tool, a DOS\r
53     compatible FAT file system, and our tiny thread aware UDP/IP stack.\r
54 \r
55     http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High\r
56     Integrity Systems to sell under the OpenRTOS brand.  Low cost OpenRTOS\r
57     licenses offer ticketed support, indemnification and middleware.\r
58 \r
59     http://www.SafeRTOS.com - High Integrity Systems also provide a safety\r
60     engineered and independently SIL3 certified version for use in safety and\r
61     mission critical applications that require provable dependability.\r
62 \r
63     1 tab == 4 spaces!\r
64 */\r
65 \r
66 \r
67 /*\r
68  * Tests the behaviour of direct task notifications.\r
69  */\r
70 \r
71 /* Standard includes. */\r
72 #include <limits.h>\r
73 \r
74 /* Scheduler include files. */\r
75 #include "FreeRTOS.h"\r
76 #include "task.h"\r
77 #include "timers.h"\r
78 \r
79 /* Demo program include files. */\r
80 #include "TaskNotify.h"\r
81 \r
82 #define notifyTASK_PRIORITY             ( tskIDLE_PRIORITY )\r
83 \r
84 /*-----------------------------------------------------------*/\r
85 \r
86 /*\r
87  * Implementation of the task that gets notified.\r
88  */\r
89 static void prvNotifiedTask( void *pvParameters );\r
90 \r
91 /*\r
92  * Performs a few initial tests that can be done prior to creating the second\r
93  * task.\r
94  */\r
95 static void prvSingleTaskTests( void );\r
96 \r
97 /*\r
98  * Software timer callback function from which xTaskNotify() is called.\r
99  */\r
100 static void prvNotifyingTimer( TimerHandle_t xTimer );\r
101 \r
102 /*\r
103  * Utility function to create pseudo random numbers.\r
104  */\r
105 static UBaseType_t prvRand( void );\r
106 \r
107 /*-----------------------------------------------------------*/\r
108 \r
109 /* Used to latch errors during the test's execution. */\r
110 static BaseType_t xErrorStatus = pdPASS;\r
111 \r
112 /* Used to ensure the task has not stalled. */\r
113 static volatile uint32_t ulNotifyCycleCount = 0;\r
114 \r
115 /* The handle of the task that receives the notifications. */\r
116 static TaskHandle_t xTaskToNotify = NULL;\r
117 \r
118 /* Used to count the notifications sent to the task from a software timer and\r
119 the number of notifications received by the task from the software timer.  The\r
120 two should stay synchronised. */\r
121 static uint32_t ulTimerNotificationsReceived = 0UL, ulTimerNotificationsSent = 0UL;\r
122 \r
123 /* The timer used to notify the task. */\r
124 static TimerHandle_t xTimer = NULL;\r
125 \r
126 /*-----------------------------------------------------------*/\r
127 \r
128 void vStartTaskNotifyTask( void  )\r
129 {\r
130         /* Create the task that performs some tests by itself, then loops around\r
131         being notified by both a software timer and an interrupt. */\r
132         xTaskCreate( prvNotifiedTask, "Notified", configMINIMAL_STACK_SIZE, NULL, notifyTASK_PRIORITY, &xTaskToNotify );\r
133 }\r
134 /*-----------------------------------------------------------*/\r
135 \r
136 static void prvSingleTaskTests( void )\r
137 {\r
138 const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );\r
139 BaseType_t xReturned;\r
140 uint32_t ulNotifiedValue, ulLoop, ulNotifyingValue;\r
141 TickType_t xTimeOnEntering;\r
142 const uint32_t ulFirstNotifiedConst = 100001UL, ulSecondNotifiedValueConst = 5555UL, ulMaxLoops = 5UL;\r
143 const uint32_t ulBit0 = 0x01UL, ulBit1 = 0x02UL;\r
144 \r
145         /* -------------------------------------------------------------------------\r
146         Check blocking when there are no notifications. */\r
147         xTimeOnEntering = xTaskGetTickCount();\r
148         xReturned = xTaskNotifyWait( ULONG_MAX, 0, &ulNotifiedValue, xTicksToWait );\r
149 \r
150         /* Should have blocked for the entire block time. */\r
151         if( ( xTaskGetTickCount() - xTimeOnEntering ) < xTicksToWait )\r
152         {\r
153                 xErrorStatus = pdFAIL;\r
154         }\r
155         configASSERT( xReturned == pdFAIL );\r
156         configASSERT( ulNotifiedValue == 0UL );\r
157 \r
158 \r
159 \r
160 \r
161         /* -------------------------------------------------------------------------\r
162         Check no blocking when notifications are pending.  First notify itself -\r
163         this would not be a normal thing to do and is done here for test purposes\r
164         only. */\r
165         xReturned = xTaskNotify( xTaskToNotify, ulFirstNotifiedConst, eSetValueWithoutOverwrite );\r
166 \r
167         /* Even through the 'without overwrite' action was used the update should\r
168         have been successful. */\r
169         configASSERT( xReturned == pdPASS );\r
170 \r
171         /* The task should now have a notification pending, and so not time out. */\r
172         xTimeOnEntering = xTaskGetTickCount();\r
173         xReturned = xTaskNotifyWait( ULONG_MAX, 0, &ulNotifiedValue, xTicksToWait );\r
174 \r
175         if( ( xTaskGetTickCount() - xTimeOnEntering ) >= xTicksToWait )\r
176         {\r
177                 xErrorStatus = pdFAIL;\r
178         }\r
179 \r
180         /* The task should have been notified, and the notified value should\r
181         be equal to ulFirstNotifiedConst. */\r
182         configASSERT( xReturned == pdPASS );\r
183         configASSERT( ulNotifiedValue == ulFirstNotifiedConst );\r
184 \r
185         /* Incremented to show the task is still running. */\r
186         ulNotifyCycleCount++;\r
187 \r
188 \r
189 \r
190 \r
191 \r
192         /*--------------------------------------------------------------------------\r
193         Check the non-overwriting functionality.  The notification is done twice\r
194         using two different notification values.  The action says don't overwrite so\r
195         only the first notification should pass and the value read back should also\r
196         be that used with the first notification. */\r
197         xReturned = xTaskNotify( xTaskToNotify, ulFirstNotifiedConst, eSetValueWithoutOverwrite );\r
198         configASSERT( xReturned == pdPASS );\r
199 \r
200         xReturned = xTaskNotify( xTaskToNotify, ulSecondNotifiedValueConst, eSetValueWithoutOverwrite );\r
201         configASSERT( xReturned == pdFAIL );\r
202 \r
203         /* Waiting for the notification should now return immediately so a block\r
204         time of zero is used. */\r
205         xReturned = xTaskNotifyWait( ULONG_MAX, 0, &ulNotifiedValue, 0 );\r
206 \r
207         configASSERT( xReturned == pdPASS );\r
208         configASSERT( ulNotifiedValue == ulFirstNotifiedConst );\r
209 \r
210 \r
211 \r
212 \r
213 \r
214         /*--------------------------------------------------------------------------\r
215         Do the same again, only this time use the overwriting version.  This time\r
216         both notifications should pass, and the value written the second time should\r
217         overwrite the value written the first time, and so be the value that is read\r
218         back. */\r
219         xReturned = xTaskNotify( xTaskToNotify, ulFirstNotifiedConst, eSetValueWithOverwrite );\r
220         configASSERT( xReturned == pdPASS );\r
221         xReturned = xTaskNotify( xTaskToNotify, ulSecondNotifiedValueConst, eSetValueWithOverwrite );\r
222         configASSERT( xReturned == pdPASS );\r
223         xReturned = xTaskNotifyWait( ULONG_MAX, 0, &ulNotifiedValue, 0 );\r
224         configASSERT( xReturned == pdPASS );\r
225         configASSERT( ulNotifiedValue == ulSecondNotifiedValueConst );\r
226 \r
227 \r
228 \r
229 \r
230         /*--------------------------------------------------------------------------\r
231         Check notifications with no action pass without updating the value.  Even\r
232         though ulFirstNotifiedConst is used as the value the value read back should\r
233         remain at ulSecondNotifiedConst. */\r
234         xReturned = xTaskNotify( xTaskToNotify, ulFirstNotifiedConst, eNoAction );\r
235         configASSERT( xReturned == pdPASS );\r
236         xReturned = xTaskNotifyWait( ULONG_MAX, 0, &ulNotifiedValue, 0 );\r
237         configASSERT( ulNotifiedValue == ulSecondNotifiedValueConst );\r
238 \r
239 \r
240 \r
241 \r
242         /*--------------------------------------------------------------------------\r
243         Check incrementing values.  Send ulMaxLoop increment notifications, then\r
244         ensure the received value is as expected - which should be\r
245         ulSecondNotificationValueConst plus how ever many times to loop iterated. */\r
246         for( ulLoop = 0; ulLoop < ulMaxLoops; ulLoop++ )\r
247         {\r
248                 xReturned = xTaskNotify( xTaskToNotify, 0, eIncrement );\r
249                 configASSERT( xReturned == pdPASS );\r
250         }\r
251 \r
252         xReturned = xTaskNotifyWait( ULONG_MAX, 0, &ulNotifiedValue, 0 );\r
253         configASSERT( xReturned == pdPASS );\r
254         configASSERT( ulNotifiedValue == ( ulSecondNotifiedValueConst + ulMaxLoops ) );\r
255 \r
256         /* Should not be any notifications pending now. */\r
257         xReturned = xTaskNotifyWait( 0, 0, &ulNotifiedValue, 0 );\r
258         configASSERT( xReturned == pdFAIL );\r
259 \r
260 \r
261 \r
262 \r
263         /*--------------------------------------------------------------------------\r
264         Check all bits can be set by notifying the task with one additional bit set\r
265         on each notification, and exiting the loop when all the bits are found to be\r
266         set.  As there are 32-bits the loop should execute 32 times before all the\r
267         bits are found to be set. */\r
268         ulNotifyingValue = 0x01;\r
269         ulLoop = 0;\r
270 \r
271         /* Start with all bits clear. */\r
272         xTaskNotifyWait( ULONG_MAX, 0, &ulNotifiedValue, 0 );\r
273 \r
274         do\r
275         {\r
276                 /* Set the next bit in the task's notified value. */\r
277                 xTaskNotify( xTaskToNotify, ulNotifyingValue, eSetBits );\r
278 \r
279                 /* Wait for the notified value - which of course will already be\r
280                 available.  Don't clear the bits on entry or exit as this loop is exited\r
281                 when all the bits are set. */\r
282                 xReturned = xTaskNotifyWait( 0, 0, &ulNotifiedValue, 0 );\r
283                 configASSERT( xReturned == pdPASS );\r
284 \r
285                 ulLoop++;\r
286 \r
287                 /* Use the next bit on the next iteration around this loop. */\r
288                 ulNotifyingValue <<= 1UL;\r
289 \r
290         } while ( ulNotifiedValue != ULONG_MAX );\r
291 \r
292         /* As a 32-bit value was used the loop should have executed 32 times before\r
293         all the bits were set. */\r
294         configASSERT( ulLoop == 32 );\r
295 \r
296 \r
297 \r
298 \r
299         /*--------------------------------------------------------------------------\r
300         Check bits are cleared on entry but not on exit when a notification fails\r
301         to arrive before timing out - both with and without a timeout value.  Wait\r
302         for the notification again - but this time it is not given by anything and\r
303         should return pdFAIL.  The parameters are set to clear bit zero on entry and\r
304         bit one on exit.  As no notification was received only the bit cleared on\r
305         entry should actually get cleared. */\r
306         xReturned = xTaskNotifyWait( ulBit0, ulBit1, &ulNotifiedValue, xTicksToWait );\r
307         configASSERT( xReturned == pdFAIL );\r
308 \r
309         /* Notify the task with no action so as not to update the bits even though\r
310         ULONG_MAX is used as the notification value. */\r
311         xTaskNotify( xTaskToNotify, ULONG_MAX, eNoAction );\r
312 \r
313         /* Reading back the value should should find bit 0 is clear, as this was\r
314         cleared on entry, but bit 1 is not clear as it will not have been cleared on\r
315         exit as no notification was received. */\r
316         xReturned = xTaskNotifyWait( 0x00UL, 0x00UL, &ulNotifiedValue, 0 );\r
317         configASSERT( xReturned == pdPASS );\r
318         configASSERT( ulNotifiedValue == ( ULONG_MAX & ~ulBit0 ) );\r
319 \r
320 \r
321 \r
322 \r
323 \r
324         /*--------------------------------------------------------------------------\r
325         Now try clearing the bit on exit.  For that to happen a notification must be\r
326         received, so the task is notified first. */\r
327         xTaskNotify( xTaskToNotify, 0, eNoAction );\r
328         xTaskNotifyWait( 0x00, ulBit1, &ulNotifiedValue, 0 );\r
329 \r
330         /* However as the bit is cleared on exit, after the returned notification\r
331         value is set, the returned notification value should not have the bit\r
332         cleared... */\r
333         configASSERT( ulNotifiedValue == ( ULONG_MAX & ~ulBit0 ) );\r
334 \r
335         /* ...but reading the value back again should find that the bit was indeed\r
336         cleared internally.  The returned value should be pdFAIL however as nothing\r
337         has notified the task in the mean time. */\r
338         xReturned = xTaskNotifyWait( 0x00, 0x00, &ulNotifiedValue, 0 );\r
339         configASSERT( xReturned == pdFAIL );\r
340         configASSERT( ulNotifiedValue == ( ULONG_MAX & ~( ulBit0 | ulBit1 ) ) );\r
341 \r
342 \r
343 \r
344         /* Incremented to show the task is still running. */\r
345         ulNotifyCycleCount++;\r
346 \r
347         /* Leave all bits cleared. */\r
348         xTaskNotifyWait( ULONG_MAX, 0, NULL, 0 );\r
349 }\r
350 /*-----------------------------------------------------------*/\r
351 \r
352 static void prvNotifyingTimer( TimerHandle_t xTimer )\r
353 {\r
354         ( void ) xTimer;\r
355 \r
356         xTaskNotifyGive( xTaskToNotify );\r
357 \r
358         /* This value is also incremented from an interrupt. */\r
359         taskENTER_CRITICAL();\r
360         {\r
361                 ulTimerNotificationsSent++;\r
362         }\r
363         taskEXIT_CRITICAL();\r
364 }\r
365 /*-----------------------------------------------------------*/\r
366 \r
367 static void prvNotifiedTask( void *pvParameters )\r
368 {\r
369 const TickType_t xMaxPeriod = pdMS_TO_TICKS( 90 ), xMinPeriod = pdMS_TO_TICKS( 10 ), xDontBlock = 0;\r
370 TickType_t xPeriod;\r
371 \r
372         /* Remove compiler warnings about unused parameters. */\r
373         ( void ) pvParameters;\r
374 \r
375         /* Run a few tests that can be done from a single task before entering the\r
376         main loop. */\r
377         prvSingleTaskTests();\r
378 \r
379         /* Create the software timer that is used to send notifications to this\r
380         task.  Notifications are also received from an interrupt. */\r
381         xTimer = xTimerCreate( "Notifier", xMaxPeriod, pdFALSE, NULL, prvNotifyingTimer );\r
382 \r
383         for( ;; )\r
384         {\r
385                 /* Start the timer again with a different period.  Sometimes the period\r
386                 will be higher than the tasks block time, sometimes it will be lower\r
387                 than the tasks block time. */\r
388                 xPeriod = prvRand() % xMaxPeriod;\r
389                 if( xPeriod < xMinPeriod )\r
390                 {\r
391                         xPeriod = xMinPeriod;\r
392                 }\r
393 \r
394                 xTimerChangePeriod( xTimer, xPeriod, portMAX_DELAY );\r
395                 xTimerStart( xTimer, portMAX_DELAY );\r
396 \r
397                 /* Block waiting for the notification again with a different period.\r
398                 Sometimes the period will be higher than the tasks block time, sometimes\r
399                 it will be lower than the tasks block time. */\r
400                 xPeriod = prvRand() % xMaxPeriod;\r
401                 if( xPeriod < xMinPeriod )\r
402                 {\r
403                         xPeriod = xMinPeriod;\r
404                 }\r
405 \r
406                 /* Block to wait for a notification but without clearing the\r
407                 notification count, so only add one to the count of received\r
408                 notifications as any other notifications will remain pending. */\r
409                 if( ulTaskNotifyTake( pdFALSE, xPeriod ) != 0 )\r
410                 {\r
411                         ulTimerNotificationsReceived++;\r
412                 }\r
413 \r
414 \r
415                 /* Take a notification without clearing again, but this time without a\r
416                 block time specified. */\r
417                 if( ulTaskNotifyTake( pdFALSE, xDontBlock ) != 0 )\r
418                 {\r
419                         ulTimerNotificationsReceived++;\r
420                 }\r
421 \r
422                 /* Wait for the next notification from the timer, clearing all\r
423                 notifications if one is received, so this time adding the total number\r
424                 of notifications that were pending as none will be left pending after\r
425                 the function call. */\r
426                 ulTimerNotificationsReceived += ulTaskNotifyTake( pdTRUE, xPeriod );\r
427 \r
428                 /* Wait for the next notification again, clearing all notifications if\r
429                 one is received, but this time blocking indefinitely. */\r
430                 ulTimerNotificationsReceived += ulTaskNotifyTake( pdTRUE, portMAX_DELAY );\r
431 \r
432                 /* Incremented to show the task is still running. */\r
433                 ulNotifyCycleCount++;\r
434         }\r
435 }\r
436 /*-----------------------------------------------------------*/\r
437 \r
438 void xNotifyTaskFromISR( void )\r
439 {\r
440 static BaseType_t xCallCount = 0;\r
441 const BaseType_t xCallInterval = pdMS_TO_TICKS( 50 );\r
442 \r
443         /* The task performs some tests before starting the timer that gives the\r
444         notification from this interrupt.  If the timer has not been created yet\r
445         then the initial tests have not yet completed and the notification should\r
446         not be sent. */\r
447         if( xTimer != NULL )\r
448         {\r
449                 xCallCount++;\r
450 \r
451                 if( xCallCount >= xCallInterval )\r
452                 {\r
453                         /* It is time to 'give' the notification again. */\r
454                         xCallCount = 0;\r
455 \r
456                         xTaskNotifyGiveFromISR( xTaskToNotify, NULL );\r
457                         ulTimerNotificationsSent++;\r
458                 }\r
459         }\r
460 }\r
461 /*-----------------------------------------------------------*/\r
462 \r
463 /* This is called to check the created tasks are still running and have not\r
464 detected any errors. */\r
465 BaseType_t xAreTaskNotificationTasksStillRunning( void )\r
466 {\r
467 static uint32_t ulLastNotifyCycleCount = 0;\r
468 const uint32_t ulMaxSendReceiveDeviation = 5UL;\r
469 \r
470         /* Check the cycle count is still incrementing to ensure the task is still\r
471         actually running. */\r
472         if( ulLastNotifyCycleCount == ulNotifyCycleCount )\r
473         {\r
474                 xErrorStatus = pdFAIL;\r
475         }\r
476         else\r
477         {\r
478                 ulLastNotifyCycleCount = ulNotifyCycleCount;\r
479         }\r
480 \r
481         /* Check the count of 'takes' from the software timer is keeping track with\r
482         the amount of 'gives'. */\r
483         if( ulTimerNotificationsSent > ulTimerNotificationsSent )\r
484         {\r
485                 if( ( ulTimerNotificationsSent - ulTimerNotificationsReceived ) > ulMaxSendReceiveDeviation )\r
486                 {\r
487                         xErrorStatus = pdFAIL;\r
488                 }\r
489         }\r
490 \r
491         return xErrorStatus;\r
492 }\r
493 /*-----------------------------------------------------------*/\r
494 \r
495 static UBaseType_t prvRand( void )\r
496 {\r
497         static uint32_t ulNextRand = ( uint32_t ) prvRand;\r
498 \r
499         const uint32_t ulMultiplier = 0x015a4e35UL, ulIncrement = 1UL;\r
500 \r
501         /* Utility function to generate a pseudo random number. */\r
502         ulNextRand = ( ulMultiplier * ulNextRand ) + ulIncrement;\r
503         return( ( int ) ( ulNextRand >> 16UL ) & 0x7fffUL );\r
504 }\r
505 /*-----------------------------------------------------------*/\r