学习目标:
- 一周掌握 FreeRtos 入门知识
学习内容:
为什么要学习FreeRTOS
1. 只需要3个c文件就能实现FreeRtos的基本功能,分别是task.c queue.c list.c.文件小非常适合嵌入式操作系统,并且已经移植到很多设备平台上,使用性很广.
2. Free=免费开源,Rtos=实时操作系统
3. Esp32,非常适合用于开发,集成了很多stm32没有的外设,例如wifi 蓝牙一块芯片就可以完成很多功能,而且性能也比高于stm32,芯片价格也比stm32便宜,相信以后会是esp32的天下,并且esp32-idf已经移植好FreeRtos,所以基于esp32-idf学习FreeRtos是比较好的选择.
以下为学习笔记,供大家学习:
一. 创建Mytask的任务函数方法
传入函数的参数为无类型指针方便后期传入内容定义:
void MytTask(void* param)
{
while(1)//任务内容是无限循环的
{
printf("Hello World !");//任务内容是打印Hello World!
vTaskDelay(1000/portTICK_PERIOD_MS);
//延时1000ms=1s,并将操作交还系统执行其他任务实现非阻塞延时
}
}
在主函数void app_main(void)里创建任务xTaskCreate用于调用任务函数: xTaskCreate: 创建函数,参数依次为 函数指针 , 函数名称 , 分配的内存 , 传递的参数 , 优先级别和句柄,主题函数为无限循环.
void app_main(void)
{
xTaskCreate(MyTask,"mytask1",1024,NULL,1,NULL);//创建任务函数
//1:pxtaskcode任务函数
//2:PcName任务名字
//3:usStackDepth任务堆栈
//4:pyParameters任务传入参数
//5:usPriorit任务优先级,最低优先级为0=空闲任务,可以设置0-31
//6:pxCreattedTask任务句柄任务创建成功后会返回这个句柄,其他api任务接口可调用这个句柄
}
xTaskDelete: (句柄)删除函数,也可以在主体函数中用xTaskDelete(NULL)删除函数
void app_main(void)
{
TaskHandle_t MyHandle=NULL;//创造一个TaskHandle_t类型的变量;
xTaskCreate(myTask,"mytask1",1024,NULL,1,&MyHandle);//创建任务函数
//1:pxtaskcode任务函数
//2:PcName任务名字
//3:usStackDepth任务堆栈
//4:pyParameters任务传入参数
//5:usPriorit任务优先级,最低优先级为0=空闲任务,可以设置0-31
//6:pxCreattedTask任务句柄任务创建成功后会返回这个句柄,其他api任务接口可调用这个句柄
if(MyHandle!=NULL)//判断任务创建函数是否执行
vTaskDelete(MyHandle);//删除任务
}
但是如果任务创建完立马执行删除任务,会导致创建的任务还没来得及执行就被删除了,及没有执行或者没有执行完毕,所以要在vTaskDelete()函数上加延时
portTICK_PERIOD_MS是在FreeRTOS.h里定义的系统时钟一般为1;
vTaskDelay()以毫秒为单位执行的
void app_main(void)
{
TaskHandle_t MyHandle=NULL;//创造一个TaskHandle_t类型的变量;
xTaskCreate(MyTask,"Mytask1",1024,NULL,1,&MyHandle);//创建任务函数
//1:pxtaskcode任务函数
//2:PcName任务名字
//3:usStackDepth任务堆栈
//4:pyParameters任务传入参数
//5:usPriorit任务优先级,最低优先级为0=空闲任务,可以设置0-31
//6:pxCreattedTask任务句柄任务创建成功后会返回这个句柄,其他api任务接口可调用这个句柄
vTaskDelay(1000/portTICK_PERIOD_MS);//添加延时保证任务执行完才删除任务
if(MyHandle!=NULL)//判读任务创建函数是否执行
vTaskDelete(MyHandle);//删除任务
}
将延时时间设置为8000及8000ms=8s可以使任务执行8次
void app_main(void)
{
TaskHandle_t MyHandle=NULL;//创造一个TaskHandle_t类型的变量;
xTaskCreate(myTask,"Mytask1",1024,NULL,1,&MyHandle);//创建任务函数
//1:pxtaskcode任务函数
//2:PcName任务名字
//3:usStackDepth任务堆栈
//4:pyParameters任务传入参数
//5:usPriorit任务优先级,最低优先级为0=空闲任务,可以设置0-31
//6:pxCreattedTask任务句柄任务创建成功后会返回这个句柄,其他api任务接口可调用这个句柄
vTaskDelay(8000/portTICK_PERIOD_MS);//设置为8s延时,上面的任务每个为1s即以执行8次任务函数
if(MyHandle!=NULL)//判读任务创建函数是否执行
vTaskDelete(MyHandle);//删除任务
}
或者可以使用第二种任务删除的方法:
在创建任务内容的函数里添加vTaskDelete函数可以删除当前task任务:
创建任务的函数句柄不需要设置值,可直接设置为NULL.
void MyTask(void* param)
{
// while(1)//不需要循环因为执行完需要删除任务
{
printf("Hello World !");
vTaskDelay(1000/portTICK_PERIOD_MS);
}
vTaskDelete(NULL);//删除当前任务
}
void app_main(void)
{
xTaskCreate(MyTask,"mytask1",1024,NULL,1,NULL);//创建任务函数
}
二. 传入数据
Task Input Parameter输入参数:4种 分别为 整数,数组,结构体,字符串
任务函数xTaskCreate传入的第4个参数pvParameters是void类型指针即可以接收任何类型的参数
想要将参数传入任务中需要定义一个变量TaskNum;将变量转换为void指针变量.再传入xTaskCreate函数中;
void MyTask(void* param)
{
int*Pint;//创建一个int类型的指针
Pint=(int*)param;//将传入函数的参数赋值给Pint
printf("%d \n",*Pint);//打印Pint指针的内容用于验证参数是否传入任务函数中
vTaskDelay(1000/portTICK_PERIOD_MS);
vTaskDelete(NULL);//删除当前任务
}
int TestNum=1;//创建一个int类型变量用于传入任务函数;
void app_main(void)
{
xTaskCreate(MyTask,"mytask1",1024,(void*)&TestNum,1,NULL);
//创建任务函数 并将TestNum参数传入函数中
}
如果显示stack overflow即栈溢出可以将任务分配的内存调整为2048,如果没有输出可能是printf里面没加\n,没加\n是不会输出东西的.
传入数组 注意传入数组则参数不需要加&取地址符,因为数组将首地址传入函数的
void MyTask(void* param)
{
int* pArryAddr;//创建一个int类型的指针
pArryAddr=(int*)param;//将传入函数的参数赋值给 pArryAddr
printf("%d \n",*pArryAddr);//打印Pint指针的内容用于验证参数是否传入任务函数中
printf("%d \n",*(pArryAddr+1));//打印Pint指针的内容用于验证参数是否传入任务函数中
printf("%d \n",*(pArryAddr+2));//打印Pint指针的内容用于验证参数是否传入任务函数中
vTaskDelay(1000/portTICK_PERIOD_MS);
vTaskDelete(NULL);//删除当前任务
}
int TestNum[]={1,2,3};//创建一个int类型数组用于传入任务函数;
void app_main(void)
{
xTaskCreate(MyTask,"mytask1",2024,(void*)TestNum,1,NULL);
//创建任务函数 并将TestNum参数传入函数中
}
传入结构体
typedef struct A_STRUCT
{
int inum1;
int inum2;
}xStruct;
xStruct xStrtest={1,2};//创建结构体
void MyTask(void* param)
{
xStruct *pStrtest;//创建一个xStruct类型的结构体指针
pStrtest=(xStruct*)param;//将传入函数的参数赋值给Pint
printf("%d \n",pStrtest->inum1);//打印pStrtest指针的内容用于验证参数是否传入任务函数中
printf("%d \n",pStrtest->inum2);//打印pStrtest指针的内容用于验证参数是否传入任务函数中
vTaskDelay(1000/portTICK_PERIOD_MS);
vTaskDelete(NULL);//删除当前任务
}
void app_main(void)
{
xTaskCreate(MyTask,"mytask1",2048,(void*)&xStrtest,1,NULL);//创建任务函数并传入结构体
}
传入字符串: 注意字符串本身就是地址所以传入参数是也是不需要取地址的:
static const char *TastTxt="Hello World !"//创建字符串指针
void MyTask(void* param)
{
char *pcTxtmytask=(char *)param;//创建一个char类型的指针
printf("%s \n",pcTxtmytask);//打印pStrtest指针的内容用于验证参数是否传入任务函数中
vTaskDelay(1000/portTICK_PERIOD_MS);
vTaskDelete(NULL);//删除当前任务
}
void app_main(void)
{
xTaskCreate(MyTask,"mytask1",2048,(void*)pcTxtmytask,1,NULL);//创建任务函数并传入结构体
}
三. 任务优先级
任务优先级在FreeRTOSConfig.h里由 configMAX_PRIORITIES定义.
如果设置的优先级大于 configMAX_PRIORITIES-1会默认用 configMAX_PRIORITIES-1代替.
不建议修改FreeRTOSConfig.h里由 configMAX_PRIORITIES里的值,因为会重新编译文件,占用更多内存.
uxTaskPriorityGet()函数可以获取任务优先级用法如下:
void app_main(void)
{
UBaseType_t iPriority=0;//定义一个UBaseType_t类型的变量用于存放任务优先级
TaskHandle_t pxtask=NULL;//定义一个任务句柄
xTaskCreate(MyTask,"mytask1",2048,NULL,1,&pxtask);//创建任务函数并传入结构体
iPriority=uxTaskPriorityGet(pxtask);//读取任务函数的优先级赋值给iPriority
printf("%d\n",iPriority);
}
同样优先级的任务会以时间片的形似同时执行任务.谁先创建谁先运行.
如果打印结果是1,2,2,1是因为ESP32双核运行导致的。在SDK配置编辑器(menuconfig)中勾选 "Run FreeRTOS only on first core" 就好了。
void MyTask1(void* param)
{
while(1)
{
printf("1\n");
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
void MyTask2(void* param)
{
while(1)
{
printf("2\n");
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
void app_main(void)
{
xTaskCreate(MyTask1,"mytask1",2048,NULL,1,NULL);
xTaskCreate(MyTask2,"mytask2",2048,NULL,1,NULL);
}
结果
1
2
1
2
1
vTaskPrioritySet();可以设置任务的优先级
void MyTask1(void* param)
{
while(1)
{
printf("1\n");
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
void MyTask2(void* param)
{
while(1)
{
printf("2\n");
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
void app_main(void)
{
TaskHandle_t pxtask=NULL;//定义一个任务句柄
xTaskCreate(MyTask1,"mytask1",2048,NULL,2,NULL);
xTaskCreate(MyTask2,"mytask2",2048,NULL,1,&pxtask);
vTaskPrioritySet(pxtask,3);//可以更改或者设置任务优先级
}
结果
1
2
2
1
2
四. vTaskSuspend():任务挂起
void MyTask1(void* param)
{
while(1)
{
printf("1\n");
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
void MyTask2(void* param)
{
while(1)
{
printf("2\n");
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
void app_main(void)
{
TaskHandle_t pxtask=NULL;//定义一个任务句柄
xTaskCreate(MyTask1,"mytask1",2048,NULL,1,NULL);
xTaskCreate(MyTask2,"mytask2",2048,NULL,1,&pxtask);
vTaskDelay(2000/portTICK_PERIOD_MS);
vTaskSuspend(pxtask);
}
结果
1
2
1
2
1
1
1
1
vTaskResume():任务恢复
void MyTask1(void* param)
{
while(1)
{
printf("1\n");
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
void MyTask2(void* param)
{
while(1)
{
printf("2\n");
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
void app_main(void)
{
TaskHandle_t pxtask=NULL;//定义一个任务句柄
xTaskCreate(MyTask1,"mytask1",2048,NULL,1,NULL);
xTaskCreate(MyTask2,"mytask2",2048,NULL,1,&pxtask);
vTaskSuspend(pxtask);//任务挂起
vTaskDelay(2000/portTICK_PERIOD_MS);
vTaskResume(pxtask);//任务恢复
}
结果
1
1
1
2
vTaskSuspendAll()挂起所有任务
vTaskResumeAll()恢复所有任务
如果在一段代码中不希望这段代码被其他任务切换就可以调用上面的函数,但是在这段代码中就不能再调用其他FreeRtos函数了即挂起后不可以执行FreeRTOS自身的函数
五. 任务列表vTaskList():
首先需要在系统设置menuconfig中设置或者在FreeRTOSconfig.h中将以下参数设置为1.
然后创建建一个char类型的数组用于存放列表信息再调用vTaskList(数组)函数.
void MyTask1(void* param)
{
while(1)
{
printf("1\n");
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
void MyTask2(void* param)
{
while(1)
{
printf("2\n");
vTaskDelay(1000/portTICK_PERIOD_MS);
}
}
void app_main(