Showing posts with label full demo. Show all posts
Showing posts with label full demo. Show all posts

Friday, June 2, 2017

FreeRTOS 9.0.0 on ArduinoMEGA - Full Demo - Task Notifications

After the Demo Blinky and Demo AVR323 I'm feeling more confident in this port, so let's get serious. Let's make the Full Demo work!

I'll try to follow the same order I used to describe the Full Demo here and get each module to work properly. I'll also use all the files used in AVR323 Demo project to start the Full Demo. To do that, I duplicated the AVR323 project with a different name (FreeRTOS_ArduinoMEGA-Demo_Full) and copied a few source and header files from the FreeRTOS folder.
  • The whole Standard Demo Header files can be copied to the project folder "FreeRTOS_ArduinoMEGA-Demo_Full\FreeRTOS\Demo\Common\include" folder (there was only the partest.h header). 
  • The source files should be copied from the folder "FreeRTOS 9.0.0\FreeRTOS\Demo\Common\Minimal" to the project folder "FreeRTOS_ArduinoMEGA-Demo_Full\FreeRTOS\Demo\Common\Minimal", but not all of them. The list follows:
    • AbortDelay.c
    • BlockQ.c
    • blocktim.c
    • countsem.c
    • death.c
    • dynamic.c
    • EventGroupsDemo.c
    • flop.c
    • GenQTest.c
    • integer.c
    • IntSemTest.c
    • PollQ.c
    • QPeek.c
    • QueueOverwrite.c
    • QueueSet.c
    • QueueSetPolling.c
    • recmutex.c
    • semtest.c
    • TaskNotify.c
    • TimerDemo.c
  • You'll also need to add a few source files that implement the FreeRTOS. Add the following from the folder "FreeRTOSv9.0.0\FreeRTOS\Source" to you project folder "FreeRTOS_ArduinoMEGA-Demo_Full\FreeRTOS\Source" (there was only list.c, queue.c and tasks.c, which are the bare minimum):
    • croutine.c
    • event_groups.c
    • timers.c
  • For the Full Demo, we'll need to use some dynamic allocation, thus change your heap file to the heap_5.c to have the full experience.
Finally, before we start, I suggest you compile the project as it is (essencially the AVR323 project) and correct any problems that may appear. For me, there were two problems:
  • References missing: go to Project > Properties > C/C++ General > Paths and Symbols > Includes > GNU C, add the following workpace paths:
    • /FreeRTOS_ArduinoMEGA-Demo_Full
    • /FreeRTOS_ArduinoMEGA-Demo_Full/FreeRTOS/Source/portable/WinAVR/ATmega2560
    • /FreeRTOS_ArduinoMEGA-Demo_Full/FreeRTOS/Source/include
    • /FreeRTOS_ArduinoMEGA-Demo_Full/FreeRTOS/Demo/Common/include
  • Definitions missing: on FreeRTOSConfig.h:
/* Software timer related configs - Full Demo */
#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 )
#define configTIMER_QUEUE_LENGTH 20

/* The following includes are used in Full DEMO */
#define INCLUDE_eTaskGetState   1

With everything compiling correctly, even though it doesn't do anything different, we can start implementing (or actually just using the implemented algorithms) the Full Demo functions.

Tasks Notifications


Task notification is used to either send simple messages to another specific task or to just unblock the other task. This API can be considered a "lightweight" one position queue (called mailbox), since it is faster and consumes less RAM. To understand how it is used, let's assume we have two tasks, one I'll call Producer and one Consumer. The Producer will send a notification to the Consumer using  xTaskNotify(),  xTaskNotifyGive(),  xTaskNotifyAndQuery() or its interrupt safe equivalents, and will remain blocked until the Consumer receives the notification through  xTaskNotifyWait(),  ulTaskNotifyTake() or some task calls xTaskNotifyStateClear() with the Producer's handle as parameter. If the Consumer was already waiting for a notification, it will be immediately unblocked.

