当前位置: 首页 > news >正文

FreeRTOS笔记【一】 任务的创建(动态方法和静态方法)

一、任务创建和删除API函数

函数描述
xTaskCreate()使用动态的方法创建一个任务
xTaskCreateStatic()使用静态的方法创建一个任务
xTaskCreateRestricted()创建一个使用MPU进行限制的任务,相关内存使用动态内存分配
vTaskDelete()删除一个任务

二、动态创建任务

2.1 宏定义

使用 xTaskCreate()  函数是在 FreeRTOS 中创建任务的一种方法,使用该函数所需的 RAM 会自动从 FreeRTOS的堆中自动分配。因此需要开启 FreeRTOSConfig.h 中的一个宏定义为1,就可以创建任务了。

#define configSUPPORT_DYNAMIC_ALLOCATION        1     //支持动态内存申请

2.2 函数原型

xTaskCreate() 函数原型如下:

BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,const char * const pcName,const uint16_t usStackDepth,void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pxCreatedTask )
参数描述
pxTaskCode任务函数
pcName任务名字,一般用于追踪和调试,任务名字长度不能超过configMAX_TASK_NAME_LEN
usStackDepth任务堆栈大小,注意实际申请到的堆栈是usStackDepth的4倍。其中空闲任务的任务堆栈大小为configMINIMAL_STACK_SIZE
pvParameters传递给任务函数的参数
uxPriotiry任务优先级,范围0~ configMAX_PRIORITIES-1
pxCreatedTask任务句柄,任务创建成功以后会返回此任务的任务句柄,这个句柄其实就是任务的任务堆栈。此参数就用来保存这个任务句柄。其他API函数可能会使用到这个句柄。
返回值描述
pdPASS任务创建成功。
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY任务创建失败,因为堆内存不足! 

2.3 示例代码

TaskHandle_t Task1_Handler;         //任务句柄
void task_1(void *pvParameters);    //任务函数TaskHandle_t Task2_Handler;         //任务句柄
void task_2(void *pvParameters);    //任务函数int main(void)
{HAL_Init();                     //初始化HAL库   Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhzdelay_init(180);                //初始化延时函数LED_Init();                     //初始化LED KEY_Init();uart_init(115200);              //初始化串口//创建task1任务xTaskCreate((TaskFunction_t )task_1,     	  //任务函数(const char*    )"task_1",   	  //任务名称(uint16_t       )50,              //任务堆栈大小(void*          )NULL,		      //传递给任务函数的参数(UBaseType_t    )2,	              //任务优先级(TaskHandle_t*  )&Task1_Handler); //任务句柄        //创建task2任务xTaskCreate((TaskFunction_t )task_2,     (const char*    )"task_2",   (uint16_t       )50, (void*          )NULL,(UBaseType_t    )2,(TaskHandle_t*  )&Task2_Handler);vTaskStartScheduler();          //开启任务调度
}//task_1任务函数 
void task_1(void *pvParameters)
{while(1)printf("1");
}   //task_2任务函数
void task_2(void *pvParameters)
{while(1)printf("2");
}

 效果:

 

对于 FreeRTOS 来说,同等优先级的任务会在时间片反复切换。在上述代码中,task1 和 task2 的优先级都是2,所以每一个时间片上就会切换一次任务,导致这样的效果。

  2.4 FreeRTOS 内部实现

在图上三个是我们需要编写的,下三步是 FreeRTOS 替我们完成的。

2.5 TCB 任务控制块

本篇暂时不解析其他的流程,我们来详解 TCP 任务控制块。他是一个结构体,可以看成是任务的身份证,每一个任务都有自己任务控制块,结构体成员保存了任务的特征,任务、优先级、任务状态等。

typedef struct tskTaskControlBlock             {// 这里栈顶指针必须位于TCB第一项是为了便于上下文切换操作,详见xPortPendSVHandler中任务切换的操作。volatile StackType_t    *pxTopOfStack;    // 表示任务状态,不同的状态会挂接在不同的状态链表下ListItem_t            xStateListItem;    // 事件链表项,会挂接到不同事件链表下ListItem_t            xEventListItem;        // 任务优先级,数值越大优先级越高UBaseType_t            uxPriority;            // 指向堆栈起始位置,这只是单纯的一个分配空间的地址,可以用来检测堆栈是否溢出StackType_t            *pxStack;            // 任务名char                pcTaskName[ configMAX_TASK_NAME_LEN ];//...省略一些条件编译的成员
} tskTCB;

三、静态创建任务函数

3.1 宏定义

使用函数 xTaskCreateStatic() 来创建任务,也就是静态方法,任务的堆栈、任务控制块就需要由用户来指定了。使用静态方法创建任务的时候需要将宏configSUPPORT_STATIC_ALLOCATION设置为1,在文件FreeRTOSConfig.h中设置。

#define configSUPPORT_STATIC_ALLOCATION 1 //静态内存 

3.2 函数原型

xTaskCreateStatic() 函数原型如下:

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任务函数
pcName任务名字,一般用于追踪和调试,任务名字长度不能超过
usStackDepth任务堆栈大小,由于本函数是静态方法创建任务,所以任务堆栈由用户给出,一般是个数组,此参数就是这个数组的大小
pvParameters传递给任务函数的参数
uxPriotiry任务优先级,范围0~ configMAX_PRIORITIES-1
puxStackBuffer任务堆栈,一般为数组,数组类型要为StackType_t类型
pxTaskBuffer任务控制块
返回值描述
NULL任务创建失败,puxStackBuffer或pxTaskBuffer为NULL的时候会导致这个错误的发生
其他值任务创建成功,返回任务的任务句柄。

