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

news2024/11/28 20:58:12

一、任务创建和删除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);   //设置时钟,180Mhz
	delay_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);   //设置时钟,180Mhz
	delay_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.coloradmin.cn/o/1174847.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

国内外一级市场TOP10股权投资研究报告

前言 在金融领域,令人心跳加速的时刻往往来自于那些领先群雄的成就,无论是在科技创新、生产效率还是投资回报上。想象一下,如果财富的累积只是微不足道的,那又何异于日复一日的朝九晚五呢?随着时间的推移,…

【C++】详解IO流(输入输出流+文件流+字符串流)

文章目录 一、标准输入输出流1.1提取符>>&#xff08;赋值给&#xff09;与插入符<<&#xff08;输出到&#xff09;理解cin >> a理解ifstream&#xff08;读&#xff09; >> a例子 1.2get系列函数get与getline函数细小但又重要的区别 1.3获取状态信息…

数据包端到端的流程

流程 A给F发送一个数据包的流程&#xff1a; 首先 A&#xff08;192.168.0.1&#xff09;通过子网掩码&#xff08;255.255.255.0&#xff09;计算出自己与 F&#xff08;192.168.2.2&#xff09;并不在同一个子网内&#xff0c;于是决定发送给默认网关&#xff08;192.168.0.…

307. 区域和检索 - 数组可修改

给你一个数组 nums &#xff0c;请你完成两类查询。 其中一类查询要求 更新 数组 nums 下标对应的值 另一类查询要求返回数组 nums 中索引 left 和索引 right 之间&#xff08; 包含 &#xff09;的nums元素的 和 &#xff0c;其中 left < right 实现 NumArray 类&#xff…

数据分析与数据挖掘期末复习,附例题及答案

文章目录 一、概述1.数据挖掘能做什么&#xff1f;2.数据挖掘在哪些方面有应用&#xff1f;3.数据挖掘与数据分析的区别&#xff1f;4.数据挖掘的四大类模型5.什么是数据挖掘&#xff1f;6.数据挖掘的常用方法&#xff1f; 二、数据1.余弦相似度、欧几里得距离2.近似中位数 三、…

刚入职因为粗心大意,把事情办砸了,十分后悔

刚入职&#xff0c;就踩大坑&#xff0c;相信有很多朋友有我类似的经历。 5年前&#xff0c;我入职一家在线教育公司&#xff0c;新的公司福利非常好&#xff0c;各种零食随便吃&#xff0c;据说还能正点下班&#xff0c;一切都超出我的期望&#xff0c;“可算让我找着神仙公司…

数据结构与算法【02】—线性表

CSDN系列专栏&#xff1a;数据结构与算法专栏 针对以前写的数据结构与算法系列重写(针对文字描述、图片、错误修复)&#xff0c;改动会比较大&#xff0c;一直到更新完为止 前言 通过前面数据结构与算法基础知识我们知道了数据结构的一些概念和重要性&#xff0c;那么本章总结…

新技术前沿-2023-应用GPT提问模板写技术文章

参考一份万能的GPT提问模版&#xff01;直接套用&#xff01; 参考用GPT写技术文章是真爽&#xff01; 参考码住这篇 8200 字 ChatGPT 实战指南&#xff01;&#xff01; 1 GPT提问模板 想让GPT回答的内容符合我们所希望的&#xff0c;最最重要的一点就在于我们如何提问。提问…

NFS服务以及静态路由及临时IP配置

目录 一、NFC服务基础知识 1、NFS服务初相识 2、NFS服务工作原理 二、NFC服务基础操作 1、NFS服务端配置 2、NFS服务 - exports 相关参数 3、NFS服务 - 命令相关 三、RPC 远程调度 四、静态路由及临时IP配置 1、Linux 静态路由相关命令 2、Linux 临时IP地址添加与删除…

【漏洞复现】Nginx_0.7.65_空字节漏洞

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 1.1、漏洞描述 1.2、漏洞等级 1.3、影响版本 0.7.65 1.4、漏洞复现 1、基础环…

Redis那些事儿(三)

文章目录 1. 前言2. 常用api介绍3. 需求假设&#xff08;获取离我最近的停车场&#xff09;4. 代码示例 1. 前言 接着上一篇Redis那些事儿&#xff08;二&#xff09; &#xff0c;这一篇主要介绍Redis基于Geo数据结构实现的地理服务&#xff0c;它提供了一种方便的方式来存储和…

linux 创建git项目并提交到gitee(保姆式教程)

01、git安装与初始化设置 mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ apt install mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ git config --global user.name "用户名" mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ git config --global user.ema…

Instant-NGP论文笔记

文章目录 论文笔记 论文笔记 instant-ngp的nerf模型与vanilla nerf的模型架构相同。 instant-ngp的nerf模型包含两个MLP&#xff0c;第一个MLP就两个全连接&#xff0c;输入维度是32&#xff08;16层分辨率x2&#xff09;&#xff0c;输出是16&#xff08;用于预测密度&#x…

SpringBoot配置文件优先级

1.idea临时属性 说明&#xff1a;Program arguments配置--server.port8082 --ab&#xff1b;意思是将端口改成了8082。这个优先级最高。 2.resource 说明&#xff1a;创建config文件里面的yml文件。 3.jar包同级&#xff08;yml&#xff09; 说明&#xff1a;创建一个yml文件…

机器学习中的关键组件

机器学习中的关键组件 数据 每个数据集由一个个样本组成&#xff0c;大多时候&#xff0c;它们遵循独立同分布。样本有时也叫作数据点或数据实例&#xff0c;通常每个样本由一组称为特征或协变量的属性组成。机器学习会根据这些属性进行预测&#xff0c;预测得到的称为标签或…

平面扫描(Plane-sweeping)深度体会

先看文章 三维重建之平面扫描算法&#xff08;Plane-sweeping&#xff09;_plane sweeping_小玄玄的博客-CSDN博客 Plane Sweeping | 平面扫描 - 知乎 (zhihu.com) 注意平面Dm,这是其中一个平面&#xff0c;平面上有一个M点&#xff0c;这个点也再物体上。所以会被摄像机看到…

Idea去掉显示的测试覆盖率

一.启东时 误点击了 快捷键调出 【Ctrl 】【Alt】【F6】

优雅的 Dockerfile 是怎样炼成的?

Docker 简介 目前&#xff0c;Docker 主要有两个形态&#xff1a;Docker Desktop 和 Docker Engine。 Docker Desktop 是专门针对个人使用而设计的&#xff0c;支持 Mac&#xff08;已支持arm架构的M系芯片&#xff09; 和 Windows 快速安装&#xff0c;具有直观的图形界面&a…

数据结构—字符串

文章目录 7.字符串(1).字符串及其ADT#1.基本概念#2.ADT (2).字符串的基本操作#1.求子串substr#2.插入字符串insert#3.其他操作 (3).字符串的模式匹配#1.简单匹配(Brute-Force方法)#2.KMP算法I.kmp_match()II.getNext() #3.还有更多 小结附录&#xff1a;我自己写的string 7.字符…

手搭手Ajax实现搜索地址自动补全功能

输入单词后&#xff0c;自动提示出要搜索的信息&#xff0c;点击某个内容后&#xff0c;自动补全至搜索框。 比如&#xff1a; 如何实现搜索自动补全功能 键盘事件&#xff1a;keyup按键弹起事件发送ajax请求&#xff0c;请求中提交用户输入的搜索内容,后端接收内容后&#x…