The notification value is a 32-bit value that every task has and that is cleared (set to zero) when the task is created. When sending a notification, the Producer can change the Consumer's notification value, either by overwriting, setting one or more bits or by an increment. The notification can also not be changed, if desired.

A fast and basic reference for the functions related to this API follows. Firstly, the functions used by the Producer (the one who sends the notification).

/* Notify a task
Returns: pdFAIL only if eAction is eSetValueWithoutOverwrite and there was another notification waiting, otherwise, pdPASS */
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify,
                        uint32_t ulValue, /* Interpretation of this value depends on eAction */
                        eNotifyAction eAction );

/* Notify and increment notification value 
Returns: pdPASS, always */
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );

/* Notify and retrieve the previous value
Returns: pdFAIL only if eAction is eSetValueWithoutOverwrite and there was another notification waiting, otherwise, pdPASS */
BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify,
                                uint32_t ulValue, /* Interpretation of this value depends on eAction */
                                eNotifyAction eAction,
                                uint32_t *pulPreviousNotifyValue ); /* Returns the previous notification value from the Consumer */

The eAction is an enumerated type and can take one of the following values:

  • eNoAction - ulValue is not used, the task is just notified;
  • eSetBits - ulValue represents the bits that will be set in the Consumer's notification value (bits not set won't be altered);
  • eIncrement - ulValue is not used, the Consumer's value is incremented by one;
  • eSetValueWithOverwrite - ulValue is written in the Consumer's value;
  • eSetValueWithoutOverwrite - ulValue is only written if no other Producer is waiting for the Consumer to be notified (there is not a notification pending).
Functions used by the Consumer (the one who receives a notification) or other tasks.

/* Waits for a notification and changes the notification value according to xClearCountOnExit
Returns: the notification value before it's altered*/
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, /* if pdTRUE, the notification value is reset to 0; if pdFALSE, the value is decremented by 1 */
                           TickType_t xTicksToWait ); /* Can wait forever, if set to portMAX_DELAY */

/* Waits for a notification, allowing the notification value to be altered bit by bit
Returns: pdFALSE if timed out, pdTRUE otherwise */
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, /* Bits to clear in the notification value before the enters the block state waiting for a notification */
                            uint32_t ulBitsToClearOnExit, /* Bits to clear in the notification value after the notification is received and the value is saved */
                            uint32_t *pulNotificationValue, /* Notification value resultant from the notification, before alteration according to ulBitsToClearOnExit */
                            TickType_t xTicksToWait ); /* Can wait forever, if set to portMAX_DELAY */

/* Clears a pending notification, not altering the notification value
Returns: pdPASS if there was a notification pendinge, otherwise pdFAIL */
BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask ); /* xTask can be either the handle of another task that may have a notification pending or NULL, which reflects the task that called the function */


Full Demo's Task Notify


OK, API explained, now let's check what's happening in the DEMO. Basically what it does is create a common task, which will receive the notifications, thus represent the Consumer (check prvNotifiedTask()), and two Producers, one from a software timer (check prvNotifyingTimer()) and one from an interruption (check xNotifyTaskFromISR()). The interruption used is Tick interruption, through the use of the vApplicationTickHook().

The Producer from the interruption will notify the Consumer every 50 ms each time using a different API (vTaskNotifyGiveFromISR(), xTaskNotifyFromISR() and xTaskNotifyAndQueryFromISR()), always incrementing the notification value by one, but will only do that after the Producer from the sotware timer is already running. The Producer from the software timer always notifies the Consumer using the xTaskNotifyGive() and its period varies from 10 to 90 ms.

