/* | |
FreeRTOS V8.2.3 - Copyright (C) 2015 Real Time Engineers Ltd. | |
All rights reserved | |
VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. | |
This file is part of the FreeRTOS distribution. | |
FreeRTOS is free software; you can redistribute it and/or modify it under | |
the terms of the GNU General Public License (version 2) as published by the | |
Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception. | |
*************************************************************************** | |
>>! NOTE: The modification to the GPL is included to allow you to !<< | |
>>! distribute a combined work that includes FreeRTOS without being !<< | |
>>! obliged to provide the source code for proprietary components !<< | |
>>! outside of the FreeRTOS kernel. !<< | |
*************************************************************************** | |
FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY | |
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
FOR A PARTICULAR PURPOSE. Full license text is available on the following | |
link: http://www.freertos.org/a00114.html | |
*************************************************************************** | |
* * | |
* FreeRTOS provides completely free yet professionally developed, * | |
* robust, strictly quality controlled, supported, and cross * | |
* platform software that is more than just the market leader, it * | |
* is the industry's de facto standard. * | |
* * | |
* Help yourself get started quickly while simultaneously helping * | |
* to support the FreeRTOS project by purchasing a FreeRTOS * | |
* tutorial book, reference manual, or both: * | |
* http://www.FreeRTOS.org/Documentation * | |
* * | |
*************************************************************************** | |
http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading | |
the FAQ page "My application does not run, what could be wrong?". Have you | |
defined configASSERT()? | |
http://www.FreeRTOS.org/support - In return for receiving this top quality | |
embedded software for free we request you assist our global community by | |
participating in the support forum. | |
http://www.FreeRTOS.org/training - Investing in training allows your team to | |
be as productive as possible as early as possible. Now you can receive | |
FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers | |
Ltd, and the world's leading authority on the world's leading RTOS. | |
http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, | |
including FreeRTOS+Trace - an indispensable productivity tool, a DOS | |
compatible FAT file system, and our tiny thread aware UDP/IP stack. | |
http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate. | |
Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS. | |
http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High | |
Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS | |
licenses offer ticketed support, indemnification and commercial middleware. | |
http://www.SafeRTOS.com - High Integrity Systems also provide a safety | |
engineered and independently SIL3 certified version for use in safety and | |
mission critical applications that require provable dependability. | |
1 tab == 4 spaces! | |
*/ | |
/* | |
* Tests the behaviour of direct task notifications. | |
*/ | |
/* Standard includes. */ | |
#include <limits.h> | |
/* Scheduler include files. */ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include "timers.h" | |
/* Demo program include files. */ | |
#include "TaskNotify.h" | |
#define notifyTASK_PRIORITY ( tskIDLE_PRIORITY ) | |
#define notifyUINT32_MAX ( ( uint32_t ) 0xffffffff ) | |
/*-----------------------------------------------------------*/ | |
/* | |
* 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 ); | |
/*-----------------------------------------------------------*/ | |
/* 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, "Notified", configMINIMAL_STACK_SIZE, NULL, notifyTASK_PRIORITY, &xTaskToNotify ); | |
/* 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; | |
/* ------------------------------------------------------------------------- | |
Check blocking when there are no notifications. */ | |
xTimeOnEntering = xTaskGetTickCount(); | |
xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, xTicksToWait ); | |
/* Should have blocked for the entire block time. */ | |
if( ( xTaskGetTickCount() - xTimeOnEntering ) < xTicksToWait ) | |
{ | |
xErrorStatus = pdFAIL; | |
} | |
configASSERT( xReturned == pdFAIL ); | |
configASSERT( ulNotifiedValue == 0UL ); | |
/* ------------------------------------------------------------------------- | |
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 ); | |
/* No bits should have been pending previously. */ | |
configASSERT( ulPreviousValue == 0 ); | |
/* 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 ); | |
/* 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 ); | |
xReturned = xTaskNotify( xTaskToNotify, ulSecondNotifiedValueConst, eSetValueWithoutOverwrite ); | |
configASSERT( xReturned == pdFAIL ); | |
/* 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 ); | |
/*-------------------------------------------------------------------------- | |
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 ); | |
xReturned = xTaskNotify( xTaskToNotify, ulSecondNotifiedValueConst, eSetValueWithOverwrite ); | |
configASSERT( xReturned == pdPASS ); | |
xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 ); | |
configASSERT( xReturned == pdPASS ); | |
configASSERT( ulNotifiedValue == ulSecondNotifiedValueConst ); | |
/*-------------------------------------------------------------------------- | |
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 ); | |
xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 ); | |
configASSERT( ulNotifiedValue == ulSecondNotifiedValueConst ); | |
/*-------------------------------------------------------------------------- | |
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 ); | |
} | |
xReturned = xTaskNotifyWait( notifyUINT32_MAX, 0, &ulNotifiedValue, 0 ); | |
configASSERT( xReturned == pdPASS ); | |
configASSERT( ulNotifiedValue == ( ulSecondNotifiedValueConst + ulMaxLoops ) ); | |
/* Should not be any notifications pending now. */ | |
xReturned = xTaskNotifyWait( 0, 0, &ulNotifiedValue, 0 ); | |
configASSERT( xReturned == pdFAIL ); | |
/*-------------------------------------------------------------------------- | |
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 ); | |
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 ); | |
/* 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 ) ); | |
/*-------------------------------------------------------------------------- | |
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 ) ) ); | |
/*-------------------------------------------------------------------------- | |
Now try querying the previus 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 ); | |
/* Incremented to show the task is still running. */ | |
ulNotifyCycleCount++; | |
/* Leave all bits cleared. */ | |
xTaskNotifyWait( notifyUINT32_MAX, 0, NULL, 0 ); | |
} | |
/*-----------------------------------------------------------*/ | |
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 tasks block time, sometimes it will be lower | |
than the tasks 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 tasks block time, sometimes | |
it will be lower than the tasks 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; | |
/* 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 ) ); | |
} | |
/*-----------------------------------------------------------*/ |