FreeRTOS(一)FreeRTOS基础知识

目录

简单介绍

FreeRTOS基础知识

任务调度

抢占式调度:

举例说明:

时间片调度

举例说明

任务状态

FreeRTOS中4种任务状态

四种任务状态转换图

任务状态列表

任务实现

任务函数模板

任务控制块

TCB的结构

任务堆栈


今天开个新坑----FreeRTOS

简单介绍

FreeRTOS是一个轻量级的实时操作系统(RTOS),专为微控制器和小型处理器设计,广泛应用于嵌入式系统领域。它提供了任务管理、时间管理、互斥量、信号量等基本的实时操作系统功能,支持多任务处理,使得开发者能够在单一处理器上同时运行多个任务。

其核心特点:

  • 轻量级和高效:FreeRTOS占用的内存非常小,适合资源受限的嵌入式系统。
  • 跨平台支持:支持多种微控制器架构,包括ARM Cortex-M、AVR、PIC32等。
  • 可配置性:提供灵活的配置选项,允许开发者根据需求调整系统功能。
  • 丰富的API:提供了丰富的API函数,便于实现任务管理、同步机制、时间管理等功

FreeRTOS基础知识

任务调度

FreeRTOS的任务调度是其核心功能之一,它允许多个任务在单个处理器上并发执行。任务调度器控制哪个任务应当在何时运行,以及运行多长时间,从而实现对处理器时间的高效管理。

抢占式调度

在FreeRTOS中,每个任务都被分配一个优先级,优先级的数值越高,任务获得CPU时间的优先权就越大。(我们采用的是硬件优先级,就是从0-31,总共32位。在FreeRTOS中任务设置的数值越大,优先级越高)。

上下文切换:当操作系统决定将CPU从一个任务切换到另一个任务时,需要保存当前任务的状态(上下文)并加载新任务的状态,这个过程称为上下文切换。

中断:抢占通常是通过硬件中断实现的。当一个高优先级任务需要执行时,操作系统会接收到一个中断信号,触发当前任务的抢占。

举例说明:

假设有一个简化的操作系统,其中运行着三个任务:任务A、任务B和任务C,它们的优先级依次降低。

  1. 开始时:任务A首先获得CPU,开始执行。
  2. 任务B到来:在任务A执行期间,更高优先级的任务B到达。操作系统接收到任务B的请求,并决定抢占任务A,将CPU分配给任务B。
    • 此时,系统会保存任务A的当前状态(如寄存器值、程序计数器等),这是上下文切换的一部分。
    • 接着,系统加载任务B的上下文,并开始执行任务B。
  3. 任务C到来:假设在任务B执行期间,任务C也到来了。但因为任务C的优先级低于任务B,所以任务B继续执行,不会被任务C抢占。
  4. 任务B完成:任务B执行完毕后,系统再次进行上下文切换,此时会检查等待执行的任务中优先级最高的任务。
    • 因为任务A的优先级高于任务C,所以系统会恢复任务A的上下文,并继续执行任务A。
  5. 任务A完成后:任务A完成后,系统再次进行上下文切换,开始执行剩余的任务C。

时间片调度

同等优先级的情况下,可以有多个任务,那么就有了时间片调度。在具有相同优先级的任务之间使用时间片调度(会在每一次系统时钟节拍到的时候切换任务)。时间片调度的目标是保证具有相同优先级的任务获得公平的处理器时间分配。

时间片的长度通常由系统的时钟滴答(Tick)率决定,一个时间片是滴答定时器中断周期(是可以设置的)。当一个进程的时间片用完时,系统会保存当前进程的状态,然后将CPU分配给下一个进程。

举例说明