The Consumer will first raise some single tasks tests (check prvSingleTaskTests()), even before it creates the software timer:

  • Tries to take a notification (xTaskNotifyWait()), even though none has or will be given, in order to check if the timeout works;
  • Gives a notification to itself (xTaskNotifyAndQuery() setting the notification value without overwriting) and takes (xTaskNotifyWait()) it, in order to check if it will be done ASAP, thus no timeout-ing;
  • Checks the no-overwrite function by giving two notifications to itself  with different notification values and eSetValueWithoutOverwrite (xTaskNotify()) - the second should fail - and then taking only once (xTaskNotifyWait()) - the notification value should be equal to the first one given;
  • Same as before, but using overwrite: uses eSetValueWithOverwrite and different values, the value taken should be equal to last given.
  • Checks whether giving a notification with no changes to the value works by using xTaskNotify() with eNoAction and a value for the notification
  • Checks increments on the notification value by giving notifications for a known number of times (xTaskNotify() with eIncrement), then taking once and comparing the values; it tries to take again to ensure there are no more notifications pending;
  • Checks whether each bit of the notification value can be set by setting one bit at a time while giving (xTaskNotify() with eSetBits) and taking (the notification value is cleared before it starts through a xTaskNotifyWait() with all bits set on bits to clear on entry and no timeout) - it should run 32 times, since the notification value is 32 bit wide;
  • Checks if bits will be cleared on the entry and not on the exit by trying to take a notification, even though none was given, using xTaskNotifyWait() with bit0 (0x01) as bits to clear on entry and bit1 (0x02) as bit to clear on exit - as no notification was given, the bits are only cleared on entry.
  • Checks if bits will be cleared on exit by giving one notification, taking once with bit1 (0x02) as bits to clear on exit and trying to take again just to save the previous value.
  • Checks if the previous value is correctly received while notifying a task using xTaskNotifyAndQuery();
  • Finally, takes all notifications that might still exist (shouldn't be any), gives one generic notification and tries to clear the state twice (xTaskNotifyStateClear()), the first time should succeed and the second shouldn't.
Now back to the Consumer: after the tests described, it creates the software timer Producer, which will liberate the interruption Producer, and both will create notifications by incrementing the notification value, which means the value will be the amount of notifications given. So the consumer loop will:
  • Restart the software timer with a random period;
  • Try to take one notification with another random timeout and not clearing the value;
  • Try to take another notification, but this time not blocking but also not clearing the value;
  • Takes another notification, with the same timeout as before, but this time clearing the value;
  • Every 50 cycles, it raises the priority of the Consumer, receives all notifications by blocking until there is one available and reseting the counter (notification value), and restores the original priority (it tests the path where the Consumer 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);
  • Finally, a counter of cycles is incremented.
There is one more function used, which is the Check function (xAreTaskNotificationTasksStillRunning()), responsible to check whether this Task Notify algorithm is still running. This function is called from the "Check Task" (prvCheckTask()), which is responsible to call, every 2.5 seconds, all check functions from all algorithms and warn in case something is going wrong. In the case of this algorithm, the function checks if the current number of cycles from the Consumer is higher than the last time it checked and if the number of times a notification was given is roughly the same of the taken ones. In case something is wrong, pdFAIL is returned.

Check Task


The check task (prvCheckTask()) is declared in main.c and is initialized with a low priority. Because the original code was designed to run in Windows, there was no problems with using printf(). With ArduinoMEGA things are obviously different, so a few changes were made. Instead of printing on the terminal, one LED is used to signalize that some problem occurred. A string (a static char *pcStatusMessage declared in main.c) is still used to signalize which error occurred, but it is treated as an array of chars with a defined size (mainERRORSIZE) and the messages always have this size, they are defined in a special header file (error_messages.h) and they're always transferred to the string using memcpy. Maybe in the future I could dispatch those messages through the serial port. Maybe.




Have fun!

Wednesday, March 30, 2016

Full demo - FreeRTOSConfig.h

This file is a big deal. Each application should have its own (application, not port) and that's where much of the customization is made. It is mostly comprised of definitions which turn ON or OFF some feature (usually a kernel's feature) that you need in your application. Remember those definitions started with "config", such as configUSE_IDLE_HOOK? In FreeRTOSConfig.h you will find, young Padawan. In this post I'll list and explain briefly all those you will find in the Full Demo for Windows. More detailed info can be found in FreeRTOS' website [1].

  • configUSE_PREEMPTION: this chooses the scheduler, whether preemptive or cooperative. Preemptive scheduling means the task will be stopped in order to other task take place on the processor. Cooperative scheduling will patiently wait until the task kindly leaves the processor (either if its execution finished or it went to sleep, etc).
  • configUSE_PORT_OPTIMISED_TASK_SELECTION: there is usually two kinds of task selection, a generic one, which was written in C and can be used in any port, or an optimized one, usually written in the port's specific assembly and optimized to the port. The generic doesn't have a limit in the amount of priorities for the task, while the optimized is usually limited to 32.
  • configUSE_IDLE_HOOK: this chooses whether the Idle Hook will be used or not.
  • configUSE_TICK_HOOK: this chooses whether the Tick Hook will be used or not.
  • configTICK_RATE_HZ: this is the frequency of the tick interrupt, when the task is stopped to make place to another one (or maybe not). Care must be taken when you choose this value, as the higher the frequency, the more the overhead caused by the context switching will matter. For all the demo projects, it's set to 1000 Hz, and that's a good starting number.
  • configMINIMAL_STACK_SIZE: this is the size of the stack used by the Idle Task and shoul not differ from the one specified on the demo project of the port you're using. It is specified in words, as in the xTaskCreate() function, thus, if your microcontroller/microprocessor uses a 32-bit (4 bytes) wide stack, a stack of 50 words will mean 200 bytes.
  • configTOTAL_HEAP_SIZE: total amount of RAM available to the RTOS kernel. This is only used in certain cases of memory management [2] and I'll discuss that soon.
  • configMAX_TASK_NAME_LEN: the maximum length of the task's descriptive name (the one you write on xTaskCreate()), including the ending NULL char.
  • configUSE_TRACE_FACILITY: enables a few more trace capabilities, in order to assist with execution visualization.
  • configUSE_16_BIT_TICKS: this specifies the size of the variable that counts the ticks. If the config is set, the type (TickType_t) will bean unsigned 16-bit, other wise, it will be 32-bit wide. With a tick rate of 1 kHz and a 16-bit counter, the max amount of time that can be counted is 65535 ticks or 65.535 seconds, instead of 2^32 (4294967296) ticks or around 1193 hours. 8 and 16-bit ports can benefit a lot when setting this option, since the overhead of treating a 32-bi variable can be high.
  • configIDLE_SHOULD_YIELD: if the preemption is being used and there are user tasks with Idle priority, this option tells the idle function to Yield whenever there is another task with the same priority ready to be executed. This means the Idle task will let another task take the remaining time it had (which will be less than one tick). This can lead to a task having less execution time than others with the same priority (imagine tasks A, B and C, where A is always executed when the idle task yields, while B and C take full Ticks), which can be prevented by increasing the other tasks priority.
  • configUSE_MUTEXES: enables Mutex capabilities (check Generic Queue Tasks).
  • configCHECK_FOR_STACK_OVERFLOW: chooses between not using a stack overflow control (set to 0) and two methods of checking (options 1 and 2). It will be discussed soon, but what you should know by now is that, if the config != 0, then you should have a Stack Overflow Hook.
  • configUSE_RECURSIVE_MUTEXES: enables Recursive Mutex capabilities (check Recursive Mutex Tasks).
  • configQUEUE_REGISTRY_SIZE: defines the maximum number of queues and semaphores that can be registered for easier debugging. It only makes sense when using a RTOS kernel aware debugger.
  • configUSE_MALLOC_FAILED_HOOK: enables the Malloc Failed Hook function (vApplicationMallocFailedHook()), which will be called whenever the pvPortMalloc() function returns NULL, which means there were not enough memory left on the heap for the allocation.
  • configUSE_APPLICATION_TASK_TAG: enables the function vTaskSetApplicationTaskTag(), which will allow a tag value (or function) to be assigned to a task. This functionality is used for tracing purposes and an example can be studied here.
  • configUSE_COUNTING_SEMAPHORES: enables Counting Semaphores capabilities (check Counting Semaphore Tasks).
  • configUSE_ALTERNATIVE_API: enables the alternative queue API described in queue.h header. Should not be used as is deprecated (although is set on the Full Demo).
  • configUSE_QUEUE_SETS: enables the Queue Sets functionality (check Queue Set Tasks).
  • configUSE_TASK_NOTIFICATIONS: enables the Direct to Task Notification API (each task will consume 8 more bytes). See Notify Task.
  • configUSE_TIMERS: enables the Software Timers. See Timer Demo Tasks.
  • configTIMER_TASK_PRIORITY: sets the priority of the software timer task. Maybe later I'll go more into that.
  • configTIMER_QUEUE_LENGTH: sets the length of the software timer command queue (the maximum number of unprocessed requests).
  • configTIMER_TASK_STACK_DEPTH: sets the size (in Words) of the stack of the software timer task. Depends highly on the timer callback functions, as the context where the calls are made is the timer service task.
  • configMAX_PRIORITIES: sets the number of priorities available. The higher this number, the higher the amount of RAM spent, so should be kept as low as needed.
  • ulGetRunTimeCounterValue( void ): this is the prototype for a Run Time statistics function. This one, as the name intends, will return the time since the application started.
  • vConfigureTimerForRunTimeStats( void ): initializes the Run Time Statistics.
  • configGENERATE_RUN_TIME_STATS: enables the Run Time Statistics. When this is enabled, the two next macros have to be defined.
  • portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(): this is a macro to a function (in this case, vConfigureTimerForRunTimeStats()) that will initialize a higher resolution (higher than the tick rate usually by 10 to 100 times) timer. The function is usually port specific (the Full Demo for Windows uses the Windows API for that, but a microcontroller may use a hardware timer).
  • portGET_RUN_TIME_COUNTER_VALUE(): this is a macro to a function (in this case, ulGetRunTimeCounterValue()) that will return the current time. The function is also usually port specific, as is the initialization described above.
  • configUSE_CO_ROUTINES: enables co-routine functionality. Even though it is set on the Full Demo, I couldn't find where these co-routines were used. Maybe I'll write another post explaining them, although it should be discontinued in FreeRTOS [3].
  • configMAX_CO_ROUTINE_PRIORITIES: the maximum number of co-routines priorities, similar to configMAX_PRIORITIES.
  • configUSE_STATS_FORMATTING_FUNCTIONS: enables some functions for trace capabilities. If this and configUSE_TRACE_FACILITY are set, vTaskList() and vTaskGetRunTimeStats() may be used.
  • INCLUDE_xxxxxx: these macros force the insertion (or not) of some functions, even though the unused are usually no inserted by the linker.
  • AssertCalled( unsigned long ulLine, const char * const pcFileName );: prototype of the function called by configASSERT(). Check configASSERT().
  • configASSERT( x ): macro that calls the function described above. Check configASSERT().
  • TRACE_ENTER_CRITICAL_SECTION() and TRACE_EXIT_CRITICAL_SECTION(): macros for the entering and existing critical sections, used in trace. The functions are port specific. Check the official FreeRTOS+ Trace docs [4].
  • "trcKernelPort.h": header file for the trace capabilities. Check the official FreeRTOS+ Trace docs [4].
