这一次的代码基础是"FreeRTOS_编写调试代码"这篇文章。博客链接如下:
动态创建任务函数介绍
每一个任务都有自己的栈,动态创建任务就是使用malloc来开辟栈的空间。
函数声明
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
返回值:创建成功,返回pdPASS
pxTaskCode:函数指针,指向任务的处理函数
pcName:函数名称
usStackDepth:栈的深度,传入100,代表栈大小为100*4字节
pvParameters:参数
uxPriority:优先级,数值越小,优先级越低
pxCreatedTask:句柄,每个任务都被抽象为一个结构体TCB_t,句柄就是指向结构体的指针
使用示例
创建一个任务需要3步:
- 定义一个句柄
- 定义一个任务函数
- 调用任务创建函数
1、定义一个句柄
句柄的类型就是函数声明中句柄的类型:TaskHandle_t,名称以xHandle+任务名定义
具体定义如下:
TaskHandle_t xHandleTask1;
2、定义一个任务函数
在声明中任务函数的类型为:TaskFunction_t,这是一个别名,它实际上的内容如下:
typedef void (* TaskFunction_t)( void * );
因此需要定义一个返回值为void,参数为void*的函数
具体定义如下:
void Task1Function(void *param){
while(1){
printf("1");
}
}
3、调用任务创建函数
按照声明的含义,将参数传入任务创建即可。
具体代码如下:
xTaskCreate(Task1Function,"Task1",100,NULL,1,&xHandleTask1);
这代表创建了一个任务,这个任务在Task1Function中去执行,任务名称是Task1,任务的栈有100*4字节,任务函数Task1Function获得的参数是NULL,任务优先级为1,任务的句柄为xHandleTask1
4、整体main函数与执行效果
main函数主要部分如下:
/* 任务处理函数 */
void Task1Function(void *param){
while(1){
printf("1");
}
}
int main( void )
{
#ifdef DEBUG
debug();
#endif
TaskHandle_t xHandleTask1;/* 任务句柄定义 */
prvSetupHardware();
SerialPortInit();
printf("UART TEST\r\n");
xTaskCreate(Task1Function,"Task1",100,NULL,1,&xHandleTask1);/* 创建任务 */
vTaskStartScheduler();
return 0;
}
执行结果如下:
该结果说明,Task1已经被执行。
RTOS任务交叉运行验证
按照上述的方式创建两个任务,一个任务用来打印1,一个任务用来打印2。观察串口输出现象。
main函数主要部分如下:
/* 任务1处理函数 */
void Task1Function(void *param){
while(1){
printf("1");
}
}
/* 任务2处理函数 */
void Task2Function(void *param){
while(1){
printf("2");
}
}
int main( void )
{
#ifdef DEBUG
debug();
#endif
TaskHandle_t xHandleTask1;/* 任务1句柄 */
TaskHandle_t xHandleTask2;/* 任务2句柄 */
prvSetupHardware();
SerialPortInit();
printf("UART TEST\r\n");
xTaskCreate(Task1Function,"Task1",100,NULL,1,&xHandleTask1);/* 创建任务1 */
xTaskCreate(Task2Function,"Task2",100,NULL,1,&xHandleTask2);/* 创建任务2 */
vTaskStartScheduler();
return 0;
}
执行结果如下:
可以看到,虽然在代码中1和2是跑在两个while中,但实际中1和2是交替打印的。因此验证了RTOS可以实现多任务的功能。
静态创建任务
函数声明
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 )
返回值:句柄,每个任务都被抽象为一个结构体TCB_t,句柄就是指向结构体的指针
pxTaskCode:函数指针,指向任务的处理函数
pcName:函数名称
ulStackDepth:栈的深度,传入100,代表栈大小为100*4字节
pvParameters:参数
uxPriority:优先级,数值越小,优先级越低
puxStackBuffer:栈的内存空间,栈本质就是一片空内存,这里传入数组即可
pxTaskBuffer :指向任务控制块的指针,与句柄作用类似,但类型不一样
相关配置
使用静态任务创建函数时,需自己进行一些配置,具体为以下两步:
- 开启宏开关
- 定义空闲任务函数
1、开启宏开关
静态任务创建函数的定义被宏开关控制,默认情况下为关闭,所以需自己在FreeRTOSConfig.h下添加宏。
具体宏如下:
#define configSUPPORT_STATIC_ALLOCATION 1
2、定义空闲任务函数
因为在开启宏开关之后,调度函数中使用到了空闲任务,所以需要自己实现该函数。
函数具体实现如下:
StackType_t xIdleTask3Stack[100];/* 空闲任务的栈 */
StaticTask_t xIdleTask3TCB; /* 空闲任务的句柄 */
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &xIdleTask3TCB; /* 句柄 */
*ppxIdleTaskStackBuffer = xIdleTask3Stack; /* 栈 */
*pulIdleTaskStackSize = 100; /* 栈地址 */
}
使用示例
实现了上述的配置,就可以使用静态创建任务函数来创建任务了。具体
1、定义一个栈数组
栈数组的类型为StackType_t ,这是个别名,实质是uint32_t
具体定义如下
StackType_t xTask3Stack[100];
2、定义一个指向任务控制块的指针
指向任务控制块的指针的类型就是函数声明中句柄的类型:pxTaskBuffer ,名称以x任务名+TCB来命名
具体定义如下:
StaticTask_t xTask3TCB;
3、定义一个任务函数
任务函数的定义方法与动态创建任务时一样。
具体的定义如下:
void Task3Function(void *param){
while(1){
printf("3");
}
}
4、调用任务创建函数
按照声明的含义,将参数传入任务创建即可。
具体代码如下:
xTaskCreateStatic(Task3Function,"Task3",100,NULL,1,xTask3Stack,&xTask3TCB);
这代表创建了一个任务,这个任务在Task3Function中去执行,任务名称是Task3,任务的栈有100*4字节,任务函数Task3Function获得的参数是NULL,任务优先级为1,任务手动分配的栈空间为xTask3Stack,指向任务控制块的指针为xTask3TCB。
5、整体main函数与执行效果
main函数主要部分如下:
/* 任务3的栈空间与句柄 */
StackType_t xTask3Stack[100];/* 栈 */
StaticTask_t xTask3TCB; /* 句柄 */
/* 实现空闲任务 */
StackType_t xIdleTask3Stack[100];/* 栈 */
StaticTask_t xIdleTask3TCB; /* 句柄 */
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &xIdleTask3TCB; /* 句柄 */
*ppxIdleTaskStackBuffer = xIdleTask3Stack; /* 栈 */
*pulIdleTaskStackSize = 100; /* 栈地址 */
}
/* 3个任务的定义 */
void Task1Function(void *param){
while(1){
printf("1");
}
}
void Task2Function(void *param){
while(1){
printf("2");
}
}
void Task3Function(void *param){
while(1){
printf("3");
}
}
int main( void )
{
#ifdef DEBUG
debug();
#endif
TaskHandle_t xHandleTask1;
TaskHandle_t xHandleTask2;
prvSetupHardware();
SerialPortInit();
printf("UART TEST\r\n");
/* 动态创建任务1、任务2 */
xTaskCreate(Task1Function,"Task1",100,NULL,1,&xHandleTask1);
xTaskCreate(Task2Function,"Task2",100,NULL,1,&xHandleTask2);
/* 静态创建任务3 */
xTaskCreateStatic(Task3Function,"Task3",100,NULL,1,xTask3Stack,&xTask3TCB);
vTaskStartScheduler();
return 0;
}
执行结果如下:
该结果说明,Task3已经被执行。
删除任务
函数声明
void vTaskDelete( TaskHandle_t xTaskToDelete )
xTaskToDelete:函数创建时,赋值的句柄
杀死自己时传入NULL
使用示例
在静态创建任务的代码基础上,修改任务2的任务函数,使得任务2运行几次之后,杀死任务1;再运行几次之后,杀死自己。
具体代码如下:
/* 改写后的任务2任务函数 */
void Task2Function(void *param){
int i=0;
while(1){
printf("2");
i++;
/* 运行15次,杀死任务1 */
if(i==15){
vTaskDelete((TaskHandle_t)param);
}
/* 运行30次,杀死自己 */
if(i==30){
vTaskDelete(NULL);
}
}
}
/* 改写后的任务2创建函数 */
xTaskCreate(Task2Function,"Task2",100,(void*)xHandleTask1,1,&xHandleTask2);
执行结果如下:
可以看到2打印了一些后,1不再被打印(任务1被删除)。2又打印了一些后,2也不再打印(任务2也被删除)