假设有三个进程:进程A、进程B和进程C,它们几乎同时到达就绪队列,等待CPU执行。操作系统采用时间片调度策略,每个进程被分配一个时间片长度为10毫秒。

  1. 进程A开始执行:首先,进程A获得第一个时间片(10毫秒),开始执行。
  2. 进程A的时间片用完:10毫秒后,不管进程A是否完成,它的时间片已经用完。系统执行上下文切换,保存进程A的当前状态。
  3. 进程B获得CPU:接着,系统将CPU分配给进程B,进程B开始使用它的10毫秒时间片执行。
  4. 进程B的时间片用完:类似地,进程B的时间片用完后,系统再次进行上下文切换,保存进程B的状态。
  5. 进程C获得CPU:然后,进程C获得CPU,开始执行它的时间片。
  6. 循环继续:进程C的时间片用完后,如果所有进程仍需要CPU时间,系统会再次从进程A开始,继续这个循环过程。

补充:任务中没有用完的时间片不会再使用,下次执行的时候还是按照一个时间片的时钟节拍运行

任务状态

FreeRTOS中4种任务状态

运行态:正在执行的任务,该任务就处于运行态,注意在STM32中,同一时间仅一个任务处于运行态

就绪态:如果该任务已经能够被执行,但当前还未被执行,那么该任务处于就绪态

阻塞态:如果一个任务因延时或等待外部事件发生,那么这个任务就处于阻塞态

挂起态:类似暂停,调用函数 vTaskSuspend() 进入挂起态,需要调用解挂函数vTaskResume()

才可以进入就绪态

四种任务状态转换图

 只有就绪态可转变成运行态。

其他状态的任务想运行,必须先转变成就绪态。

任务状态列表

就绪列表(Ready List)

就绪列表包含所有准备好执行但正在等待CPU分配的进程。这些进程已经获得了除CPU之外的所有必要资源,并且随时可以开始执行。

调度器总是在所有处于就绪列表的任务中,选择具有最高优先级的任务来执行

假设有三个进程A、B和C,它们都已完成初始化并准备执行。如果只有一个CPU可用,那么在任何给定时刻,只有一个进程可以执行,而其他进程则被放入就绪列表中,等待调度程序分配CPU资源。

阻塞列表(Blocked List 或 Waiting List)

阻塞列表包含那些因为某种原因(如等待I/O操作完成、等待信号量或其他同步事件)而暂停执行的进程。处于阻塞状态的进程不能执行,直到其等待的事件发生。

进程D发出了一个读取硬盘的I/O请求。由于I/O操作需要时间完成,进程D将被移入阻塞列表,并处于等待状态,直到数据读取完成。在这段时间里,CPU可以被分配给就绪列表中的其他进程。

挂起列表(Suspended List)

挂起列表包含那些被暂时挂起(冻结)的进程。挂起的原因可能是系统资源不足、用户请求、或进程正在被调试。挂起的进程既不会执行,也不会被调度,直到它们被重新激活。

假设系统内存资源紧张,操作系统可能决定挂起一些低优先级或当前不活跃的进程,将它们移入挂起列表,并释放它们占用的内存资源。当资源变得可用时,这些进程可以被重新激活,移回就绪列表,等待执行。

任务实现

在FreeRTOS中,任务是最小的执行单元,每个任务都执行一个特定的功能。任务实现就是指如何编写和配置这些任务

在使用FreeRTOS 的过程中,我们要使用函数xTaskCreate()或 xTaskCreateStatic()来创建任 务,这两个函数的第一个参数pxTaskCode,就是这个任务的任务函数。

任务 函数就是完成本任务工作的函数。我这个任务要干嘛?要做什么?要完成什么样的功能都是在 这个任务函数中实现的。

任务函数模板

void vATaskFunction(void *pvParameters)      
{ 
 for( ; ; )            
{ 
 /*--任务应用程序--*/         
 vTaskDelay();         
} 
/*不能从任务函数中返回或者退出,从任务函数中返回或退出的话就会调用
configASSERT(),前提是你定义了configASSERT()。如果一定要从任务函数中退出的话那一定               
要调用函数vTaskDelete(NULL)来删除此任务。*/ 
 
vTaskDelete(NULL);         
 
} 

这个函数接受一个void*类型的参数,这个参数可以用来传递初始化数据给任务。

任务函数的结构