Well, those are all of the options included on the FreeRTOSConfig.h in our Full Demo application. The FreeRTOS website brings a few more options that were left out:
  • configUSE_TICKLESS_IDLE: this is used to enhance the low power capabilities by allowing the application to disable the ticks interruptions and sleep, if there is nothing else to be done (all tasks are blocked or suspended). It can be set to 1, in order to use a port specific implementation of tickless idle, or to 2, to use the generic implementation. More information may be checked on the FreeRTOS specific web page [5].
  • configCPU_CLOCK_HZ: sets the frequency, in Hertz (Hz), of the internal clock of the peripheral that generates the Tick interrupts runs (usually the same as the CPU clock). This parameter is used to correctly configure the timer peripherals.
  • configUSE_TIME_SLICING: when set to 0, the scheduler will let tasks with the same period to run until it wants to stop (may be blocked or suspended). If not set (the default option) or set to 1, the scheduler will change the running task among others with the same priority every Tick.
  • configUSE_NEWLIB_REENTRANT: if set to 1, a newlib reent structure will be allocated for each task created. Any newlib functionalities are not maintained by the FreeRTOS crew. More about that on newlib's website [6].
  • configENABLE_BACKWARD_COMPATIBILITY: this enables the use of older (FreeRTOS pre 8.0.0) structure names.
  • configNUM_THREAD_LOCAL_STORAGE_POINTERS: this set the amount of Thread Local Storage (TLS) pointer. In a multi threaded application, TLS is used to substitute a global variable, as it will be stored inside the task's control block (imagine each task wanted to have an error number variable, commonly called "errno", available to every other task) [7].
  • configKERNEL_INTERRUPT_PRIORITYconfigMAX_SYSCALL_INTERRUPT_PRIORITY and configMAX_API_CALL_INTERRUPT_PRIORITY: those options are used to set some interrupts priorities. configMAX_SYSCALL* and configMAX_API_CALL* are equivalent (the later is the new name). Together, the two options can be set in order to some specific interrupt allow the kernel itself to be stoped (maybe some emergency interrupt that has to be processed as soon as it happens). Check the official documentation for more info [8].
  • configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS: this allows the user to create privileged functions (defined and implemented inside the application_defined_privileged_functions.h file). This means the task will be able to access anything in the application (even memory outside the task's context). Check the example in FreeRTOS specific page [9].
  • configAPPLICATION_ALLOCATED_HEAP: when set, allows the heap to be positioned in a specific location [10].

Uff, that was a lot. See you soon!


[1] http://www.freertos.org/a00110.html
[2] http://www.freertos.org/a00111.html
[3] http://www.freertos.org/taskandcr.html
[4] http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_Trace/RTOS_Trace_Instructions.shtml
[5] http://www.freertos.org/low-power-tickless-rtos.html
[6] http://sourceware.org/newlib/
[7] http://www.freertos.org/thread-local-storage-pointers.html
[8] http://www.freertos.org/a00110.html#kernel_priority
[9] http://www.freertos.org/a00110.html#configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS
[10] http://www.freertos.org/a00111.html#heap_4

Tuesday, February 23, 2016

Full demo - part 2

Well, a few things left unsaid. 


The Check Task

This is there to ensure everything is going as expected. It will run every 2.5 seconds asking each and every set of tasks described in the last post whether they're working fine and will print a "OK" message if so (that's the OK message you saw the first time you run the main_full program).
Each set of tasks has a "xAre*********TasksStillRunning()", which is implemented close to the tasks code and will return a value informing whether or not the functionality tested is ok. You'll notice the order of the tasks is different here from the part 1, but that's how it was implemented.
  • Timer Demo Tasks: xAreTimerDemoTasksStillRunning(), as each successful test of the Software Timer increments a counter, this function tests whether this value has changed, thus all is running fine.
  • Notify Task: xAreTaskNotificationTasksStillRunning(), as the functions and tasks increment counters whenever a notification is taken and given, this test will check whether the counting is changing and if every notification given is taken.
  • Interrupt Semaphores Tasks: xAreInterruptSemaphoreTasksStillRunning(), checks whether the counting semaphores are still working and the master is still running.
  • Event Group Tasks: xAreEventGroupTasksStillRunning(), checks whether the master and slave are still running and the ISR calls are ok.
  • Integer Math Tasks: xAreIntegerMathsTaskStillRunning(), checks whether the answer from the calculations keep being correct all the time.
  • Generic Queue Tasks: xAreGenericQueueTasksStillRunning(), check if the queues and mutexes keep on going well.
  • Queue Peek Tasks: xAreQueuePeekTasksStillRunning(), checks if the queue keeps on being peeked.
  • Blocking Queue Tasks: xAreBlockingQueuesStillRunning(), checks whether all the consumers and producers keep on consuming and producing.
  • Semaphore Tasks: xAreSemaphoreTasksStillRunning(), check if the sempahores are being taken and given.
  • Polled Queue TasksxArePollingQueuesStillRunning(), checks if the producer keeps producing and the consumer keeps consuming to and from the queue.
  • Math Tasks: xAreMathsTaskStillRunning(), same as integer math tasks, but this time there are more tasks testing the floating point operations.
  • Recursive Mutex Tasks: xAreRecursiveMutexTasksStillRunning(), checks if the controlling, blocking and polling tasks keep on running.
  • Counting Semaphore Tasks: xAreCountingSemaphoreTasksStillRunning(), checks if the semaphores keep on being incremented and decremented.
  • Suicidal Tasks: xIsCreateTaskStillRunning(), checks if the creator is still alive and creating and if no more than 4 extra tasks (besides all the other tasks that were created in this project) are created.
  • Dynamic Priority Tasks: xAreDynamicPriorityTasksStillRunning(), checks if everything is still running.
  • Queue Set Tasks: xAreQueueSetTasksStillRunning(), checks if all tasks are still running, if all queues are being used and if the ISR function is still sending values to the queues.
  • Queue Overwrite Tasks: xIsQueueOverwriteTaskStillRunning(), checks if the task and the ISR function are still working.
  • Queue Space Task: (not tested).

