freeRTOS Task Function
freeRTOS Task Function
FreeRTOS tasks must not be allowed to return from their implementing function in any way—they must not contain a ‘return’ statement and must not be allowed to execute past the end of the function.
(必需要是無限迴圈)
If a task is no longer required, it should instead be explicitly deleted.
A single task function definition can be used to create any number of tasks—each created task being a separate execution instance, with its own stack and its own copy of any automatic (stack) variables defined within the task itself.
(每個task都有自己的記憶體塊和自己的變數)
Function
The xTaskCreate() API Function
Tasks are created using the FreeRTOS xTaskCreate() API function.
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
const char * const pcName,
uint16_t usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask );
pvTaskCode
Tasks are simply C functions that never exit and, as such, are normally implemented as an infinite loop. The pvTaskCode parameter is simply a pointer to the function that implements the task (in effect, just the name of the function).
pcName
A descriptive name for the task. This is not used by FreeRTOS in any way. It is included purely as a debugging aid. Identifying a task by a human readable name is much simpler than attempting to identify it by its handle.
The application-defined constant configMAX_TASK_NAME_LEN defines the maximum length a task name can take—including the NULL terminator. Supplying a string longer than this maximum will result in the string being silently truncated.
usStackDepth
Each task has its own unique stack that is allocated by the kernel to the task when the task is created. The usStackDepth value tells the kernel how large to make the stack.
The value specifies the number of words the stack can hold, not the number of bytes. For example, if the stack is 32-bits wide and usStackDepth is passed in as 100, then 400 bytes of stack space will be allocated (100 * 4 bytes). The stack depth multiplied by the stack width must not exceed the maximum value that can be contained in a variable of type uint16_t.
The size of the stack used by the Idle task is defined by the application-defined constant configMINIMAL_STACK_SIZE1. The value assigned to this constant in the FreeRTOS demo application for the processor architecture being used is the minimum recommended for any task. If your task uses a lot of stack space, then you must assign a larger value.
There is no easy way to determine the stack space required by a task. It is possible to calculate, but most users will simply assign what they think is a reasonable value, then use the features provided by FreeRTOS to ensure that the space allocated is indeed adequate, and that RAM is not being wasted unnecessarily. Section 12.3, Stack Overflow, contains information on how to query the maximum stack space that has actually been used by a task.
pvParameters
Task functions accept a parameter of type pointer to void ( void* ). The value assigned to pvParameters is the value passed into the task. Some examples in this book demonstrate how the parameter can be used.
uxPriority
Defines the priority at which the task will execute. Priorities can be assigned from 0, which is the lowest priority, to (configMAX_PRIORITIES – 1), which is the highest priority. configMAX_PRIORITIES is a user defined constant that is described in section 3.5.
Passing a uxPriority value above (configMAX_PRIORITIES – 1) will result in the priority assigned to the task being capped silently to the maximum legitimate value.
pxCreatedTask
pxCreatedTask can be used to pass out a handle to the task being created. This handle can then be used to reference the task in API calls that, for example, change the task priority or delete the task.
If your application has no use for the task handle, then pxCreatedTask can be set to NULL.
Returned value
There are two possible return values:
1. pdPASS
This indicates that the task has been created successfully.
2. pdFAIL
This indicates that the task has not been created because there is insufficient heap memory available for FreeRTOS to allocate enough RAM to hold the task data structures and stack.
Example1
main.c
#include "FreeRTOS.h"
#include "task.h"
#include "stm32f4xx.h"
//Task1
// ----------------------------------------------------------------------------
void vTask1( void *pvParameters )
{
const char *pcTaskName = "Task 1 is running\r\n";
volatile uint32_t ul; /* volatile to ensure ul is not optimized away. */
/* As per most tasks, this task is implemented in an infinite loop. */
volatile uint16_t count1=0;
for( ;; )
{
/* Print out the name of this task. */
count1++;
/* Delay for a period. */
for( ul = 0; ul < 1000; ul++ )
{
/* This loop is just a very crude delay implementation. There is
nothing to do in here. Later examples will replace this crude
loop with a proper delay/sleep function. */
}
}
}
//Task2
// ----------------------------------------------------------------------------
void vTask2( void *pvParameters )
{
const char *pcTaskName = "Task 2 is running\r\n";
volatile uint32_t ul; /* volatile to ensure ul is not optimized away. */
/* As per most tasks, this task is implemented in an infinite loop. */
volatile uint16_t count2=0;
for( ;; )
{
/* Print out the name of this task. */
count2++;
/* Delay for a period. */
for( ul = 0; ul < 1000; ul++ )
{
/* This loop is just a very crude delay implementation. There is
nothing to do in here. Later examples will replace this crude
loop with a proper delay/sleep function. */
}
}
}
int main( void )
{
/* Create one of the two tasks. Note that a real application should check
the return value of the xTaskCreate() call to ensure the task was created
successfully. */
xTaskCreate( vTask1, /* Pointer to the function that implements the task. */
"Task 1",/* Text name for the task. This is to facilitate
debugging only. */
100, /* Stack depth - small microcontrollers will use much
less stack than this. */
NULL, /* This example does not use the task parameter. */
1, /* This task will run at priority 1. */
NULL ); /* This example does not use the task handle. */
/* Create the other task in exactly the same way and at the same priority. */
xTaskCreate( vTask2, "Task 2", 100, NULL, 1, NULL );
/* Start the scheduler so the tasks start executing. */
vTaskStartScheduler();
/* If all is well then main() will never reach here as the scheduler will
now be running the tasks. If main() does reach here then it is likely that
there was insufficient heap memory available for the idle task to be created.
Chapter 2 provides more information on heap memory management. */
for( ;; );
}
Example1 程式介紹
Task1 和 Task2的優先級相同,每次
執行Task1則count1加1
執行Task2則count2加1
Example2
main.c
#include "FreeRTOS.h"
#include "task.h"
#include "stm32f4xx.h"
//Task1
// ----------------------------------------------------------------------------
void vTask1( void *pvParameters )
{
const char *pcTaskName = "Task 1 is running\r\n";
volatile uint32_t ul; /* volatile to ensure ul is not optimized away. */
/* As per most tasks, this task is implemented in an infinite loop. */
volatile uint16_t count1=0;
for( ;; )
{
/* Print out the name of this task. */
count1++;
/* Delay for a period. */
for( ul = 0; ul < 1000; ul++ )
{
/* This loop is just a very crude delay implementation. There is
nothing to do in here. Later examples will replace this crude
loop with a proper delay/sleep function. */
}
}
}
//Task2
// ----------------------------------------------------------------------------
void vTask2( void *pvParameters )
{
const char *pcTaskName = "Task 2 is running\r\n";
volatile uint32_t ul; /* volatile to ensure ul is not optimized away. */
/* As per most tasks, this task is implemented in an infinite loop. */
volatile uint16_t count2=0;
for( ;; )
{
/* Print out the name of this task. */
count2++;
/* Delay for a period. */
for( ul = 0; ul < 1000; ul++ )
{
/* This loop is just a very crude delay implementation. There is
nothing to do in here. Later examples will replace this crude
loop with a proper delay/sleep function. */
}
}
}
int main( void )
{
/* Create one of the two tasks. Note that a real application should check
the return value of the xTaskCreate() call to ensure the task was created
successfully. */
xTaskCreate( vTask1, /* Pointer to the function that implements the task. */
"Task 1",/* Text name for the task. This is to facilitate
debugging only. */
100, /* Stack depth - small microcontrollers will use much
less stack than this. */
NULL, /* This example does not use the task parameter. */
1, /* This task will run at priority 1. */
NULL ); /* This example does not use the task handle. */
/* Create the other task in exactly the same way and at the same priority. */
xTaskCreate( vTask2, "Task 2", 100, NULL, 2, NULL );
/* Start the scheduler so the tasks start executing. */
vTaskStartScheduler();
/* If all is well then main() will never reach here as the scheduler will
now be running the tasks. If main() does reach here then it is likely that
there was insufficient heap memory available for the idle task to be created.
Chapter 2 provides more information on heap memory management. */
for( ;; );
}
Example2 程式介紹
Task2的優先級>Task1且由於Task2不用等代某件事先被完成,所以隨時都保持在ready狀態,執行的結果程序會被卡在Task2子程室內(Task1 is said to be starved of processing time by Task2)
由於此類不用等代的子程式永遠處於可執行狀態,所以若有兩個以上的此程式需要處理時,必須指定兩者的優先權為相同,否則優先全較低的子程式將不會被觸及。
為了要善用task函數我們需要將函數編寫為事件觸發的型式(event-driven),事件觸發的函式的特性為只有當事件被觸發時函式能被執行,使用此種方式優先級的設定將變得有意義,應為高優先級的task並非隨時都處於能被執行的狀態,這使得低優先全的task能夠被觸及。
Task2的優先級>Task1且由於Task2不用等代某件事先被完成,所以隨時都保持在ready狀態,執行的結果程序會被卡在Task2子程室內(Task1 is said to be starved of processing time by Task2)
由於此類不用等代的子程式永遠處於可執行狀態,所以若有兩個以上的此程式需要處理時,必須指定兩者的優先權為相同,否則優先全較低的子程式將不會被觸及。
為了要善用task函數我們需要將函數編寫為事件觸發的型式(event-driven),事件觸發的函式的特性為只有當事件被觸發時函式能被執行,使用此種方式優先級的設定將變得有意義,應為高優先級的task並非隨時都處於能被執行的狀態,這使得低優先全的task能夠被觸及。
The Blocked State
A task that is waiting for an event is said to be in the ‘Blocked’ state, which is a sub-state of the Not Running state.
Tasks can enter the Blocked state to wait for two different types of event:
1. Temporal (time-related) events—the event being either a delay period expiring, or an absolute time being reached. For example, a task may enter the Blocked state to wait for 10 milliseconds to pass.
(定時觸發)
2. Synchronization events—where the events originate from another task or interrupt. For example, a task may enter the Blocked state to wait for data to arrive on a queue. Synchronization events cover a broad range of event types.
(由其他程式觸發)
FreeRTOS queues, binary semaphores, counting semaphores, mutexes, recursive mutexes, event groups and direct to task notifications can all be used to create synchronization events. All these features are covered in future chapters of this book.
FreeRTOS queues, binary semaphores, counting semaphores, mutexes, recursive mutexes, event groups and direct to task notifications can all be used to create synchronization events. All these features are covered in future chapters of this book.
The Suspended State
‘Suspended’ is also a sub-state of Not Running. Tasks in the Suspended state are not available to the scheduler. The only way into the Suspended state is through a call to the vTaskSuspend() API function, the only way out being through a call to the vTaskResume() or xTaskResumeFromISR() API functions. Most applications do not use the Suspended state.
The Ready State
Tasks that are in the Not Running state but are not Blocked or Suspended are said to be in the Ready state. They are able to run, and therefore ‘ready’ to run, but are not currently in the Running state.
Using the Blocked state to create a delay
由Example1,2中我們使用for loop來達到delay的功能,這個方法有效但是非常粗糙,會導致項Example2中,高優先權之函式佔用掉全部的執行時間的問題,就算他指示在等待時間。
在這裡我們希望在delay時執行其他ready的程式。
vTaskDelay()
places the calling task into the Blocked state for a fixed number of tick interrupts.
The task does not use any processing time while it is in the Blocked state, so the task only
uses processing time when there is actually work to be done.
void vTaskDelay( TickType_t xTicksToDelay );
xTicksToDelay
The number of tick interrupts that the calling task will remain in the Blocked state before being transitioned back into the Ready state.
For example, if a task called vTaskDelay( 100 ) when the tick count was 10,000, then it would immediately enter the Blocked state, and remain in the Blocked state until the tick count reached 10,100.
The macro pdMS_TO_TICKS() can be used to convert a time specified in milliseconds into a time specified in ticks. For example, calling vTaskDelay( pdMS_TO_TICKS( 100 ) ) will result in the calling task remaining in the Blocked state for 100 milliseconds.
Note that the
vTaskDelay() API function is available only when INCLUDE_vTaskDelay is set to 1 in FreeRTOSConfig.h.
Using vTaskDelay() does not guarantee that the frequency at which they run is fixed, as the time at which the tasks leave the Blocked state is relative to when they call vTaskDelay().
Using vTaskDelay() does not guarantee that the frequency at which they run is fixed, as the time at which the tasks leave the Blocked state is relative to when they call vTaskDelay().
Example3
main.c
#include "FreeRTOS.h"
#include "task.h"
#include "stm32f4xx.h"
//Task1
// ----------------------------------------------------------------------------
void vTask1( void *pvParameters )
{
const char *pcTaskName = "Task 1 is running\r\n";
volatile uint32_t ul; /* volatile to ensure ul is not optimized away. */
/* As per most tasks, this task is implemented in an infinite loop. */
volatile uint16_t count1=0;
for( ;; )
{
/* Print out the name of this task. */
count1++;
/* Delay for a period. */
vTaskDelay(pdMS_TO_TICKS(100));
}
}
//Task2
// ----------------------------------------------------------------------------
void vTask2( void *pvParameters )
{
const char *pcTaskName = "Task 2 is running\r\n";
volatile uint32_t ul; /* volatile to ensure ul is not optimized away. */
/* As per most tasks, this task is implemented in an infinite loop. */
volatile uint16_t count2=0;
for( ;; )
{
/* Print out the name of this task. */
count2++;
/* Delay for a period. */
vTaskDelay(pdMS_TO_TICKS(100));
}
}
int main( void )
{
/* Create one of the two tasks. Note that a real application should check
the return value of the xTaskCreate() call to ensure the task was created
successfully. */
xTaskCreate( vTask1, /* Pointer to the function that implements the task. */
"Task 1",/* Text name for the task. This is to facilitate
debugging only. */
100, /* Stack depth - small microcontrollers will use much
less stack than this. */
NULL, /* This example does not use the task parameter. */
1, /* This task will run at priority 1. */
NULL ); /* This example does not use the task handle. */
/* Create the other task in exactly the same way and at the same priority. */
xTaskCreate( vTask2, "Task 2", 100, NULL, 2, NULL );
/* Start the scheduler so the tasks start executing. */
vTaskStartScheduler();
/* If all is well then main() will never reach here as the scheduler will
now be running the tasks. If main() does reach here then it is likely that
there was insufficient heap memory available for the idle task to be created.
Chapter 2 provides more information on heap memory management. */
for( ;; );
}
將Example2中使用for loop製作delay的方發改為使用block state創造delay使得低優先權的Task1也能被執行到
vTaskDelayUntil()
is the API function that should be used when a fixed execution period is required (where you want your task to execute periodically with a fixed frequency),as the time at which the calling task is unblocked is absolute, rather than relative to when the function was called (as is the case with vTaskDelay()).void vTaskDelayUntil( TickType_t * pxPreviousWakeTime, TickType_t xTimeIncrement );
pxPreviousWakeTime
This parameter is named on the assumption that vTaskDelayUntil() is being used to implement a task that executes periodically and with a fixed frequency. In this case, pxPreviousWakeTime holds the time at which the task last left the Blocked state (was ‘woken’ up). This time is used as a reference point to calculate the time at which the task should next leave the Blocked state.
The variable pointed to by pxPreviousWakeTime is updated automatically within the vTaskDelayUntil() function; it would not normally be modified by the application code, but must be initialized to the current tick count before it is used for the first time. Listing 25 demonstrates how the initialization is performed.
xTimeIncrement
This parameter is also named on the assumption that vTaskDelayUntil() is being used to implement a task that executes periodically and with a fixed frequency—the frequency being set by the xTimeIncrement value.
xTimeIncrement is specified in ‘ticks’. The macro pdMS_TO_TICKS() can be used to convert a time specified in milliseconds into a time specified in ticks.
pdMS_TO_TICKS()
converts a time specified in milliseconds into
a time specified in ticks. The resolution available depends on the defined tick frequency, and pdMS_TO_TICKS() cannot be used if the tick frequency is above 1KHz (if configTICK_RATE_HZ is greater than 1000).(不曉得為什麼我實測可以可能版本有更新我使用FreeRTOSv10.0.0)
Example4
main.c
#include "FreeRTOS.h"
#include "task.h"
#include "stm32f4xx.h"
//Task1
// ----------------------------------------------------------------------------
void vTask1( void *pvParameters )
{
volatile uint16_t count1=0;
TickType_t xLastWakeTime;
/* The xLastWakeTime variable needs to be initialized with the current tick
count. Note that this is the only time the variable is written to explicitly.
After this xLastWakeTime is automatically updated within vTaskDelayUntil(). */
xLastWakeTime = xTaskGetTickCount();
/* As per most tasks, this task is implemented in an infinite loop. */
for( ;; )
{
/* Print out the name of this task. */
count1++;
GPIO_ToggleBits(GPIOD,GPIO_Pin_15);
/* This task should execute every 1 milliseconds exactly. As per
the vTaskDelay() function, time is measured in ticks, and the
pdMS_TO_TICKS() macro is used to convert milliseconds into ticks.
xLastWakeTime is automatically updated within vTaskDelayUntil(), so is not
explicitly updated by the task. */
vTaskDelayUntil( &xLastWakeTime, pdMS_TO_TICKS( 1 ) );
}
}
////Task2
//// ----------------------------------------------------------------------------
void vTask2( void *pvParameters )
{
volatile uint16_t count2=0;
TickType_t xLastWakeTime;
/* The xLastWakeTime variable needs to be initialized with the current tick
count. Note that this is the only time the variable is written to explicitly.
After this xLastWakeTime is automatically updated within vTaskDelayUntil(). */
xLastWakeTime = xTaskGetTickCount();
/* As per most tasks, this task is implemented in an infinite loop. */
for( ;; )
{
/* Print out the name of this task. */
count2++;
/* This task should execute every 1 milliseconds exactly. As per
the vTaskDelay() function, time is measured in ticks, and the
pdMS_TO_TICKS() macro is used to convert milliseconds into ticks.
xLastWakeTime is automatically updated within vTaskDelayUntil(), so is not
explicitly updated by the task. */
GPIO_ToggleBits(GPIOD,GPIO_Pin_13);
vTaskDelayUntil( &xLastWakeTime, pdMS_TO_TICKS( 1 ) );
}
}
void GPIOInit(){
GPIO_InitTypeDef g;
g.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; // ?? 12,13,14,15 ?
g.GPIO_Mode = GPIO_Mode_OUT; // ???????
g.GPIO_Speed = GPIO_Speed_100MHz; // ?? GPIO ???100 MHz
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); // ?? GPIOD
GPIO_Init(GPIOD, &g); // ??? GPIO D
}
int main( void )
{
GPIOInit();
/* Create one of the two tasks. Note that a real application should check
the return value of the xTaskCreate() call to ensure the task was created
successfully. */
xTaskCreate( vTask1, /* Pointer to the function that implements the task. */
"Task 1",/* Text name for the task. This is to facilitate
debugging only. */
1000, /* Stack depth - small microcontrollers will use much
less stack than this. */
NULL, /* This example does not use the task parameter. */
1, /* This task will run at priority 1. */
NULL ); /* This example does not use the task handle. */
/* Create the other task in exactly the same way and at the same priority. */
xTaskCreate( vTask2, "Task 2", 1000, NULL, 2, NULL );
/* Start the scheduler so the tasks start executing. */
vTaskStartScheduler();
/* If all is well then main() will never reach here as the scheduler will
now be running the tasks. If main() does reach here then it is likely that
there was insufficient heap memory available for the idle task to be created.
Chapter 2 provides more information on heap memory management. */
for( ;; );
}
Example4 程式介紹
功能為每1ms LED燈改變一次狀態,並且頻率需要極為正確使用vTaskDelayUntil()函數,此程式的configTICK_RATE_HZ設定為2000,測試pdMS_TO_TICKS()函數是否會沒有做用,結果為程式執行的很正確,所以其實pdMS_TO_TICKS()在configTICK_RATE_HZ參數>1000時還是可以使用。
留言
張貼留言