函数 | 描述 |
---|---|
xTaskCreate() | 使用动态的方法创建一个任务。 |
xTaskCreateStatic() | 使用静态的方法创建一个任务。 |
xTaskCreateRestricted() | 创建一个使用 MPU 进行限制的任务,相关内存使用动态内存分配。 |
vTaskDelete() | 删除一个任务。 |
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 函数指针, 任务函数
const char * const pcName, // 任务的名字
const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小,单位为word,10表示40字节
void * const pvParameters, // 调用任务函数时传入的参数
UBaseType_t uxPriority, // 优先级
TaskHandle_t * const pxCreatedTask ); // 任务句柄, 以后使用它来操作这个任务
参数 | 描述 |
---|---|
pxTaskCode | 函数指针,可以简单地认为任务就是一个C函数。 它稍微特殊一点:永远不退出,或者退出时要调用"vTaskDelete(NULL)" |
pcName | 任务的名字,FreeRTOS内部不使用它,仅仅起调试作用。 长度为:configMAX_TASK_NAME_LEN |
usStackDepth | 每个任务都有自己的栈,这里指定栈大小。 单位是word,比如传入100,表示栈大小为100 word,也就是400字节。 最大值为uint16_t的最大值。 怎么确定栈的大小,并不容易,很多时候是估计。 精确的办法是看反汇编码。 |
pvParameters | 调用pvTaskCode函数指针时用到:pvTaskCode(pvParameters) 传递给任务函数的参数。 |
uxPriority | 优先级范围:0~(configMAX_PRIORITIES – 1) 数值越小优先级越低, 如果传入过大的值,xTaskCreate会把它调整为(configMAX_PRIORITIES – 1) |
pxCreatedTask | 用来保存xTaskCreate的输出结果:task handle。 以后如果想操作这个任务,比如修改它的优先级,就需要这个handle。 如果不想使用该handle,可以传入NULL |
返回值 | 成功:pdPASS; 失败:errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY(失败原因只有内存 不足) 注意:文档里都说失败时返回值是pdFAIL,这不对。 pdFAIL是0,errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY是-1。 |
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 )
参数 | 描述 |
---|---|
pxTaskCode | 函数指针,可以简单地认为任务就是一个C函数。 它稍微特殊一点:永远不退出,或者退出时要调用"vTaskDelete(NULL)" |
pcName | 任务的名字,FreeRTOS内部不使用它,仅仅起调试作用。 长度为:configMAX_TASK_NAME_LEN |
ulStackDepth | 任务堆栈大小, 由于本函数是静态方法创建所以用户给出任务堆栈大小, 一般是一个数组,此参数就是这个数组的大小 |
pvParameters | 调用pvTaskCode函数指针时用到:pvTaskCode(pvParameters) 传递给任务函数的参数。 |
uxPriority | 优先级范围:0~(configMAX_PRIORITIES – 1) 数值越小优先级越低, 如果传入过大的值,xTaskCreate会把它调整为(configMAX_PRIORITIES – 1) |
puxStackBuffer | 任务堆栈,一般为 数组类型要StackType_t类 |
pxTaskBuffer | 任务控制块 |
返回值 | NULL: 任务创建失败, puxStackBuffer或 pxTaskBuffer为 NULL的时候会导致这个 的时候会导致这个 错误的发生。 其他值 : 任 |
此函数也是用来创建任务的, 只不过此函数要求 所使用的 MCU有 MPU(内存保护单元 内存保护单元 ), 用此函数创建的任务会受到 MPU的保护。 其他的功能和函数 xTaxkCreate()一
BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition,
TaskHandle_t * pxCreatedTask )
参数 | 描述 |
---|---|
pxTaskDefinition: | 指向一个结构体 TaskParameters_t,这个结构体描述了任务的函数、 ,这个结构体描述了任务的函数、 堆栈大小、优先级等。此结构体在文件 task.h中有定义。 |
pxCreatedTask: | 任务句柄 |
返回值 | pdPASS:创建成功 其他值 : 任务未创建成功, 很有可能是因为 FreeRTOS的堆太小 |
vTaskDelete( TaskHandle_t xTaskToDelete )
参数 | 描述 |
---|---|
xTaskToDelete: | 要删除的任务句柄 |
返回值 | NULL |
使用2个函数分别创建2个任务。
任务1的代码:
void vTask1( void *pvParameters )
{
const char *pcTaskName = "T1 run\r\n";
volatile uint32_t ul; /* volatile用来避免被优化掉 */
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
/* 打印任务1的信息 */
printf( "%s ",pcTaskName );
/* 延迟一会(比较简单粗暴) */
delay_xms(5);
}
}
任务2的代码:
void vTask2( void *pvParameters )
{
const char *pcTaskName = "T2 run\r\n";
volatile uint32_t ul; /* volatile用来避免被优化掉 */
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
/* 打印任务1的信息 */
printf( "%s ",pcTaskName );
/* 延迟一会(比较简单粗暴) */
delay_xms(5);
}
}
main 的代码
int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(336,8,2,7); //设置时钟,168Mhz
delay_init(168); //初始化延时函数
uart_init(115200); //初始化USART
xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
xTaskCreate(vTask2, "Task 2", 1000, NULL, 1, NULL);
/* 启动调度器 */
vTaskStartScheduler();
while(1)
{
printf("Hello is ok\r\n");
HAL_Delay(1000);
}
}
运行结果如下:
注意:
使用静态方法创建任务的时候需要将宏 configSUPPORT_STATIC_ALLOCATION设置为 1, 在文件 FreeRTOSConfig.h中
#define configSUPPORT_STATIC_ALLOCATION 1 //静态内存
宏 configSUPPORT_STATIC_ALLOCATION定义为 1以后编译一次,会提示我们有两个函 以后编译一次,会提示我们有两个函 数未定义,如图所示
如果使用静态方法 的 话 需 要 用 户 实 现 两 个 函 数 vApplicationGetIdleTaskMemory() 和vApplicationGetTimerTaskMemory()。通过这两个函数来给空闲任务和定时器服务任务的任务堆栈和任务控制块分配内存,这两个函数我们在 mainc.c 中定义,定义如下:
//空闲任务任务堆栈
static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];
//空闲任务控制块
static StaticTask_t IdleTaskTCB;
//定时器服务任务堆栈
static StackType_t TimerTaskStack[configTIMER_TASK_STACK_DEPTH];
//定时器服务任务控制块
static StaticTask_t TimerTaskTCB;
//获取空闲任务地任务堆栈和任务控制块内存,因为本例程使用的
//静态内存,因此空闲任务的任务堆栈和任务控制块的内存就应该
//有用户来提供,FreeRTOS提供了接口函数vApplicationGetIdleTaskMemory()
//实现此函数即可。
//ppxIdleTaskTCBBuffer:任务控制块内存
//ppxIdleTaskStackBuffer:任务堆栈内存
//pulIdleTaskStackSize:任务堆栈大小
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer=&IdleTaskTCB;
*ppxIdleTaskStackBuffer=IdleTaskStack;
*pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;
}
//获取定时器服务任务的任务堆栈和任务控制块内存
//ppxTimerTaskTCBBuffer:任务控制块内存
//ppxTimerTaskStackBuffer:任务堆栈内存
//pulTimerTaskStackSize:任务堆栈大小
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize)
{
*ppxTimerTaskTCBBuffer=&TimerTaskTCB;
*ppxTimerTaskStackBuffer=TimerTaskStack;
*pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;
}
可以看出这两个函数很简单,用户定义静态的任务堆栈和任务控制块内存,然后将这些内存传递给函数参数。最后创建空闲任务和定时器服务任务的 API 函数会调用vApplicationGetIdleTaskMemory()和 vApplicationGetTimerTaskMemory()来获取这些内存。
vtask3代码
void vTask3( void *pvParameters )
{
const char *pcTaskName = "T3 run\r\n";
volatile uint32_t ul; /* volatile用来避免被优化掉 */
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
/* 打印任务1的信息 */
printf( "%s", pcTaskName);
/* 延迟一会(比较简单粗暴) */
delay_ms(1000);
}
}
main 代码
StackType_t vtask3Stack[100];
StaticTask_t xtask3TCB;
int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(336,8,2,7); //设置时钟,168Mhz
delay_init(168); //初始化延时函数
uart_init(115200); //初始化USART
// xTaskCreate(vTaskFunction, "Task 1", 1000, (void *)pcTextForTask1, 1, NULL);
// xTaskCreate(vTaskFunction, "Task 2", 1000, (void *)pcTextForTask2, 1, NULL);
//xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
/* 启动调度器 */
xTaskCreateStatic(vTask3, "Task 3", 100, NULL, 1, vtask3Stack,&xtask3TCB);
vTaskStartScheduler();
while(1)
{
printf("Hello is ok\r\n");
HAL_Delay(1000);
}
}
运行结果
可以看出来是和动态创建任务是一样的,并且这个方式很麻烦,一般情况都使用动态创建!!
多个任务可以使用同一个函数,怎么体现它们的差别?
我们创建2个任务,使用同一个函数,代码如下:
void vTaskFunction( void *pvParameters )
{
const char *pcTaskText = pvParameters;
volatile uint32_t ul; /* volatile用来避免被优化掉 */
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
/* 打印任务的信息 */
printf( "%s", pcTaskText);
/* 延迟一会(比较简单粗暴) */
delay_ms(1000);
}
}
上述代码中的pcTaskText 来自参数pvParameters , pvParameters 来自哪里?创建任务时传入的。
代码如下:
static const char *pcTextForTask1 = "T1 run\r\n";
static const char *pcTextForTask2 = "T2 run\r\n";
int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(336,8,2,7); //设置时钟,168Mhz
delay_init(168); //初始化延时函数
uart_init(115200); //初始化USART
xTaskCreate(vTaskFunction, "Task 1", 1000, (void *)pcTextForTask1, 1, NULL);
xTaskCreate(vTaskFunction, "Task 2", 1000, (void *)pcTextForTask2, 1, NULL);
/* 启动调度器 */
vTaskStartScheduler();
while(1)
{
printf("Hello is ok\r\n");
HAL_Delay(1000);
}
}
运行结果:
看着和刚刚一样
本节代码会涉及优先级的知识,可以只看vTaskDelete的用法,忽略优先级的讲解。
我们要做这些事情:
创建任务1:任务1的大循环里,创建任务2,然后休眠一段时间
任务2:打印一句话,然后就删除自己
任务1的代码如下:
TaskHandle_t xTask2Handle = NULL;
void vTask1( void *pvParameters )
{
const TickType_t xDelay100ms = pdMS_TO_TICKS( 100UL );
BaseType_t ret;
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
/* 打印任务的信息 */
printf("Task1 is running\r\n");
ret = xTaskCreate( vTask2, "Task 2", 1000, NULL, 2, &xTask2Handle );
if (ret != pdPASS)
printf("Create Task2 Failed\r\n");
// 如果不休眠的话, Idle任务无法得到执行
// Idel任务会清理任务2使用的内存
// 如果不休眠则Idle任务无法执行, 最后内存耗尽
vTaskDelay( xDelay100ms );
}
}
任务2的代码如下:
void vTask2( void *pvParameters )
{
/* 打印任务的信息 */
printf("Task2 is running and about to delete itself\r\n");
// 可以直接传入参数NULL, 这里只是为了演示函数用法
vTaskDelete(xTask2Handle);
}
main函数代码如下:
int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(336,8,2,7); //设置时钟,168Mhz
delay_init(168); //初始化延时函数
uart_init(115200); //初始化USART
// xTaskCreate(vTaskFunction, "Task 1", 1000, (void *)pcTextForTask1, 1, NULL);
// xTaskCreate(vTaskFunction, "Task 2", 1000, (void *)pcTextForTask2, 1, NULL);
xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
/* 启动调度器 */
vTaskStartScheduler();
while(1)
{
printf("Hello is ok\r\n");
HAL_Delay(1000);
}
}
运行结果如下:
任务运行图:
链接:https://pan.baidu.com/s/1IJJu9LHdEAh_i_lHVwNOSg?pwd=6bj7
提取码:6bj7
–来自百度网盘超级会员V4的分享