任务函数通常包含以下几个部分:

  1. 无限循环:任务函数包含一个无限循环,这意味着任务一旦启动,就会一直运行,直到被删除或者系统关闭。(while(1)要进行一次判断,for(;;)直接进入循环)
  2. 任务代码:在循环中,你可以编写任务需要执行的具体代码。这部分代码定义了任务的行为和功能。
  3. 延时:通常在任务的末尾调用vTaskDelay()来让出CPU,允许其他任务运行。这是实现任务间协作和系统响应性的关键。(FreeRTOS的延时函数,此处不一定要用延时函数,其他只要能让FreeRTOS发生任务 切换的API函数都可以,比如请求信号量、队列等,甚至直接调用任务调度器。只不过最常用 的就是FreeRTOS的延时函数。)

任务控制块

FreeRTOS的每个任务都有一些属性需要存储,FreeRTOS把这些属性集合到一起用一个结 构体来表示,这个结构体叫做任务控制块.

在FreeRTOS中,任务控制块(Task Control Block,简称TCB)是一个关键的数据结构,它存储了与任务相关的所有信息。每个任务都有一个对应的TCB,它在任务创建时被初始化,并在任务的整个生命周期中被用来管理和调度任务。

TCB的结构

typedef struct tskTaskControlBlock {
    volatile StackType_t *pxTopOfStack; // 堆栈顶指针
    #if ( portUSING_MPU_WRAPPERS == 1 )
        xMPU_SETTINGS xMPUSettings; // 内存保护单元设置
    #endif
    ListItem_t xStateListItem; // 状态列表项
    ListItem_t xEventListItem; // 事件列表项
    UBaseType_t uxPriority; // 任务优先级
    // 其他成员...
} tskTCB;
typedef tskTCB TCB_t;

TCB成员详解

  1. pxTopOfStack:指向任务堆栈的顶部。堆栈用于存储任务执行时的局部变量和CPU寄存器的状态。当任务被调度运行时,CPU的状态(如程序计数器、寄存器等)会被保存到堆栈中。

  2. xMPUSettings:这是一个条件编译选项,仅在FreeRTOS配置为使用内存保护单元(MPU)时才存在。它用于存储与任务相关的内存保护设置。

  3. xStateListItem:这是一个列表项,用于将任务插入到任务状态列表中。FreeRTOS使用这个列表来管理不同状态的任务,如就绪态、阻塞态等。

  4. xEventListItem:这是另一个列表项,用于将任务插入到事件列表中。当任务因为等待事件(如信号量、互斥锁等)而被阻塞时,它会通过这个列表项被添加到相应的事件列表中。

  5. uxPriority:表示任务的优先级。FreeRTOS是一个抢占式实时操作系统,任务的执行顺序由它们的优先级决定。数字越大,优先级越高。

TCB的作用

  • 任务调度:TCB中的信息被调度器用来决定哪个任务应该运行。调度器会根据任务的优先级和状态来选择下一个要执行的任务。

  • 任务切换:当任务切换发生时(例如,从当前任务切换到另一个任务),调度器会使用TCB中的信息来恢复新任务的状态,包括堆栈指针和寄存器状态。

  • 任务管理:操作系统通过TCB来管理任务的生命周期,包括任务的创建、删除、挂起和恢复。

任务堆栈

在FreeRTOS中,任务堆栈(Task Stack)是每个任务独有的内存区域,用于保存任务执行期间的局部变量、函数参数、返回地址以及CPU的寄存器状态。

任务调 度器在进行任务切换的时候会将当前任务的现场(CPU寄存器值等)保存在此任务的任务堆栈中, 等到此任务下次运行的时候就会先用堆栈中保存的值来恢复现场,恢复现场以后任务就会接着 从上次中断的地方开始运行。

创建任务的时候需要给任务指定堆栈,如果使用的函数xTaskCreate()创建任务(动态方法) 的话那么任务堆栈就会由函数xTaskCreate()自动创建,后面分析xTaskCreate()的时候会讲解。 如果使用函数xTaskCreateStatic()创建任务(静态方法)的话就需要程序员自行定义任务堆栈,然 后堆栈首地址作为函数的参数puxStackBuffer传递给函数,如下

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  )  

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值