开始前我们需要具备一些数据结构的知识,只需要知道队列和链表就行了,栈这个结构在前面我们已经学习过了。

核心组件

链表

        链表是FreeRTOS的核心组件,也是核心的数据结构,其他组件都是基于链表实现的,也可以叫"列表"

        核心就是列表项列表

任务

        任务的基本概念和原理在之前的章节已经讲过了,这里只要看它的数据结构,也包含了基本结构和任务通知

        任务结构

       1. 任务基本
  • 任务栈

  • 任务优先级

  • 任务状态列表项:任务状态实现

  • 任务事件列表项:用于任务事件实现,如等待队列、信号量、通知值等

        2. 任务通知
  • 任务通知状态

  • 任务通知值

队列

        不是纯粹数据结构上的队列,包含了其他核心功能,信号量和互斥量是基于这个队列搭建的,所以用队列概括。

        队列结构

  • 发生等待列表:用于存放等待任务事件列表项

  • 接收等待列表:用于存放等待任务事件列表项

  • 队列消息数量

软件定时器

        基于任务和队列的基础上,开发的软件的定时器。

        软件定时器结构

  • 定时器列表项

  • 周期:执行时间

  • 自动重装标志:一次性定时器还是重装定时器

  • 回调函数

事件组

        对于事件驱动的实现,以及简化消息通知方式,相对于信号量来说,事件更加轻量和高效。

        事件组结构

  • 事件位

  • 等待事件位列表

协程

        当理解任务的运行和原理后,协程的概念也很好懂,但是在FreeRTOS中协程的使用有点麻烦,使用这里不多说


流缓冲

        是以队列数据结构为基础,相较于队列组件效率更高的一种数据处理方式,基于任务通知的基础。

        流缓冲结构

  • 等待接收任务

  • 等待发送任务

  • 缓冲区指针

  • 缓冲区长度

内存管理

        以heap4为例的内存堆管理模型,是常用的内存管理方式。

对于heap4内存管理来说有几个核心特征:

  1. 内存堆的管理方式:寻找足够大的内存空间进行分配

  2. 合并空闲块防止内存碎片

以上就是FreeRTOS的核心组件,了解这些基本的核心组件才可以很好来的理解FreeRTOS

初始化资源

        全局资源

        要实现OS的这些抽象概念,在FreeRTOS实现正常运行,需要一些全局变量来管理任务、队列以及保存当前任务资源,包含有:

  • 当前运行任务句柄(运行态)

  • 优先级就绪列表数组(优先级和就绪态任务)

  • 阻塞列表(阻塞态)

  • 挂起列表(挂起态)

  • 删除列表(用于删除任务释放资源)

        对应程序实现就是FreeRTOS源码中的:

PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL;                           //当前运行任务指针

/* Lists for ready and blocked tasks. --------------------*/
PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ] = {0};  //优先级就绪列表数组
PRIVILEGED_DATA static List_t xDelayedTaskList1 = {0};                          //阻塞列表1    
PRIVILEGED_DATA static List_t xDelayedTaskList2 = {0};                          //阻塞列表2    
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList = NULL;              //阻塞任务列表指针,用于切换阻塞列表和调用    
PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList = NULL;      //溢出阻塞列表指针,当tick溢出后,用于阻塞列表切换和调用
PRIVILEGED_DATA static List_t xPendingReadyList = {0};                          //这个不用太在意                        

#if( INCLUDE_vTaskDelete == 1 )

    PRIVILEGED_DATA static List_t xTasksWaitingTermination = {0};               //任务删除列表
    PRIVILEGED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U;

#endif

#if ( INCLUDE_vTaskSuspend == 1 )

    PRIVILEGED_DATA static List_t xSuspendedTaskList = {0};                     //任务挂起列表

#endif
  • 队列注册表(管理队列)

对应程序实现就是FreeRTOS源码中的:

PRIVILEGED_DATA QueueRegistryItem_t xQueueRegistry[ configQUEUE_REGISTRY_SIZE ]; //全局注册队列组

        运行资源

        假设我们现在用CubeMX直接生成的代码运行,不创建自己的任务,系统运行会是什么样的?

        一般来说,系统会创建空闲任务和软件定时器任务来作为系统的初始化运行资源。

        空闲任务

  • 创建空闲任务     

  • 将任务加入优先级就绪列表中

        定时器任务

  • 创建软件定时器任务用于处理定时器逻辑

  • 创建软件定时器队列由于接收定时器消息

系统运行

        配合我们之前提到的中断任务切换工具SystemTickHandlerPendSVSVC

通过全局资源及运行资源和中断工具,可以构成一个系统正常运行的雏形:

        在这个初始化系统中运行过程也是很简单的,我们可以提取一些关键信息

  • 空闲任务与软件定时器任务

  • 2个任务都有自己的优先级。

  • 软件定时器任务高于空闲任务(空闲任务一般设定为最低优先级)

  • 定时器任务中包含队列

  • 当没有软件定时器存在或者定时器周期未到时,整个任务是阻塞的

        所以当系统稳定运行时,软件定时器任务处于阻塞态,空闲任务是处于就绪态。

        当创建某个软件定时器并且启动后,当软件定时器运行时,空闲任务切换为软件定时器任务,但是空闲任务依旧处于就绪态,只不过被高优先级任务抢占了。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