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 );
pvTaskCodepcNameconfigMAX_TASK_NAME_LEN: constant that defines the maximum length a task name can task including the NULL terminator.usStackDepthconfigMINIMAL_STACK_SIZEpvParameterspvParameters 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.pxCreatedTaskpdTRUE : task has been created successfully.errCOULD_NOT_ALLOCATE_REQUIRED_MEMORYvTaskFunction 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_HZportTICK_RATE_MSmain() 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);
pxPreviousWakeTimevTaskDelayUtil() 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.xTimeIncrementvTaskDelayUtil() is being used to implement a task that executes periodically and with a fixed frequency – set by xTimeIncrementportTICK_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_SIZExPortGetFreeHeapSize() 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_PRIORITYconfigMAX_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(); } }