/* * FreeRTOS V202212.00 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * https://www.FreeRTOS.org * https://github.com/FreeRTOS * */ /* * Tests the behaviour of direct task notifications. */ /* Standard includes. */ #include /* Scheduler include files. */ #include "FreeRTOS.h" #include "task.h" #include "timers.h" /* Demo program include files. */ #include "TaskNotify.h" /* Allow parameters to be overridden on a demo by demo basis. */ #ifndef notifyNOTIFIED_TASK_STACK_SIZE #define notifyNOTIFIED_TASK_STACK_SIZE configMINIMAL_STACK_SIZE #endif #define notifyTASK_PRIORITY ( tskIDLE_PRIORITY ) /* Constants used in tests when setting/clearing bits. */ #define notifyUINT32_MAX ( ( uint32_t ) 0xffffffff ) #define notifyUINT32_HIGH_BYTE ( ( uint32_t ) 0xff000000 ) #define notifyUINT32_LOW_BYTE ( ( uint32_t ) 0x000000ff ) #define notifySUSPENDED_TEST_TIMER_PERIOD pdMS_TO_TICKS( 50 ) /*-----------------------------------------------------------*/ /* * Implementation of the task that gets notified. */ static void prvNotifiedTask( void * pvParameters ); /* * Performs a few initial tests that can be done prior to creating the second * task. */ static void prvSingleTaskTests( void ); /* * Software timer callback function from which xTaskNotify() is called. */ static void prvNotifyingTimer( TimerHandle_t xTimer ); /* * Utility function to create pseudo random numbers. */ static UBaseType_t prvRand( void ); /* * Callback for a timer that is used during preliminary testing. The timer * tests the behaviour when 1: a task waiting for a notification is suspended * and then resumed without ever receiving a notification, and 2: when a task * waiting for a notification receives a notification while it is suspended. */ static void prvSuspendedTaskTimerTestCallback( TimerHandle_t xExpiredTimer ); /*-----------------------------------------------------------*/ /* Used to latch errors during the test's execution. */ static BaseType_t xErrorStatus = pdPASS; /* Used to ensure the task has not stalled. */ static volatile uint32_t ulNotifyCycleCount = 0; /* The handle of the task that receives the notifications. */ static TaskHandle_t xTaskToNotify = NULL; /* Used to count the notifications sent to the task from a software timer and * the number of notifications received by the task from the software timer. The * two should stay synchronised. */ static uint32_t ulTimerNotificationsReceived = 0UL, ulTimerNotificationsSent = 0UL; /* The timer used to notify the task. */ static TimerHandle_t xTimer = NULL; /* Used by the pseudo random number generating function. */ static size_t uxNextRand = 0; /*-----------------------------------------------------------*/ void vStartTaskNotifyTask( void ) { /* Create the task that performs some tests by itself, then loops around * being notified by both a software timer and an interrupt. */ xTaskCreate( prvNotifiedTask, /* Function that implements the task. */ "Notified", /* Text name for the task - for debugging only - not used by the kernel. */ notifyNOTIFIED_TASK_STACK_SIZE, /* Task's stack size in words, not bytes!. */ NULL, /* Task parameter, not used in this case. */ notifyTASK_PRIORITY, /* Task priority, 0 is the lowest. */ &xTaskToNotify ); /* Used to pass a handle to the task out is needed, otherwise set to NULL. */ /* Pseudo seed the random number generator. */ uxNextRand = ( size_t ) prvRand; } /*-----------------------------------------------------------*/ static void prvSingleTaskTests( void ) { const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL ); BaseType_t xReturned; uint32_t ulNotifiedValue, ulLoop, ulNotifyingValue, ulPreviousValue, ulExpectedValue; TickType_t xTimeOnEntering; const uint32_t ulFirstNotifiedConst = 100001UL, ulSecondNotifiedValueConst = 5555UL, ulMaxLoops = 5UL; const uint32_t ulBit0 = 0x01UL, ulBit1 = 0x02UL; TimerHandle_t xSingleTaskTimer; /* ------------------------------------------------------------------------ * Check blocking when there are no notifications. */ xTimeOnEntering = xTaskGetTickCount(); xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, xTicksToWait ); ( void ) xReturned; /* In case configASSERT() is not defined. */ /* Should have blocked for the entire block time. */ if( ( xTaskGetTickCount() - xTimeOnEntering ) < xTicksToWait ) { xErrorStatus = pdFAIL; } configASSERT( xReturned == pdFAIL ); configASSERT( ulNotifiedValue == 0UL ); ( void ) xReturned; /* In case configASSERT() is not defined. */ ( void ) ulNotifiedValue; /* ------------------------------------------------------------------------ * Check no blocking when notifications are pending. First notify itself - * this would not be a normal thing to do and is done here for test purposes * only. */ xReturned = xTaskNotifyAndQuery( xTaskToNotify, ulFirstNotifiedConst, eSetValueWithoutOverwrite, &ulPreviousValue ); /* Even through the 'without overwrite' action was used the update should * have been successful. */ configASSERT( xReturned == pdPASS ); ( void ) xReturned; /* In case configASSERT() is not defined. */ /* No bits should have been pending previously. */ configASSERT( ulPreviousValue == 0 ); ( void ) ulPreviousValue; /* The task should now have a notification pending, and so not time out. */ xTimeOnEntering = xTaskGetTickCount(); xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, xTicksToWait ); if( ( xTaskGetTickCount() - xTimeOnEntering ) >= xTicksToWait ) { xErrorStatus = pdFAIL; } /* The task should have been notified, and the notified value should * be equal to ulFirstNotifiedConst. */ configASSERT( xReturned == pdPASS ); configASSERT( ulNotifiedValue == ulFirstNotifiedConst ); ( void ) xReturned; /* In case configASSERT() is not defined. */ ( void ) ulNotifiedValue; /* Incremented to show the task is still running. */ ulNotifyCycleCount++; /*------------------------------------------------------------------------- * Check the non-overwriting functionality. The notification is done twice * using two different notification values. The action says don't overwrite so * only the first notification should pass and the value read back should also * be that used with the first notification. */ xReturned = xTaskNotify( xTaskToNotify, ulFirstNotifiedConst, eSetValueWithoutOverwrite ); configASSERT( xReturned == pdPASS ); ( void ) xReturned; /* In case configASSERT() is not defined. */ xReturned = xTaskNotify( xTaskToNotify, ulSecondNotifiedValueConst, eSetValueWithoutOverwrite ); configASSERT( xReturned == pdFAIL ); ( void ) xReturned; /* In case configASSERT() is not defined. */ /* Waiting for the notification should now return immediately so a block * time of zero is used. */ xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 ); configASSERT( xReturned == pdPASS ); configASSERT( ulNotifiedValue == ulFirstNotifiedConst ); ( void ) xReturned; /* In case configASSERT() is not defined. */ ( void ) ulNotifiedValue; /*------------------------------------------------------------------------- * Do the same again, only this time use the overwriting version. This time * both notifications should pass, and the value written the second time should * overwrite the value written the first time, and so be the value that is read * back. */ xReturned = xTaskNotify( xTaskToNotify, ulFirstNotifiedConst, eSetValueWithOverwrite ); configASSERT( xReturned == pdPASS ); ( void ) xReturned; /* In case configASSERT() is not defined. */ xReturned = xTaskNotify( xTaskToNotify, ulSecondNotifiedValueConst, eSetValueWithOverwrite ); configASSERT( xReturned == pdPASS ); ( void ) xReturned; /* In case configASSERT() is not defined. */ xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 ); configASSERT( xReturned == pdPASS ); ( void ) xReturned; /* In case configASSERT() is not defined. */ configASSERT( ulNotifiedValue == ulSecondNotifiedValueConst ); ( void ) ulNotifiedValue; /*------------------------------------------------------------------------- * Check notifications with no action pass without updating the value. Even * though ulFirstNotifiedConst is used as the value the value read back should * remain at ulSecondNotifiedConst. */ xReturned = xTaskNotify( xTaskToNotify, ulFirstNotifiedConst, eNoAction ); configASSERT( xReturned == pdPASS ); ( void ) xReturned; /* In case configASSERT() is not defined. */ xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 ); configASSERT( ulNotifiedValue == ulSecondNotifiedValueConst ); ( void ) ulNotifiedValue; /* In case configASSERT() is not defined. */ /*------------------------------------------------------------------------- * Check incrementing values. Send ulMaxLoop increment notifications, then * ensure the received value is as expected - which should be * ulSecondNotificationValueConst plus how ever many times to loop iterated. */ for( ulLoop = 0; ulLoop < ulMaxLoops; ulLoop++ ) { xReturned = xTaskNotify( xTaskToNotify, 0, eIncrement ); configASSERT( xReturned == pdPASS ); ( void ) xReturned; /* In case configASSERT() is not defined. */ } xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 ); configASSERT( xReturned == pdPASS ); configASSERT( ulNotifiedValue == ( ulSecondNotifiedValueConst + ulMaxLoops ) ); ( void ) xReturned; /* In case configASSERT() is not defined. */ ( void ) ulNotifiedValue; /* Should not be any notifications pending now. */ xReturned = xTaskNotifyWait( 0, 0, &ulNotifiedValue, 0 ); configASSERT( xReturned == pdFAIL ); ( void ) xReturned; /* In case configASSERT() is not defined. */ ( void ) ulNotifiedValue; /*------------------------------------------------------------------------- * Check all bits can be set by notifying the task with one additional bit set * on each notification, and exiting the loop when all the bits are found to be * set. As there are 32-bits the loop should execute 32 times before all the * bits are found to be set. */ ulNotifyingValue = 0x01; ulLoop = 0; /* Start with all bits clear. */ xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 ); do { /* Set the next bit in the task's notified value. */ xTaskNotify( xTaskToNotify, ulNotifyingValue, eSetBits ); /* Wait for the notified value - which of course will already be * available. Don't clear the bits on entry or exit as this loop is exited * when all the bits are set. */ xReturned = xTaskNotifyWait( 0, 0, &ulNotifiedValue, 0 ); configASSERT( xReturned == pdPASS ); ( void ) xReturned; /* In case configASSERT() is not defined. */ ulLoop++; /* Use the next bit on the next iteration around this loop. */ ulNotifyingValue <<= 1UL; } while( ulNotifiedValue != notifyUINT32_MAX ); /* As a 32-bit value was used the loop should have executed 32 times before * all the bits were set. */ configASSERT( ulLoop == 32 ); /*------------------------------------------------------------------------- * Check bits are cleared on entry but not on exit when a notification fails * to arrive before timing out - both with and without a timeout value. Wait * for the notification again - but this time it is not given by anything and * should return pdFAIL. The parameters are set to clear bit zero on entry and * bit one on exit. As no notification was received only the bit cleared on * entry should actually get cleared. */ xReturned = xTaskNotifyWait( ulBit0, ulBit1, &ulNotifiedValue, xTicksToWait ); configASSERT( xReturned == pdFAIL ); ( void ) xReturned; /* In case configASSERT() is not defined. */ /* Notify the task with no action so as not to update the bits even though * notifyUINT32_MAX is used as the notification value. */ xTaskNotify( xTaskToNotify, notifyUINT32_MAX, eNoAction ); /* Reading back the value should should find bit 0 is clear, as this was * cleared on entry, but bit 1 is not clear as it will not have been cleared on * exit as no notification was received. */ xReturned = xTaskNotifyWait( 0x00UL, 0x00UL, &ulNotifiedValue, 0 ); configASSERT( xReturned == pdPASS ); configASSERT( ulNotifiedValue == ( notifyUINT32_MAX & ~ulBit0 ) ); ( void ) xReturned; /* In case configASSERT() is not defined. */ /*------------------------------------------------------------------------- * Now try clearing the bit on exit. For that to happen a notification must be * received, so the task is notified first. */ xTaskNotify( xTaskToNotify, 0, eNoAction ); xTaskNotifyWait( 0x00, ulBit1, &ulNotifiedValue, 0 ); /* However as the bit is cleared on exit, after the returned notification * value is set, the returned notification value should not have the bit * cleared... */ configASSERT( ulNotifiedValue == ( notifyUINT32_MAX & ~ulBit0 ) ); /* ...but reading the value back again should find that the bit was indeed * cleared internally. The returned value should be pdFAIL however as nothing * has notified the task in the mean time. */ xReturned = xTaskNotifyWait( 0x00, 0x00, &ulNotifiedValue, 0 ); configASSERT( xReturned == pdFAIL ); configASSERT( ulNotifiedValue == ( notifyUINT32_MAX & ~( ulBit0 | ulBit1 ) ) ); ( void ) xReturned; /* In case configASSERT() is not defined. */ /*------------------------------------------------------------------------- * Now try querying the previous value while notifying a task. */ xTaskNotifyAndQuery( xTaskToNotify, 0x00, eSetBits, &ulPreviousValue ); configASSERT( ulNotifiedValue == ( notifyUINT32_MAX & ~( ulBit0 | ulBit1 ) ) ); /* Clear all bits. */ xTaskNotifyWait( 0x00, notifyUINT32_MAX, &ulNotifiedValue, 0 ); xTaskNotifyAndQuery( xTaskToNotify, 0x00, eSetBits, &ulPreviousValue ); configASSERT( ulPreviousValue == 0 ); ulExpectedValue = 0; for( ulLoop = 0x01; ulLoop < 0x80UL; ulLoop <<= 1UL ) { /* Set the next bit up, and expect to receive the last bits set (so * the previous value will not yet have the bit being set this time * around). */ xTaskNotifyAndQuery( xTaskToNotify, ulLoop, eSetBits, &ulPreviousValue ); configASSERT( ulExpectedValue == ulPreviousValue ); ulExpectedValue |= ulLoop; } /* ------------------------------------------------------------------------ * Clear the previous notifications. */ xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 ); /* The task should not have any notifications pending, so an attempt to clear * the notification state should fail. */ configASSERT( xTaskNotifyStateClear( NULL ) == pdFALSE ); /* Get the task to notify itself. This is not a normal thing to do, and is * only done here for test purposes. */ xTaskNotifyAndQuery( xTaskToNotify, ulFirstNotifiedConst, eSetValueWithoutOverwrite, &ulPreviousValue ); /* Now the notification state should be eNotified, so it should now be * possible to clear the notification state. */ configASSERT( xTaskNotifyStateClear( NULL ) == pdTRUE ); configASSERT( xTaskNotifyStateClear( NULL ) == pdFALSE ); /* ------------------------------------------------------------------------ * Clear bits in the notification value. */ /* Get the task to set all bits its own notification value. This is not a * normal thing to do, and is only done here for test purposes. */ xTaskNotify( xTaskToNotify, notifyUINT32_MAX, eSetBits ); /* Now clear the top bytes - the returned value from the first call should * indicate that previously all bits were set. */ configASSERT( ulTaskNotifyValueClear( xTaskToNotify, notifyUINT32_HIGH_BYTE ) == notifyUINT32_MAX ); /* Next clear the bottom bytes - the returned value this time should indicate * that the top byte was clear (before the bottom byte was cleared. */ configASSERT( ulTaskNotifyValueClear( xTaskToNotify, notifyUINT32_LOW_BYTE ) == ( notifyUINT32_MAX & ~notifyUINT32_HIGH_BYTE ) ); /* Next clear all bytes - the returned value should indicate that previously the * high and low bytes were clear. */ configASSERT( ulTaskNotifyValueClear( xTaskToNotify, notifyUINT32_MAX ) == ( notifyUINT32_MAX & ~notifyUINT32_HIGH_BYTE & ~notifyUINT32_LOW_BYTE ) ); /* Now all bits should be clear. */ configASSERT( ulTaskNotifyValueClear( xTaskToNotify, notifyUINT32_MAX ) == 0 ); configASSERT( ulTaskNotifyValueClear( xTaskToNotify, 0UL ) == 0 ); configASSERT( ulTaskNotifyValueClear( xTaskToNotify, notifyUINT32_MAX ) == 0 ); /* Now the notification state should be eNotified, so it should now be * possible to clear the notification state. */ configASSERT( xTaskNotifyStateClear( NULL ) == pdTRUE ); configASSERT( xTaskNotifyStateClear( NULL ) == pdFALSE ); /* ------------------------------------------------------------------------ * Create a timer that will try notifying this task while it is suspended. */ xSingleTaskTimer = xTimerCreate( "SingleNotify", notifySUSPENDED_TEST_TIMER_PERIOD, pdFALSE, NULL, prvSuspendedTaskTimerTestCallback ); configASSERT( xSingleTaskTimer ); /* Incremented to show the task is still running. */ ulNotifyCycleCount++; /* Ensure no notifications are pending. */ xTaskNotifyWait( notifyUINT32_MAX, 0, NULL, 0 ); /* Raise the task's priority so it can suspend itself before the timer * expires. */ vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 ); /* Start the timer that will try notifying this task while it is * suspended, then wait for a notification. The first time the callback * executes the timer will suspend the task, then resume the task, without * ever sending a notification to the task. */ ulNotifiedValue = 0; xTimerStart( xSingleTaskTimer, portMAX_DELAY ); /* Check a notification is not received. */ xReturned = xTaskNotifyWait( 0, 0, &ulNotifiedValue, portMAX_DELAY ); configASSERT( xReturned == pdFALSE ); configASSERT( ulNotifiedValue == 0 ); ( void ) xReturned; /* In case configASSERT() is not defined. */ /* Incremented to show the task is still running. */ ulNotifyCycleCount++; /* Start the timer that will try notifying this task while it is * suspended, then wait for a notification. The second time the callback * executes the timer will suspend the task, notify the task, then resume the * task (previously it was suspended and resumed without being notified). */ xTimerStart( xSingleTaskTimer, portMAX_DELAY ); /* Check a notification is received. */ xReturned = xTaskNotifyWait( 0, 0, &ulNotifiedValue, portMAX_DELAY ); configASSERT( xReturned == pdPASS ); ( void ) xReturned; /* In case configASSERT() is not defined. */ configASSERT( ulNotifiedValue != 0 ); /* Return the task to its proper priority and delete the timer as it is * not used again. */ vTaskPrioritySet( NULL, notifyTASK_PRIORITY ); xTimerDelete( xSingleTaskTimer, portMAX_DELAY ); /* Incremented to show the task is still running. */ ulNotifyCycleCount++; /* Leave all bits cleared. */ xTaskNotifyWait( notifyUINT32_MAX, 0, NULL, 0 ); } /*-----------------------------------------------------------*/ static void prvSuspendedTaskTimerTestCallback( TimerHandle_t xExpiredTimer ) { static uint32_t ulCallCount = 0; /* Remove compiler warnings about unused parameters. */ ( void ) xExpiredTimer; /* Callback for a timer that is used during preliminary testing. The timer * tests the behaviour when 1: a task waiting for a notification is suspended * and then resumed without ever receiving a notification, and 2: when a task * waiting for a notification receives a notification while it is suspended. */ if( ulCallCount == 0 ) { vTaskSuspend( xTaskToNotify ); configASSERT( eTaskGetState( xTaskToNotify ) == eSuspended ); vTaskResume( xTaskToNotify ); } else { vTaskSuspend( xTaskToNotify ); /* Sending a notification while the task is suspended should pass, but * not cause the task to resume. ulCallCount is just used as a convenient * non-zero value. */ xTaskNotify( xTaskToNotify, ulCallCount, eSetValueWithOverwrite ); /* Make sure giving the notification didn't resume the task. */ configASSERT( eTaskGetState( xTaskToNotify ) == eSuspended ); vTaskResume( xTaskToNotify ); } ulCallCount++; } /*-----------------------------------------------------------*/ static void prvNotifyingTimer( TimerHandle_t xNotUsed ) { ( void ) xNotUsed; xTaskNotifyGive( xTaskToNotify ); /* This value is also incremented from an interrupt. */ taskENTER_CRITICAL(); { ulTimerNotificationsSent++; } taskEXIT_CRITICAL(); } /*-----------------------------------------------------------*/ static void prvNotifiedTask( void * pvParameters ) { const TickType_t xMaxPeriod = pdMS_TO_TICKS( 90 ), xMinPeriod = pdMS_TO_TICKS( 10 ), xDontBlock = 0; TickType_t xPeriod; const uint32_t ulCyclesToRaisePriority = 50UL; /* Remove compiler warnings about unused parameters. */ ( void ) pvParameters; /* Run a few tests that can be done from a single task before entering the * main loop. */ prvSingleTaskTests(); /* Create the software timer that is used to send notifications to this * task. Notifications are also received from an interrupt. */ xTimer = xTimerCreate( "Notifier", xMaxPeriod, pdFALSE, NULL, prvNotifyingTimer ); for( ; ; ) { /* Start the timer again with a different period. Sometimes the period * will be higher than the task's block time, sometimes it will be lower * than the task's block time. */ xPeriod = prvRand() % xMaxPeriod; if( xPeriod < xMinPeriod ) { xPeriod = xMinPeriod; } /* Change the timer period and start the timer. */ xTimerChangePeriod( xTimer, xPeriod, portMAX_DELAY ); /* Block waiting for the notification again with a different period. * Sometimes the period will be higher than the task's block time, * sometimes it will be lower than the task's block time. */ xPeriod = prvRand() % xMaxPeriod; if( xPeriod < xMinPeriod ) { xPeriod = xMinPeriod; } /* Block to wait for a notification but without clearing the * notification count, so only add one to the count of received * notifications as any other notifications will remain pending. */ if( ulTaskNotifyTake( pdFALSE, xPeriod ) != 0 ) { ulTimerNotificationsReceived++; } /* Take a notification without clearing again, but this time without a * block time specified. */ if( ulTaskNotifyTake( pdFALSE, xDontBlock ) != 0 ) { ulTimerNotificationsReceived++; } /* Wait for the next notification from the timer, clearing all * notifications if one is received, so this time adding the total number * of notifications that were pending as none will be left pending after * the function call. */ ulTimerNotificationsReceived += ulTaskNotifyTake( pdTRUE, xPeriod ); /* Occasionally raise the priority of the task being notified to test * the path where the task is notified from an ISR and becomes the highest * priority ready state task, but the pxHigherPriorityTaskWoken parameter * is NULL (which it is in the tick hook that sends notifications to this * task). */ if( ( ulNotifyCycleCount % ulCyclesToRaisePriority ) == 0 ) { vTaskPrioritySet( xTaskToNotify, configMAX_PRIORITIES - 1 ); /* Wait for the next notification again, clearing all notifications * if one is received, but this time blocking indefinitely. */ ulTimerNotificationsReceived += ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); /* Reset the priority. */ vTaskPrioritySet( xTaskToNotify, notifyTASK_PRIORITY ); } else { /* Wait for the next notification again, clearing all notifications * if one is received, but this time blocking indefinitely. */ ulTimerNotificationsReceived += ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); } /* Incremented to show the task is still running. */ ulNotifyCycleCount++; } } /*-----------------------------------------------------------*/ void xNotifyTaskFromISR( void ) { static BaseType_t xCallCount = 0, xAPIToUse = 0; const BaseType_t xCallInterval = pdMS_TO_TICKS( 50 ); uint32_t ulPreviousValue; const uint32_t ulUnexpectedValue = 0xff; /* Check the task notification demo tasks were actually created. */ configASSERT( xTaskToNotify ); /* The task performs some tests before starting the timer that gives the * notification from this interrupt. If the timer has not been created yet * then the initial tests have not yet completed and the notification should * not be sent. */ if( xTimer != NULL ) { xCallCount++; if( xCallCount >= xCallInterval ) { /* It is time to 'give' the notification again. */ xCallCount = 0; /* Test using both vTaskNotifyGiveFromISR(), xTaskNotifyFromISR() * and xTaskNotifyAndQueryFromISR(). */ switch( xAPIToUse ) { case 0: vTaskNotifyGiveFromISR( xTaskToNotify, NULL ); xAPIToUse++; break; case 1: xTaskNotifyFromISR( xTaskToNotify, 0, eIncrement, NULL ); xAPIToUse++; break; case 2: ulPreviousValue = ulUnexpectedValue; xTaskNotifyAndQueryFromISR( xTaskToNotify, 0, eIncrement, &ulPreviousValue, NULL ); configASSERT( ulPreviousValue != ulUnexpectedValue ); xAPIToUse = 0; break; default: /* Should never get here!. */ break; } ulTimerNotificationsSent++; } } } /*-----------------------------------------------------------*/ /* This is called to check the created tasks are still running and have not * detected any errors. */ BaseType_t xAreTaskNotificationTasksStillRunning( void ) { static uint32_t ulLastNotifyCycleCount = 0; const uint32_t ulMaxSendReceiveDeviation = 5UL; /* Check the cycle count is still incrementing to ensure the task is still * actually running. */ if( ulLastNotifyCycleCount == ulNotifyCycleCount ) { xErrorStatus = pdFAIL; } else { ulLastNotifyCycleCount = ulNotifyCycleCount; } /* Check the count of 'takes' from the software timer is keeping track with * the amount of 'gives'. */ if( ulTimerNotificationsSent > ulTimerNotificationsReceived ) { if( ( ulTimerNotificationsSent - ulTimerNotificationsReceived ) > ulMaxSendReceiveDeviation ) { xErrorStatus = pdFAIL; } } return xErrorStatus; } /*-----------------------------------------------------------*/ static UBaseType_t prvRand( void ) { const size_t uxMultiplier = ( size_t ) 0x015a4e35, uxIncrement = ( size_t ) 1; /* Utility function to generate a pseudo random number. */ uxNextRand = ( uxMultiplier * uxNextRand ) + uxIncrement; return( ( uxNextRand >> 16 ) & ( ( size_t ) 0x7fff ) ); } /*-----------------------------------------------------------*/