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

FreeRTOS(二)

一.FreeRTOS的目录结构

主要涉及2个目录:

Core: Inc目录下的FreeRTOSConfig.h是配置文件 ; Src目录下的freertos.c是STM32CubeMX创建的默认任务。

Middlewares\Third_Party\FreeRTOS\Source    根目录下是核心文件,这些文件是通用的。
核心文件:
移植FreeRTOS时涉及的文件放在 FreeRTOS/Source/portable/[compiler]/[architecture] 目录下,
比如:RVDS/ARM_CM3,这表示cortexM3架构在RVDSKeil工具上的移植文件。
头文件:

二.数据类型和编程规范

1.TickType_t:
FreeRTOS配置了一个周期性的时钟中断:Tick Interrupt,每发生一次中断,中断次数累加,这被称为tick count。tick count这个变量的类型就是 TickType_tTickType_t可以是16位的,也可以是32位的。 FreeRTOSConfig.h中定义USE_16_BIT_TICKS时,TickType_t就是uint16_t,否则TickType_t就是uint32_t。对于32位架构,建议把TickType_t配置为uint32_t。
2.BaseType_t:
这是该架构最高效的数据类型,32位架构中,它就是uint32_t16位架构中,它就是uint16_t8位架构中,它就是uint8_t。BaseType_t通常用作简单的返回值的类型,还有逻辑值, 比如pdTRUE/pdFALSE。
3.StackType_t:
用于表示任务栈元素类型的数据类型定义。它的具体定义取决于所使用的处理器架构和编译器,通常在portmacro.h 文件中定义,例如:在32位处理器上,通常定义为uint32_t (32 位无符号整数)。主要用途:用于声明任务栈数组,例如: StackType_t xTaskStack[1024];作为与任务栈操作相关的函数参数或返回值类型。

三.FreeRTOS任务管理

<1>任务的创建:

1.xTaskCreate(关注我的上一篇博客FreeRTOS(一)中有详解)使用xTaskCreate创建任务,则所需的RAM将自动从FreeRTOS堆中分配内存。
案例如下:
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include "usart.h"
#include "cmsis_os.h"TaskHandle_t mytask1handle;//任务一(mytask running....)
int fputc(int ch,FILE *f){HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,HAL_MAX_DELAY);return EOF;
}//任务执行函数
void task1_fun(void *argument){for(int i=0;i<5;i++){printf("mytask running....\n");vTaskDelay(1000);}	vTaskDelete(NULL);//删除当前任务
}//创建任务
void task1_init(){BaseType_t ret = xTaskCreate(task1_fun,"mytask1", 128, NULL,osPriorityNormal, &mytask1handle);if(ret != pdPASS){printf("创建任务失败");}else{printf("创建任务成功");}
}

2.xTaskCreateStatic

#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include "usart.h"
#include "cmsis_os.h"TaskHandle_t mytask2handle;//任务二(mytask static running....)
#define STACK_DEPTH 128
StackType_t  uxStackBuffer[STACK_DEPTH];
StaticTask_t xTaskTcb;//任务执行函数
void task2_fun(void *argument){for(;;){printf("mytask static running....\n");vTaskDelay(1000);}	}//创建任务
void task2_init(){mytask2handle = xTaskCreateStatic(task2_fun,"mytask2", STACK_DEPTH, NULL,osPriorityNormal,uxStackBuffer,&xTaskTcb);}

StackType_t  uxStackBuffer[STACK_DEPTH];StackType_t 用于声明任务栈数组

任务创建过程分析总结来说:1.分配任务控制块内存空间,分配任务堆栈空间。
2.初始化任务控制块,初始化任务堆栈。3.添加任务到就绪列表中。

<2>vTaskList是FreeRTOS中用于获取系统中所有任务状态信息的调试函数。

在task.c文件中定义。它可以将任务的名称、状态、优先级、堆栈剩余空间等信息输出到指定的缓冲区。

使用它之前在STM32CubeMX中的配置

示例:

#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include "usart.h"
#include "cmsis_os.h"TaskHandle_t mytask3handle;//任务三(打印任务状态)//任务执行函数
void task3_fun(void *argument){char buffer[256];for(;;){//打印所有的任务状态vTaskList(buffer);printf("Name\t\tState\tPri\tStack\tNum\n");printf("%s\r\n",buffer);vTaskDelay(1000);}	}//创建任务
void task3_init(){BaseType_t ret = xTaskCreate(task3_fun,"mytask3", 128, NULL,osPriorityNormal, &mytask3handle);if(ret != pdPASS){printf("创建任务失败");}else{printf("创建任务成功");}
}

结果如下:

<3>vTaskDelete:从RTOS内核管理中移除任务。

要删除的任务将从所有就绪、 阻塞、挂起和事件列表中移除。空闲任务负责释放由RTOS内核分配给已删除任务的内存。但是,任务代码分配的内存不会自动释放, 应在任务删除之前手动释放。

//任务执行函数
void taskX_fun(void *argument){for(;;){......}	vTaskDelete(NULL);//删除当前任务
}

要删除的任务的句柄。如果传递NULL,会删除调用任务。

删除任务的过程总结:vTaskDelete :负责将任务从运行状态中移除、标记为待删除,并加入等待清理链表。prvDeleteTCB :负责最终的资源释放(是内核内部的清理函数,由空闲任务调用)。两者共同构成了FreeRTOS的任务删除机制:前者处理 "逻辑删除",后者处理"物理销毁"

<4>任务的挂起和恢复

1.vTaskSuspend

​​​​​​​void vTaskSuspend( TaskHandle_t xTaskToSuspend );
功能: 挂起任意任务。无论任务优先级如何,任务被挂起后将永远无法获取任何微控制器处理时间。
参数: xTaskToSuspend 挂起的任务句柄。传递空句柄将导致调用任务被挂起。
2.vTaskResume
void vTaskResume( TaskHandle_t xTaskToResume );
功能:恢复已挂起的任务。
参数: xTaskToSuspend 待恢复任务的句柄。
eg.
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include "usart.h"
#include "cmsis_os.h"TaskHandle_t mytask1handle;
TaskHandle_t mytask2handle;//任务一(mytask running....)
int fputc(int ch,FILE *f){HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,HAL_MAX_DELAY);return EOF;
}//任务执行函数
void task1_fun(void *argument){for(int i=0;i<5;i++){printf("mytask running....\n");vTaskDelay(1000);}	//唤醒任务二vTaskResume(mytask2handle);vTaskDelete(NULL);//删除当前任务
}//创建任务
void task1_init(){BaseType_t ret = xTaskCreate(task1_fun,"mytask1", 128, NULL,osPriorityNormal, &mytask1handle);if(ret != pdPASS){printf("创建任务失败");}else{printf("创建任务成功");}
}//任务二(mytask static running....)
#define STACK_DEPTH 128StackType_t  uxStackBuffer[STACK_DEPTH];
StaticTask_t xTaskTcb;//任务执行函数
void task2_fun(void *argument){//挂起调用任务vTaskSuspend(NULL);for(;;){printf("mytask static running....\n");vTaskDelay(1000);}	}//创建任务
void task2_init(){mytask2handle = xTaskCreateStatic(task2_fun,"mytask2", STACK_DEPTH, NULL,osPriorityNormal,uxStackBuffer,&xTaskTcb);}

结果:开始的时候由于任务二中调用了//挂起调用任务vTaskSuspend(NULL);所以只有任务一执行

 执行五次之后(5秒之后),在任务一中任务二被唤醒,任务一被删除。结果如下:

任务挂起业务流程: a.从就绪列表中删除 b.从事件列表中删除 c.添加任务到挂起列表中
d.开始任务调度
任务恢复业务流程: a.从挂起列表中删除 b.添加任务到就绪列表 c.开启任务调度

四.任务状态转换

创建任务时,任务状态由"未开始"变为"就绪"。调度器根据任务优先级选取该任务并将其状态变为"运行中"。任务等待某个事件(如信号量)时,任务状态变为"阻塞"。任务等待其他任务释放资源时,任务状态可能会转变为"挂起"。任务自己调用删除函数删除自己时,任务状态变为"已删除"

A.运行态

正在执行的任务,该任务就处于运行态,如果使用的是单核处理器的话那么不管在任何时刻永远都只有一个任务处于运行态。

B.就绪态
处于就绪态的任务是那些已经准备就绪(这些任务没有被阻塞或者挂起),可以运行的任务,但是处于就 绪态的任务还没有运行,因为有一个同优先级或者更高优先级的任务正在运行!
C.阻塞态
如果一个任务因延时(调用了函数 vTaskDelay())或等待外部事件发生,那么这个任务就处于阻塞态。 任务进入阻塞态会有一个超时时间,当超过这个超时时间任务就会退出阻塞态。
D.挂起态
任务进入挂起态以后也不能被调度器调用进入运行态,但是进入挂起态的任务没有超时时间。任务进入和退出挂起态通过调用函数 vTaskSuspend() 和 xTaskResume()

五.任务调度

调度器使用相关的调度算法来决定哪个任务在特定时间点获得CPU执行权。FreeRTOS中开启任务调度的函数是vTaskStartScheduler() ,但在CubeMX中被封装为osKernelStart()
1.抢占式调度:
高优先级抢占低优先级任务,系统永远执行最高优先级的任务。这是FreeRTOS的默认调度方式。
FreeRTOS对优先级的默认设置是数值越小任务优先级越低。
假设有三个任务A B C,优先级由小到大。刚开始任务TaskA在运行中,运行过程中由于Task2就绪,在抢占式调度器的作用下任务TaskB抢占TaskA的执行。 TaskB进入到运行态,TaskA由运行态进入到就绪态。任务TaskB在运行中,运行过程中由于TaskC就绪,在抢占式调度器的作用下任务TaskC抢占TaskB的执行。 TaskC进入到运行态,TaskB由运行态进入到就绪态。任务TaskC运行过程中调用了阻塞式API函数,比如vTaskDelay,任务TaskC被阻塞,在抢占式调度器的作用下查找到下一个要执行的最高优先级任务是TaskB,任务TaskB由就绪态进入到运行态。
2.时间片调度
主要针对优先级相同的任务,当多个任务的优先级相同时, 任务调度器会在每一次系统时钟节拍 到的时候切换任务。同等优先级任务轮流地享有相同的CPU时间(可设置), 叫时间片,在FreeRTOS中,一个时间片就等于SysTick中断周期(默认设置为1ms)。
在嵌入式RTOS中,最常用的的时间片调度算法就是Round-robin调度算法。这种调度算法可以用于抢占式的多任务中。另外 ,时间片调度适用于不要求任务实时响应的情况
实现Round-robin调度算法需要给同优先级的任务分配一个专门的列表,用于记录当前就绪的任务,并为每个任务分配一个时间片。

例如:创建4个同优先级任务Task1,Task2,Task3和Task4。每个任务分配的时间片大小是1个系统时钟节拍(默认设置为1ms)

先运行任务Task1,运行够1个系统时钟节拍后,通过时间片调度切换到任务Task2。任务Task2运行够1个系统时钟节拍后,通过时间片调度切换到任务Task3。

任务Task3在运行期间调用了阻塞式API函数,调用函数时,虽然1个系统时钟节拍的时间片大小还没有用完,此时依然会通过时间片调度切换到下一个任务Task4。(注意,没有用完的时间片不会再使用,下次任务Task3得到执行还是按照1个系统时钟节拍运行)。

任务Task4运行够1个系统时钟节拍后,通过时间片调度切换到任务Task1。

ps:抢占式调度和时间片轮转可以同时存在,当有高优先级任务就绪时,运行高优先级任务;当最高优先级的任务有好几个时,这几个任务可以以时间片轮转方式调度。当任务执行到阻塞操作(如等待事件、信号量、队列等)时,它会停止执行并进入阻塞状态。一旦任务阻塞,它将不再消耗CPU时间。此时,调度器会回收该任务剩余的时间片。当阻塞的任务等待的条件得到满足时(例如,它等待的事件或信号量被触发),它将变为就绪状态,并被放入相应的就绪列表。调度器在下一个合适的时机(例如,当前执行的任务完成或时间片用完)会重新调度该任务,它将从上次停止执行的地方继续执行。

通过这种方式,FreeRTOS确保了系统资源的有效利用,避免了因单个任务长时间占用CPU而造成的其他任务饥饿问题。这也体现了FreeRTOS的抢占式调度特性,即任何时刻,CPU总是由当前可运行的最高优先级任务占用。如果当前任务阻塞,调度器会立即抢占CPU,给其他就绪的任务一个运行的机会。
FREERTOS中找到Kernel setting,其中使能调度器USE_PREEMPTION。
设置为1时使用抢占式RTOS调度器,设置为0时使用协作式RTOS调度器。
时间片轮询调度的时间片固定为1个时钟节拍,可以在FreeRTOSConfig.h文件中设置宏定义configTICK_RATE_HZ,时间片的时间一到,就切换下一个同优先级的任务执行。TICK_RATE_HZ设为1000即定时器每秒产生1000次 “滴答”(tick),每秒确实会产生 1000 个滴答中断。每个滴答之间的间隔时间为 1 毫秒。这意味着定时器会以 1 毫秒为周期持续触发中断。

六.任务延时

1.vTaskDelay

如果执行任务A的过程中发生中断,那么任务A执行的周期就会变长,所以使用相对延时函数vTaskDelay(),不能周期的执行任务A。

从调用函数vTaskDelayUntil()开始,每隔固定周期,任务B的主体代码就会执行一次,即使任务B在执行过程中发生中断,也不会影响这个周期性,只是会缩短其他任务的执行时间,所以这个函数被称之为绝对延时函数,它可以用于周期性的执行任务B的主体代码。

总结来说,vTaskDelay适合于需要在某个时间点之后执行任务的场景,而vTaskDelayUntil适合于需要在固定时间间隔内周期性执行任务的场景。在使用这两个函数时,需要考虑任务的优先级、调度策略以及系统时钟的精度等因素,以确保系统的稳定运行和延时操作的准确性。
http://www.dtcms.com/a/560889.html

相关文章:

  • 开源AI智能名片链动2+1模式S2B2C商城小程序商业化路径优化研究
  • 中国优秀的企业网站做搜狐网站页面
  • 【效率工具】EXCEL批注提取工具
  • Python openpyxl 设置Excel单元格公式和工作簿合并
  • 作文生成器网站北京seo招聘信息
  • 常州网站设计制作贵美商城网站的首页怎么做代码
  • 新媒体矩阵系统全景解析:赋能企业数字化营销的智能引擎
  • 多目标优化问题在适应度计算中的支配矩阵
  • 从零开始的云原生之旅(九):云原生的核心优势:自动弹性伸缩实战
  • 【Swift】LeetCode 240.搜索二维矩阵 II
  • 矩阵(板子)
  • 防火墙的内容补充
  • C++类和对象(下):初始化列表
  • 建筑工程找活网站wordpress文章新窗口
  • 沭阳城乡建设局网站做外国网站百度搜到
  • java-接口适配器模式 jsk8 接口默认实现
  • program.cs文件详解
  • 深圳市企业网站seo做东西的网站有那些
  • 京东测开面经整理(日常实习)
  • 大文件上传
  • 做ppt找图片网站推广网发布的信息准确吗
  • Linux内核POSIX文件锁机制深度解析
  • 从“CPU 烧开水“到优雅暂停:Go 里 sync.Cond 的正确打开方式
  • 大模型系列——Excel数据治理新思路:引入智能体实现自动纠错【Python+Agent】
  • Pyppeteer 使用教程
  • React性能优化:useMemo vs useCallback
  • Onsemi展示了垂直GaN-on-GaN半导体
  • 专业机票网站建设禅城区建设局网站
  • Java 日志演进:一文读懂主流框架
  • 第3章 变量与数据类型