目录
- 创建任务原型xTaskCreate()函数
- 创建任务原型xTaskCreateStatic()函数
- 任务控制块
- 动态创建任务
- 静态创建任务
- 删除动态创建任务
- 删除静态创建任务
- 同一个函数创建多个不同的任务
1.创建任务原型xTaskCreate()函数
位于task.c文件中,如果任务使用xTaskCreate()创建的,则需要从FreeRTOS的堆中自动分配RAM。
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
参数1为一个指针,指向实现任务的函数(实际上就是函数名)
参数2为函数的名称,没什么实际含义,就是容易区分是哪个任务
参数3为栈大小,也就是这个任务需要占多大的栈内存,默认最小栈大小为configMINIMAL_STACK_SIZE,每个任务都有它单独的栈内存
参数4为任务的实参,创建任务时,给任务传来参数,void *为万能指针
参数5为任务的优先级设置,数字越小,优先级越低
参数6为创建的任务句柄,操作这个句柄就相当于操作这个任务,可以通过句柄来改变任务的优先级、删除任务等;如果不需要时直接设置为NULL
2.创建任务原型xTaskCreateStatic()函数
位于task.c文件中,如果使用xTaskCreateStatic()创建任务,则需要应用程序编写器提供RAM,这样相对于xTaskCreate()来说,需要两个额外的函数参数,但允许在编译时静态分布RAM。
//创建静态任务原型
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
StackType_t * const puxStackBuffer,
StaticTask_t * const pxTaskBuffer )
参数1为一个指针,指向实现任务的函数(实际上就是函数名)
参数2为函数的名称,没什么实际含义,就是容易区分是哪个任务
参数3为栈大小,也就是这个任务需要占多大的栈内存,默认最小栈大小为configMINIMAL_STACK_SIZE,每个任务都有它单独的栈内存
参数4为任务的实参,创建任务时,给任务传来参数,void *为万能指针
参数5为任务的优先级设置,数字越小,优先级越低
参数6为变量数组,这个数组将被用作已创建任务的堆栈
参数7将用于保存创建的任务的数据结构(TCB)
3.任务控制块
在task.c文件内
//TCB_t 结构体,就相当于handle句柄
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack;
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSettings;
#endif
ListItem_t xStateListItem;
ListItem_t xEventListItem;
UBaseType_t uxPriority;
StackType_t *pxStack;
char pcTaskName[ configMAX_TASK_NAME_LEN ];
#if ( portSTACK_GROWTH > 0 )
StackType_t *pxEndOfStack;
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
UBaseType_t uxCriticalNesting;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTCBNumber;
UBaseType_t uxTaskNumber;
#endif
#if ( configUSE_MUTEXES == 1 )
UBaseType_t uxBasePriority;
UBaseType_t uxMutexesHeld;
#endif
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
TaskHookFunction_t pxTaskTag;
#endif
#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
#endif
#if( configGENERATE_RUN_TIME_STATS == 1 )
uint32_t ulRunTimeCounter;
#endif
#if ( configUSE_NEWLIB_REENTRANT == 1 )
struct _reent xNewLib_reent;
#endif
#if( configUSE_TASK_NOTIFICATIONS == 1 )
volatile uint32_t ulNotifiedValue;
volatile uint8_t ucNotifyState;
#endif
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
uint8_t ucStaticallyAllocated;
#endif
#if( INCLUDE_xTaskAbortDelay == 1 )
uint8_t ucDelayAborted;
#endif
} tskTCB;
typedef tskTCB TCB_t;
4.动态创建任务
新创建的任务最初都处于就绪状态,如果没有更高优先级的任务可以运行,则会立即成为正在运行的状态任务。创建任务可以在启动调度程序之前和之后。
动态创建任务也就相当于是在堆内创建任务,FreeRTOS 做法是在 SRAM 里面定义一个大数组,也就是堆内存,供 FreeRTOS 的动态内存分配函数使用。
//FreeRTOSConfig.h
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) )//系统所有总的堆内存的大小
//main.c文件
//写一个任务1函数,让其打印1,也就对应于上文的参数1
void Task1Function(void *param)
{
while(1)
{
printf(“1”);
}
}
//写一个任务2函数,让其打印2,也就是也就是对应月上文的参数1
void Task2Function(void *param)
{
while(1)
{
printf("2"):
}
}
int main(void)
{
//创建一个任务句柄,也就是对应于上文的参数6
TaskHandle_t xHandleTask1;
printf("Hello World!");
//动态创建Task1
xTaskCreate(Task1Function, //创建一个任务
"Task1",//任务名
100,//栈内存
NULL,//实参
1,//优先级
&xHandleTask1//句柄
);
//动态创建Task2
xTaskCreate(Task2Function,
"Task2",
100,
NULL,
1,//与Task1一样,设置优先级为1
NULL//未使用句柄
}
vTaskStartScheduler(); //开启任务调度
}
此处Task1和Task2都使用串口1打印,两个任务交替执行,程序debug的结果为:
如果Task1和Task2使用两个串口来打印数据,则看起来像是两个任务同时进行,但实际上是时间片流转的方式,将一个任务拆分为一段一段的小任务,然后两个任务交替执行。
ps:当然这些任务也可以改为led=0延时1s,led=1延时1s来实现灯的闪烁,这也算是实现了嵌入式实时操作系统的点灯。值得注意的事情是,如果要使用延时函数必须得使用FreeRTOS的延时函数vTaskDelay(),不能使用裸机程序的延时函数。FreeRTOS提供的延时函数是阻塞延时,当前任务会被挂起,调度器执行当前的就绪任务,从而实现多任务。而裸机程序提供的延时函数则整个任务会形成死循环,如果当前任务恰好的优先级最高,则只能执行此任务,其余任务无法执行,无法实现多任务。
FreeRTOS的延时函数最小为ms级,如果想要单位为us的话,则需要使用使用SysTick.c 文件中的延时函数,注意此文件的两个延时函数与裸机的延时函数不同。
5. 静态创建任务
实现静态任务的创建,首先需要先将configSUPPORT_STATIC_ALLOCATION参数设置为1,其次需要设置两个函数vApplicationGetIdleTaskMemory()这个函数是用户设定的空闲(Idle)任 务的堆栈大小,必须由用户自己分配,而不能是动态分配
//FreeRTOSConfig.h文件
//使用静态方法来创建任务的时候,首先需要将configSUPPORT_STATIC_ALLOCATION参数设置为1
#define configSUPPORT_STATIC_ALLOCATION 1 //支持静态分配内存
//main.c文件
//写任务1函数
void Task1Function(void *param)
{
while(1)
{
printf("1"):
}
}
//写任务2函数
void Task2Function(void *param)
{
while(1)
{
printf("2"):
}
}
//写任务3的函数
void Task3Function(void *param)
{
while(1)
{
printf("3"):
}
}
//创建任务3的堆栈
StaticTask_t xTask3Buffer[100];
//创建任务3的任务块
StaticTask_t xTask3TCB;
//创建空闲任务的堆栈
StaticTask_t xIdleTaskBuffer[100];
//创建空闲任务的任务块
StaticTask_t xIdleTaskTCB;
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer=&xIdelTaskTCB; //任务控制内存
*ppxIdleTaskStackBuffer=xIdleTaskStack;//任务堆栈内存
*pulIdleTaskStackSize=100;//任务堆栈大小
}
int main()
{
//创建任务1的句柄,也就是对应于上文的参数6
TaskHandle_t xHandleTask1;
printf("Hello World!");
//动态创建任务1
xTaskCreate(Task1Function,//函数名
"task1",//函数名称
100,//堆栈内存
NULL,//函数实参
1,//优先级
&xHandleTask1
);
//动态创建任务2
xTaskCreate(Task2Function,//函数名
"task2",//函数名称
100,//堆栈内存
NULL,//函数实参
1,//优先级
NULL
);
//静态创建任务3
xTaskCreateStatic(Task3Function,//函数名
"task3",//函数名称
100,//堆栈内存
NULL,//函数实参
1,//优先级
xTask3Buffer,//变量数组
&xTask3TCB,
);
vTaskStartScheduler(); //开启任务调度
}
当设置任务3的优先级也为1时,程序执行的结果:
当设置任务3的优先级为2时,程序执行的结果:
对FreeRTOS而言,高优先级的任务先执行,除非它主动放弃执行,低优先级的任务才能执行,否则低优先级的任务永远无法执行;同优先级的任务交替执行。
6.删除动态创建任务
删除任务的函数:void vTaskDelete( TaskHandle_t xTaskToDelete );
参数为函数的句柄
动态创建的任务只能删除同为动态创建的任务,比如此处Task2可以删除Task1,也可以自杀
//在上面创建任务的任务2函数中增加语句实现删除任务1和任务2自杀
TaskHandle_t xHandleTask1;//这句话要改为全局变量,放在main函数的外面
void Task2Function(void *param)
{
int i=0;
while(1)
{
printf("2"):
if(i++ == 100)
{
vTaskDelete( xHandleTask1 );//删除任务1
}
if(i == 200)
{
vTaskDelete( NULL);//任务2自杀
}
}
}
程序执行的结果为:
7.删除静态创建任务
删除静态创建任务相对于动态创建任务更麻烦一些,因为静态创建任务的参数并没有TaskHandle_t类型的参数,但是xTaskCreateStatic()函数的返回值为TaskHandle_t类型,因此我们可以定义一个全局变量来存放此函数的返回值。
静态创建的任务只能删除同为静态创建的任务,比如此处Task3自杀
//全局变量
TaskHandle_t xHandleTask3;
//Task3函数
void Task3Function(void *param)
{
int j=0;
while(1)
{
printf("3");
}
if(j++ ==5)
{
vTaskDelete( xHandleTask3 );//任务3自杀
}
}
//main函数将xTaskCreateStatic()函数的返回值赋值给xHandleTask3;
xHandleTask3=xTaskCreateStatic(Task3Function,//函数名
"task3",//函数名称
100,//堆栈内存
NULL,//函数实参
1,//优先级
xTask3Buffer,//变量数组
&xTask3TCB,
);
程序运行结果:
8. 同一个函数创建多个不同的任务
每个任务都占有不同的堆栈,因此同一个函数可以创建多个任务
//创建一个通用的任务函数
void TaskGeneralFunction(void * param)
{
int val = (void *)param;
while(1)
{
printf("%d",val);
}
}
//main函数内添加下面两行
xTaskCreate(TaskGeneralFunction,
"Task4",
100,
(void *)4,
1,
NULL);
xTaskCreate(TaskGeneralFunction,
"Task5",
100,
(void *)5,
1,
NULL);
运行的结果: