void
and take a void
pointer parameter as the following:void ATaskFunction (void *pvParameters);
return
statementvoid ATaskFunction( void *pvParameters ) { /* Variables can be declared just as per a normal function. Each instance of a task created using this function will have its own copy of the iVariableExample variable. This would not be true if the variable was declared static - in which case only one copy of the variable would exist and this copy would be shared by each created instance of the task. */ int iVariableExample = 0; /* A task will normally be implemented as in infinite loop. */ for( ;; ) { /* The code to implement the task functionality will go here. */ } /* Should the task implementation ever break out of the above loop then the task must be deleted before reaching the end of this function. The NULL parameter passed to the vTaskDelete() function indicates that the task to be deleted is the calling (this) task. */ vTaskDelete( NULL ); }
xTaskCreate()
API functionportBASE_TYPE xTaskCreate ( pdTASK_CODE pvTaskCode, const signed portCHAR *const pcName, unsigned portSHORT usStackDepth, void *pvParameters, unsigned portBASE_TYPE uxPriority, xTaskHandle *pxCreatedTask );
pvTaskCode
pcName
configMAX_TASK_NAME_LEN
: constant that defines the maximum length a task name can task including the NULL terminator.usStackDepth
configMINIMAL_STACK_SIZE
pvParameters
pvParameters
will be the values passed into the task.uxPriority
(configMAX_PRIOIRTIES-1)
, which is the highest priority.(configMAX_PRIOIRTIES -1)
will result in the priority being capped the maximum legitimate value.pxCreatedTask
pdTRUE
: task has been created successfully.errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY
vTaskFunction
using the task parameterpcTextForTask1
:static const char *pcTextForTask1 = "Task 1 is running.\n";
main(void)
function, change to:xTaskCreate( vTaskFunction, "Task 1", 240, (void*)pcTextForTask1, 1, NULL );
vTaskFunction(void * pvParameters) char *pcTaskName; pcTaskName = (char *) pvParameters;
uxPriority
parameter of xTaskCreate()
assigns an initial priority to the task being created.vTaskPrioritySet()
API functionconfigMAX_RPIORITIES
in "FreeRTOSConfig.h"
[0(low), configMAX-PRIORITIES-1(high)]
configTICK_RATE_HZ
in "FreeRTOSConfig.h"
configTICK_RATE_HZ
portTICK_RATE_MS
main()
function, change:xTaskCreate( vTaskFunction, "Task 2", 240, (void*)pcTextForTask2, 1, NULL );
To
xTaskCreate( vTaskFunction, "Task 2", 240, (void*)pcTextForTask2, 2, NULL );
vTaskSuspend()
API functionvTaskResume()
or vTaskResumeFromISR()
API functionsvTaskDelay()
API function.INCLUDE_vTaskDelay
to 1 in "FreeRTOSConfig.h"
void vTaskDelay(portTickType xTicksToDelay);
xTicksToDelay
: the number of ticks that the calling task should remain in the Blocked state before being transitioned back into the Ready state.vTaskDelay(100)
while the tick count was 10,000, it enters the Blocked state immediately and remains there until the tick count is 10,100.void vTaskFunction(void *pvParameters)
for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ ) { }
vTaskDelay(250 / portTICK_RATE_MS); /* a period of 250ms is being specified. */
vTaskDelayUntil()
vTaskDelay()
)void vTaskDelayUntil( portTickType *pxPreviousWakeTime, portTickType xTimeIncrement);
pxPreviousWakeTime
vTaskDelayUtil()
is being used to implement a task that executes periodically and with a fixed frequency.pxPreviousWakeTime
is updated automatically, not be modified by application code, other than when the variable is first initialized.xTimeIncrement
vTaskDelayUtil()
is being used to implement a task that executes periodically and with a fixed frequency – set by xTimeIncrement
portTICK_RATE_MS
can be used to convert ms to ticks.vTaskDelay()
does not ensure that the frequency at which they run is fixed,vTaskDelay()
void vTaskFunction(void *pvParameters)
change:vTaskDelay(250 / portTICK_RATE_MS); /* a period of 250ms is being specified. */
vTaskDelayUntil(&xLastWakeTime,(vTaskDelay(250 / portTICK_RATE_MS)); /* xLastWakeTime init with current tickcount */ xLastWakeTime = xTaskGetTickCount(); /* Updated within vTaskDelayUntil() automatically */
vTaskDelayUntil()
to place itself into the Blocked state between each print iteration.void vContinuousProcessingTask(void * pvParameters) { char *pcTaskName; pcTaskName = (char *) pvParameters; for (;;) { vPrintString(pcTaskName); for( ul = 0; ul < 0xfff; ul++ ) { } }} void vPeriodicTask(void * pvParameters) { portTickType xLastWakeTime; xLastWakeTime = xTaskGetTickCount(); for (;;){ vPrintString("Periodic task is running\n"); vTaskDelayUntil(&xLastWakeTime,(10/portTICK_RATE_MS)); }}
vTaskStartScheduler()
is called.vTaskDelete()
, the Idle task hook must always return to its caller within a reasonable time period.void vApplicationIdleHook(void);
configUSE_IDLE_HOOK
to 1unsigned long ulIdleCycleCount = 0UL; /* must be called this name, take no parameters and return void. */ void vApplicationIdleHook (void) { ulIdleCycleCount++; }
vTaskFunction()
, changevPrintString(pcTaskName)
*
vPrintStringAndNumber(pcTaskName, ulIdleCycleCount);
vTaskPrioritySet(xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority);
INCLUDE_vTaskPrioritySet
is set 1.pxTask
: handle of the task. A task can change its own priority by passing NULL in place of a valid task handle.uxNewPriority
: priority to be set.unsigned portBASE_TYPE uxTaskPriorityGet (xTaskHandle pxTask);
INCLUDE_vTaskPriorityGet
is set 1pxTask
: handle of the task. A task can query its own priority by passing NULL in place of a valid task handle.vTaskPrioritySet()
API function to change the priority of two tasks relative to each other.xTaskHandle xTask2Handle;
main()
function, create two tasks:xTaskCreate(vTask1, "Task 1", 240, NULL, 2, NULL); xTaskCreate(vTask2, "Task 2", 240, NULL, 1, &xTask2Handle);
unsigned portBASE_TYPE uxPriority; uxPriority = uxTaskPriorityGet(NULL);
vTaskPrioritySet(xTaskHandl1, (uxPriority+1));
unsigned portBASE_TYPE uxPriority; uxPriority = uxTaskPriorityGet(NULL);
vTaskPrioritySet(NULL, (uxPriority-2));
vTaskDelete()
, do not completely starve the idle task of all processing time.vTaskDelete()
API functionvoid vTaskDelete(xTaskHandle pxTaskToDelete);
pxTaskToDelete
: Handle of the task that is to be deleted. A task can delete itself by passing NULL in place of valid task handle.INCLUDE_vTaskDelete
set 1main()
with priority 1. When it runs, it creates Task 2 at priority 2. Task 2 as the highest priority task starts to execute immediately.vTaskDelay()
to block for a short period.configTOTAL_HEAP_SIZE
xPortGetFreeHeapSize()
returns the total amount of heap space that remains unallocated."Heap_2.c"
taskYIELD()
xQueueCreate()
API FunctionxQueueHandle
to reference the queue it creates.xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize);
xQueueLength
: the maximum number of items that the queue being created can hold at any one time.uxItemSize
: the size in bytes of each data item that can be stored in the queue.xQueueSendToBack()
xQueueSend()
xQueueSendToFront()
portBASE_TYPE xQueueSendToBack ( xQueueHandle xQueue, const void *pvItemToQueue, portTickType xTicksToWait);
portBASE_TYPE xQueueSendToFront ( xQueueHandle xQueue, const void *pvItemToQueue, portTickType xTicksToWait);
xQueue
: The handle of the queue to which the data is being send (written). It will have been returned from the call to xQueueCreate()
used to create the queue.pvItemToQueue
: a pointer to the data to be copied into the queue.pvItemQueue
into the queue storage area.xTicksToWait
: the maximum amount of time the task should remain in the Blocked state to wait for the space to become available on the queue, should the queue already be full.xTicksToWait
is zero, both APIs will return immediately in case the queue is already full.portTICK_RATE_MS
can be used to convert tickes into a time specified in MS.pdPASS
will be returned if data was successfully sent to the queue.errQUEUE_FULL
will be returned if data could not be written to the queue as the queue was already full.xQueueReceive()
xQueuePeek()
portBASE_TYPE xQueueReceive ( xQueueHandle xQueue, const void *pvBuffer, portTickType xTicksToWait);
portBASE_TYPE xQueuePeek( xQueueHandle xQueue, const void *pvBuffer, portTickType xTicksToWait);
xQueue
: Handle of the queue from which data received (read). It will have been returned from the call to xQueueCreate()
pvBuffer
: A memory pointer where received data will be copied.xTicksToWait
: Maximum amount of time the task should remain in the Blocked state waiting data available on queue, should the queue already be empty.xTicksToWait
is zero, both APIs will return immediately in case the queue is already empty.portTICK_RATE_MS
can be used to convert a time specified in MS into ticks.pdPASS
on successfully read from the queue.errQUEUE_EMPTY
if data could not be read from the queue as the queue was already empty.unsigned portBASE_TYPE uxQueueMEssagesWaiting ( xQueueHandle xQueue);
vSenderTask()
does not specify a block time.xStatus = xQueueSendToBack(xQueue,pvParameters,0); if (xStatus != pdPASS) { vPrintString("Could not send to the queue.\n"); } taskYIELD();
vReceiverTask()
specifies a block time 100ms.xStatus = xQueueReceive(xQueue,&xReceivedValue,100/portTICK_RATE_MS); if (xStatus == pdPASS) { /* print the data received */ }
typedef struct { int iValue; // a data value int iMeaning; // a code indicating data source } xData;
vSenderTask()
, the sending task specifies a block time of 100ms.xStatus = xQueueSendToBack(xQueue,pvParameters,100/portTICK_RATE_MS); if (xStatus != pdPASS) { vPrintString("Could not send to the queue.\n"); } taskYIELD();
vReceiverTask()
will run only when both sending tasks are in the Blocked state.xStatus = xQueueReceive(xQueue,&xReceivedStructure,0); if (xStatus == pdPASS) { /* print the data received */ }
* Binary semaphores are the simplest effective way to synchronize tasks, an other even more simple, but not as effective, consists in polling an input or a resource. A binary semaphore can be seen as a queue which contains only one element.
void vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore );
xSemaphore
: semaphore to be created.portBASE_TYPE xSemaphoreTake( xSemaphoreHandle xSemaphore, portTickType xTicksToWait );
xSsemaphore
is the semaphore to take.xTicksToWait
is the time, in clock ticks, for the task to wait before it gives up with taking the semaphore. If xTicksToWait
equals MAX_DELAY
and INCLUDE_vTaskSuspend
is 1, then the task won’t stop waiting.pdPASS
. If not, pdFALSE
is returned.portBASE_TYPE xSemaphoreGive( xSemaphoreHandle xSemaphore );
xSemaphore
is the semaphore to be given.pdPASS
if the give operation was successful, or pdFAIL
if the semaphore was already available, or if the task did not hold it.xSemaphoreHandle xSemaphoreCreateCounting( unsigned portBASE_TYPE uxMaxCount, unsigned portBASE_TYPE uxInitialCount );
uxMaxCount
is the capacity of the counting semaphore, its maximum ability to be taken.uxInitialCount
is the new semaphore’s availability after it is created."FreeRTOSConfig.h"
:configKERNEL_INTERRUPT_PRIORITY
sets the interrupt priority level for the tick interrupt.configMAX_SYSCALL_INTERRUPT_PRIORITY
defines the highest interrupt level available to interrupts that use interrupt-safe FreeRTOS API functions. If this constant is not defined, then any interrupt handler function that makes a use of FreeRTOS API must execute at configKERNEL_INTERRUPT_PRIORITY
configMAX_SYSCALL_INTERRUPT_PRIORITY
or configKERNEL_INTERRUPT_PRIORITY
if configMAX_SYSCALL_INTERRUPT_PRIORITY
is not defined, will never be preempted by the kernel, but are forbidden to use FreeRTOS API functions.{{freertos_int_sembin.png|image}} * * An ISR gives a semaphore and unblock a Handler task that is able to handler the ISR, making the ISR execution much shorter.
/* Ensure access to the PORTA register cannot be interrupted by placing it within a critical section. Enter critical section. */ taskENTER_CRITICAL(); /* A switch to another task cannot occur between the call to taskENTER_CRITICAL() and the call to taskEXIT_CRITICAL(). Interrupts may still execute on FreeRTOS ports that allow interrupt nesting, but only interrupts whose priority is above the value assigned to the configMAX_SYSCALL_INTERRUPT_PRIORITY constant and those interrupts are not permitted to call FreeRTOS API functions. */ PORTA |= 0x01; /* We have finished accessing PORTA so can safely leave the critical section. */ taskEXIT_CRITICAL();
taskENTER_CRITICAL()
and stop it using taskEXIT_CRITICAL()
. The system allow a critical section to be started while an other one is already opened: this makes much easier to call external functions that can need such a section whereas the calling function also need it. However, it is important to notice that in order to end a critical section, taskEXIT_CRITICAL()
must be called exactly as much as taskSTART_CRITICAL
was. Generaly speaking, these two functions must be called as close as possible in the code to make this section very short.configMAX_SYSCALL_INTERRUPT_PRIORITY
(if defined in "FreeRTOSConfig.h"
; if not, prefer to consider the value configKERNEL_INTERRUPT_PRIORITY
instead) to create a context change./* Write the string to stdout, suspending the scheduler as a method of mutual exclusion. */ vTaskSuspendAll(); { printf( "%s", pcString ); fflush( stdout ); } xTaskResumeAll();
xTaskResumeAll()
is called, it returns pdTRUE
if no task requested a context change while scheduler was suspended and returns pdFALSE
if there was.malloc()
and free()
to allocate memory for tasks, queues or semaphores can cause various problems: preemption while allocating some memory, memory allocation and free can be an nondeterministic operations, once compiled, they consume a lot of space or suffer from memory fragmentation."heap_1.c", "heap_2.c", "heap_3.c"
void *pvPortMalloc( size_t xWantedSize); void pvPortFree( void *pv);
xWantedSize
is the size, in byte, to be allocated,pv
is a pointer to the memory to be freed.pvPortMalloc
returns a pointer to the memory allocated.pvPortMalloc
is implemented. This implementation can be found in "Source/portable/MemMang/heap_1.c"
configTOTAL_HEAP_SIZE
in "FreeRTOSConfig.h"
, and divides it in smaller parts which are allocated for memory all tasks require. This makes the application to appear to consume a lot of memory, even before any memory allocation."Source/portable/MemMang/heap_2.c"
.configTOTAL_HEAP_SIZE
and makes the application to appears to consume huge RAM. A difference with the previous solution consists in an implementation of vPortFree(). As memory can be freed, the memory allocation is also adapted.pvPortMalloc(20)
requires 20 bytes to be free so has to reserve it and return back its reference. This algorithm will return the second free space, 25 bytes large and will keep the remaining 5 bytes for a later call to pvPortMalloc(). It will always choose the smallest free space where can fit the requested size.malloc()
and free()
: large compiled code or nondeterministic execution. This implementation wraps the two functions, but make them thread safe by suspending the scheduler while allocating or deallocating.void *pvPortMalloc( size_t xWantedSize ) { void *pvReturn; vTaskSuspendAll(); { pvReturn = malloc( xWantedSize ); } xTaskResumeAll(); return pvReturn; } void vPortFree( void *pv ) { if( pv != NULL ) { vTaskSuspendAll(); { free( pv ); } xTaskResumeAll(); } }