一、四种参数传递方式
1.1 整数传递
使用 (void *) 任何类型传递参数,通过地址传递给任务。
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
void myTask(void *pvPragma)
{
int *getNum;
getNum = (int *)pvPragma; //强转int类型指针
printf("Task get num = %d\n",*getNum); //此处*为解地址符,解出地址存的东西
vTaskDelay(1000 / portTICK_PERIOD_MS);
vTaskDelete(NULL);
}
int IntNum = 1;
void app_main(void)
{
xTaskCreate(myTask, "mytask1", 2048, (void *)&IntNum, 1, NULL); //取地址后传递参数
}
效果如下:
1.2 数组
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
void myTask(void *pvPragma)
{
int *pArrayNum;
pArrayNum = (int *)pvPragma;
printf("Task get num1 = %d\n", *pArrayNum);
//因为是int类型指针 +1移动自动移动一个int
printf("Task get num2 = %d\n", *(pArrayNum + 1)); //此处*为解地址符,解出地址存的东西
printf("Task get num3 = %d\n", *(pArrayNum + 2));
vTaskDelay(1000 / portTICK_PERIOD_MS);
vTaskDelete(NULL);
}
int testNum[] = {5, 6, 7};
void app_main(void)
{
//数组本来就是地址 直接传递 不加&
xTaskCreate(myTask, "mytask1", 2048, (void *)testNum, 1, NULL);
}
效果如下:
1.3 结构体
和整数传递很像,只不过换成了结构体,任务函数也是强转成结构体指针。
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
//-------------------------------------------------------
typedef struct A_struct
{
int num1;
int num2;
} xstruct;//xstruct=struct A_struct,不清楚时查看struct和typedef struct的区别
xstruct testnum = {5,6};
void mytask(void *pvParam)
{
xstruct *p; //声明一个结构体指针类型的变量,
p = (xstruct *)pvParam; // 因为这里参数传的都是地址,将参数强制转化为相同的类型,取得输入参数的地址
printf("I get testnum num1 = %d\n", p->num1);
printf("I get testnum num2 = %d\n", p->num2);
vTaskDelay(1000 / portTICK_PERIOD_MS);
vTaskDelete(NULL);
}
//-------------------------------------------------------
void app_main(void)
{
xTaskCreate(mytask, "mytask1", 2048, (void *)&testnum, 1, NULL);
}
效果如下:
1.4 字符串
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
static const char *ptest = "hello world";
void mytask(void *pvParam)
{
char *p;
p = (char *)pvParam;
printf("I get message = %s\n", p);
vTaskDelay(1000 / portTICK_PERIOD_MS);
vTaskDelete(NULL);
}
void app_main(void)
{
xTaskCreate(mytask, "mytask1", 2048, (void *)ptest, 1, NULL);
}
效果如下:
二、Task 任务优先级
2.1 任务优先级简介
FreeRTOS 中任务优先级数字越大,优先级越高。
引用 RTOS 官方手册RTOS task priorities in FreeRTOS for pre-emptive and co-operative real time operation
也就是 configMAX_PRIORITIES 是当前项目中允许的最大优先级。
在下路径中可以找到这个宏定义,其中在 esp-idf 代码中(非项目代码,应该是模板代码路径),FreeRTOSConfig.h 文件保存了这个的默认值。
2.2 使用 uxTaskPriorityGet()
函数原型:
UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask );
//参数: xTask: 要查找的任务的任务句柄。
//返回值: 获取到的对应的任务的优先级。
对于 esp32 默认的 configMAX_PRIORITIES 是 25。但是如果我们创建优先级的时候将优先级设置为 25 以上。比如下文的 41。
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
static const char *ptest = "hello world";
void mytask(void *pvParam)
{
char *p;
p = (char *)pvParam;
printf("I get message = %s\n", p);
vTaskDelay(1000 / portTICK_PERIOD_MS);
vTaskDelete(NULL);
}
void app_main(void)
{
UBaseType_t iPriority = 0;
TaskHandle_t pxTask = NULL;
xTaskCreate(mytask, "mytask1", 2048, (void *)ptest, 41, &pxTask);
iPriority = uxTaskPriorityGet(pxTask);
printf("iPriority = %d\n",iPriority);
}
因为 41 大于 24,所以任务优先级还是被强制改成了 24。
2.3 使用 vTaskPrioritySet()
vTaskPrioritySet() 可以设置任务的优先级。如果设置的优先级高于当前执行任务的优先级,则上下文切换将在此函数返回之前发生。
void app_main(void)
{
TaskHandle_t pxTask = NULL;
UBaseType_t iPriority = 0;
xTaskCreate(myTask1, "mytask1", 2048, (void *)ptest, 1,&pxTask);
xTaskCreate(myTask2, "mytask2", 2048, (void *)ptest, 4, NULL);
vTaskPrioritySet(pxTask,3);
iPriority = uxTaskPriorityGet(pxTask);
printf("Task1 iPriority = %d\n",iPriority);
}
三、Task 堆栈设置和调试
3.1 显示任务详细信息 vTaskList()
通过 vTaskList
来协助分析操作系统当前 task 状态,以帮助优化内存,帮助定位栈溢出问题。
void vTaskList( char *pcWriteBuffer );
parameter | description |
pcWriteBuffer | 保存任务状态信息表的存储区,须足够大 |
return | 空 |
pcWriteBuffe 表中信息:
说明 | 定义 |
Name | 创建任务的时候给任务分配的名字 |
State | 任务的状态信息,X:运行态,B:阻塞态,R:就绪态,S:挂起态,D:删除态 |
Priority | 任务优先级 |
Stack | 任务堆栈的“高水位线”,就是堆栈历史最小剩余值 |
Num | 任务编号,这个编号是唯一的;当多个任务使用同一个任务名的时候;可以通过编号来区分 |
void task1(void *pvParam)
{
while (1)
{
printf("task1!\n");
vTaskDelay(3000 / portTICK_PERIOD_MS);
}
}
void task2(void *pvParam)
{
while (1)
{
printf("task2!\n");
vTaskDelay(3000 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
xTaskCreate(task1, "task1", 4096, NULL, 1, NULL);
xTaskCreate(task2, "task2", 4096, NULL, 1, NULL);
static char pcWriteBuffer[512] = {0};
while(1)
{
vTaskList(pcWriteBuffer);
printf("-----------------------------------------\n");
printf("Name State Priority Stack Num\n");
printf("%s\n",pcWriteBuffer);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
3.2 查看任务剩余的堆栈空间 uxTaskGetStackHighWaterMark()
函数原型:
UBaseType_t uxTaskGetStackHighWaterMark(TaskHandle_t xTask)
传 入 值:xTask 要查询的任务的任务句柄,若为NULL表示查询自身任务的高水位线
返 回 值:任务堆栈的高水位线值,即堆栈的历史剩余最小值。
注意是以字节为单位
void task1(void *pvParam)
{
while (1)
{
printf("task1!\n");
vTaskDelay(3000 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
TaskHandle_t pxTask1;
xTaskCreate(task1, "task1", 4096, NULL, 1,&pxTask1);
UBaseType_t iStack;
while(1)
{
iStack = uxTaskGetStackHighWaterMark(pxTask1);
printf("task1 iStack = %d\n",iStack);
vTaskDelay(3000 / portTICK_PERIOD_MS);
}
}
上述代码中,task1有两行函数。运行效果如下:
如果我们将 task1中的 printf 注释掉
void task1(void *pvParam)
{
while (1)
{
printf("task1!\n");
vTaskDelay(3000 / portTICK_PERIOD_MS);
}
}
效果如下:
与之对比,可得出printf占用了大概300多字节的堆栈。