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 实现流程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/161341.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

卷积与反卷积

文章目录 1 卷积1.1 卷积后的输出维度 2 反卷积2.1 来源2.2 原理2.3 使用2.3.1 padding2.3.2 strideoutput_padding 2.3 意义 1 卷积 1.1 卷积后的输出维度 设输入维度为i x i,卷积核大小为k x k,padding为p,strides为s,则输出维…

项目实战:新增@RequestMapping和@GetMapping和@PostMapping三个注解

1、RequestMapping package com.csdn.mymvc.annotation; import java.lang.annotation.*; Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Inherited public interface RequestMapping {String value(); }2、PostMapping package com.csdn.mymvc.annotation; im…

NLP 模型中的偏差和公平性检测

一、说明 近年来,自然语言处理 (NLP) 模型广受欢迎,彻底改变了我们与文本数据交互和分析的方式。这些基于深度学习技术的模型在广泛的应用中表现出了卓越的能力,从聊天机器人和语言翻译到情感分析和文本生成。然而&…

JVM虚拟机:垃圾回收器组合参数设定

本文重点 前面的课程中我们介绍了目前JVM中主流的垃圾回收器,不同的回收器会有不同的组合,其中1.8版本的jdk中,默认使用的是Parallel ScavengeParallel Old,也就是说新生代使用Parallel Scavenge,然后老年代使用Paral…

Java Jar 包还不知道怎么反编译,赶紧看看这个 IDEA 插件!

前言 当我们使用 Java 开发时,经常会遇到一种情况:我们拿到了一个 JAR 文件,但是却没有源代码。这时候,我们就需要使用反编译工具来帮助我们还原出源代码。 反编译工具可以将编译后的 JAR 文件转换回可读的 Java 源代码。这样&a…

Go 如何实现并发

Go语言的并发机制是其强大和流行的一个关键特性之一。Go使用协程(goroutines)和通道(channels)来实现并发编程,这使得编写高效且可维护的并发代码变得相对容易。下面是Go的并发机制的详细介绍: 协程&#x…

【Qt之QtXlsx模块】安装及使用

1. 安装Perl,编译QtXlsx源码用 可以通过命令行进行查看是否已安装Perl。 下载及安装传送门:链接: https://blog.csdn.net/MrHHHHHH/article/details/134233707?spm1001.2014.3001.5502 1.1 未安装 命令:perl --version 显示以上是未安装…

阿里云安全恶意程序检测

阿里云安全恶意程序检测 赛题理解赛题介绍赛题说明数据说明评测指标 赛题分析数据特征解题思路 数据探索数据特征类型数据分布箱型图 变量取值分布缺失值异常值分析训练集的tid特征标签分布测试集数据探索同上 数据集联合分析file_id分析API分析 特征工程与基线模型构造特征与特…

CVE-2023-34040 Kafka 反序列化RCE

漏洞描述 Spring Kafka 是 Spring Framework 生态系统中的一个模块,用于简化在 Spring 应用程序中集成 Apache Kafka 的过程,记录 (record) 指 Kafka 消息中的一条记录。 受影响版本中默认未对记录配置 ErrorHandlingDeserializer,当用户将容…

Pycharm 对容器中的 Python 程序断点远程调试

pycharm如何连接远程服务器的docker容器有两种方法: 第一种:pycharm通过ssh连接已在运行中的docker容器 第二种:pycharm连接docker镜像,pycharm运行代码再自动创建容器 本文是第一种方法的教程,第二种请点击以上的链接…

Docker与微服务实战——基础篇

Docker与微服务实战——基础篇 第一章 Docker 简介1.1 docker 理念1.2 容器与虚拟机比较 第二章 Docker 安装2.1 前提说明2.2 Docker的基本组成2.2.1 镜像(image)2.2.2 容器(container)2.2.3 仓库(repository&#xff…

看!MySQL 8.2 数据库支持读写分离啦!

更多文章,欢迎关注作者公众号,欢迎一起交流。 MySQL 8.2.0创新版本已于2023-10-17发布,MySQL Router 8.2 支持数据库的读/写分离,这里将在InnoDB Cluster集群中演示数如何进行读写分离,本篇内容包括:MySQL …