Idle Task/Hook

The Idle Hook is a function that runs every time the Idle Task runs, if configUSE_IDLE_HOOK is set to 1. The function, in this case, is defined in main.c (vApplicationIdleHook()) and only has a call to vFullDemoIdleFunction() (defined in main_full.c). It is used to demonstrate how one can use the Idle Hook to do something. 
The first thing it does is to sleep (15ms) just to allow any task that could have been terminated by the idle task to actually terminate. 
Then there are a few demonstrations (prvDemonstrateTaskStateAndHandleGetFunctions()), such as get the idle task handle (should be equal to the idle hook's handle) and the timer daemon handle. It also creates a test task (prvTestTask(), it doesn't actually do anything) to show what could be done with its handle (get its state, suspend, delete). This only happens once while the application is running.
Then there is a demonstration of pending a function call (prvDemonstratePendingFunctionCall()), that is, having the RTOS daemon task (timer service task) call some function (xTimerPendFunctionCall()).
Then, a mutex, created in main_full() function just to this purpose, is deleted to demonstrate the usage of  vSemaphoreDelete().
In the end, a test to heap_5.c is performed, by malloc'ing a random size void variable and freeing it.


Tick Task/Hook

Similar to the Idle Hook, the Tick Hook is a piece of code called whenever a Tick happens (defined in configTICK_RATE_HZ, in FreeRTOSConfig.h), if configUSE_IDLE_HOOK is set to 1. The function is defined in main.c (vApplicationTickHook()) and only performs the call to vFullDemoTickHookFunction(), defined in main_full.c. This is where most (maybe all?) the ISR functions described in part 1 are called, such as vTimerPeriodicISRTests(), from Timer Demo Tasks, xNotifyTaskFromISR() and vQueueSetAccessQueueSetFromISR().

Other Hooks

There are two other hooks defined: vApplicationMallocFailedHook(), which runs if configUSE_MALLOC_FAILED_HOOK is set to 1 and when a malloc (pvPortMalloc()) fails, and vApplicationStackOverflowHook(), which runs if configCHECK_FOR_STACK_OVERFLOW is set to 1 or 2 (which is not) and when (can you guess?) a stack overflow is detected.

On the next post, I'll talk about the FreeRTOSConfig.h, which I mentioned a few times here and there, but never gone further. See you soon!