对比 xTaskCreate() 来说, xTaskCreateStatic() 需要多自定添加 任务堆栈 (puxStackBuffer) 任务控制块 (pxTaskBuffer),也就是说在在静态任务中 TCB 结构体是需要自己定义的。而且在 xTaskCreateStatic() 中,任务句柄并不是作为参数填入,而是返回值。

3.3 定义两个接口函数

开启静态内存的同时需要实现两个函数:(使用静态内存分配任务堆栈和任务控制块内存)

vApplicationGetIdleTaskMemory():空闲任务堆栈函数。实现该函数是为了给内核提供空闲任务关于空闲任务控制块和空闲任务堆栈的相关信息。

vApplicationGetTimerTaskMemory():定时器任务堆栈函数。实现该函数是为了给内核创建定时器任务时提供定时器任务控制块和定时器任务堆栈的相关信息。

//获取空闲任务地任务堆栈和任务控制块内存,因为本例程使用的
//静态内存,因此空闲任务的任务堆栈和任务控制块的内存就应该
//有用户来提供,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;
}

3.4 示例代码

//空闲任务任务堆栈
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;
}TaskHandle_t Task1_Handler;         //任务句柄
void task_1(void *pvParameters);
StackType_t Task1TaskStack[128];    //任务堆栈
StaticTask_t Task1TaskTCB;          //任务控制块  TaskHandle_t Task2_Handler;         //任务句柄
void task_2(void *pvParameters);
StackType_t Task2TaskStack[128];    //任务堆栈
StaticTask_t Task2TaskTCB;          //任务控制块int main(void)
{HAL_Init();                     //初始化HAL库   Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhzdelay_init(180);                //初始化延时函数LED_Init();                     //初始化LED KEY_Init();uart_init(115200);              //初始化串口//创建开始任务taskENTER_CRITICAL();           //进入临界区//创建task1任务Task1_Handler = xTaskCreateStatic((TaskFunction_t )task_1,     	(const char*    )"task_1",   	(uint16_t       )50, (void*          )NULL,				(UBaseType_t    )2,	(StackType_t*   )Task1TaskStack,	(StaticTask_t*  )&Task1TaskTCB);   //创建task2任务Task2_Handler = xTaskCreateStatic((TaskFunction_t )task_2,     (const char*    )"task_2",   (uint16_t       )50, (void*          )NULL,(UBaseType_t    )2,(StackType_t*   )Task2TaskStack,	(StaticTask_t*  )&Task2TaskTCB);        taskEXIT_CRITICAL();            //退出临界区vTaskStartScheduler();          //开启任务调度
}//LED0任务函数 
void task_1(void *pvParameters)
{while(1){printf("1");}
}   //LED1任务函数
void task_2(void *pvParameters)
{while(1){printf("2");}
}

3.5 实现流程

相关文章:

  • JAVA整理学习实例(二)Object类
  • 正点原子嵌入式linux驱动开发——Linux WIFI驱动
  • 软考高级系统架构设计师系列之:软件可靠性基础
  • centos9 stream 下 rabbitmq高可用集群搭建及使用
  • oracle 基础语法总结
  • 【操作系统】进程调度
  • UE5C++学习(一)--- 增强输入系统
  • MYSQL:索引与锁表范围简述
  • Python 解压静态库 .a 和 .lib 文件
  • 【蓝桥杯】2023省赛H题
  • springboot前后端时间类型传输
  • Ansible的role
  • 0基础学习PyFlink——使用datagen生成流式数据
  • 【架构图解】API架构图解:如何以图表形式展现复杂系统
  • XPATH 注入漏洞
  • 30秒的PHP代码片段(1)数组 - Array
  • CSS魔法堂:Absolute Positioning就这个样
  • Docker入门(二) - Dockerfile
  • Fastjson的基本使用方法大全
  • js正则,这点儿就够用了
  • JS正则表达式精简教程(JavaScript RegExp 对象)
  • nginx 负载服务器优化
  • vue-router 实现分析
  • 工程优化暨babel升级小记
  • 计算机在识别图像时“看到”了什么?
  • 开源地图数据可视化库——mapnik
  • 力扣(LeetCode)357
  • 使用docker-compose进行多节点部署
  • 为什么要用IPython/Jupyter?
  • 再次简单明了总结flex布局,一看就懂...
  • 正则学习笔记
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • ​TypeScript都不会用,也敢说会前端?
  • ​创新驱动,边缘计算领袖:亚马逊云科技海外服务器服务再进化
  • #{}和${}的区别是什么 -- java面试
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #微信小程序:微信小程序常见的配置传值
  • $jQuery 重写Alert样式方法
  • (1)(1.19) TeraRanger One/EVO测距仪
  • (9)YOLO-Pose:使用对象关键点相似性损失增强多人姿态估计的增强版YOLO
  • (Java)【深基9.例1】选举学生会
  • (Ruby)Ubuntu12.04安装Rails环境
  • (补)B+树一些思想
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (数据结构)顺序表的定义
  • (转)淘淘商城系列——使用Spring来管理Redis单机版和集群版
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .Net Core/.Net6/.Net8 ,启动配置/Program.cs 配置
  • .NET delegate 委托 、 Event 事件,接口回调
  • .NET开源快速、强大、免费的电子表格组件
  • .net实现客户区延伸至至非客户区
  • .NET学习教程二——.net基础定义+VS常用设置
  • 。Net下Windows服务程序开发疑惑
  • @DataRedisTest测试redis从未如此丝滑
  • @require_PUTNameError: name ‘require_PUT‘ is not defined 解决方法