FreeRTOS基础学习

  • Post author:
  • Post category:其他



一、学习资源

1、正点原子免费教学视频:

原子哥,专注电子技术教学
3、PPT与源码资料:在”FreeRTOS资料”目录里。


二、FreeRTOS基本介绍


1、FreeRTOS简介

是一个

免费的

嵌入式

实时操作

系统;属于

轻量级

小系统,核心代码9000行左右,包含在3个.c文件中;软件实现优先级数量不限,硬件优先级受CPU限制,

任务优先级值越大则越高

;可以创建不限数量的任务,但要考虑堆栈内存资源;支持

抢占式、协程式、时间片流转

等任务调度算法。
///

2、FreeRTOS基础知识


任务调度简介

调度器就是使用相关的调度算法来决定当前需要执行的哪个任务,FreeRTOS一共支持

三种任务调度方式


① 抢占式调度

:主要是针对

优先级不同

的任务,每个任务都有一个优先级,优先级高的任务可以抢占优先级低的任务。

② 时间片调度

:主要针对

优先级相同

的任务,当多个任务的优先级相同时,任务调度器会在

每一次

系统时钟节拍到的时候切换任务(

sysTick的中断时间可设置

)。

③ 协程式调度



当前执行任务

将会

一直运行

,同时高优先级的任务

不会抢占

低优先级任务(

FreeRTOS现在虽然还支持,但已经不再更新这种调度算法了

)。

任务状态


① 运行态

:正在执行的任务,该任务就处于运行态,

同一个时间仅有一个任务处于运行态


② 就绪态

:如果该任务已经

能够被执行

,但当前

还未被执行

,那么该任务处于就绪态;

③ 阻塞态

:如果一个任务因

延时或等待外部事件发生

,那么这个任务就处于阻塞态;

④ 挂起态



类似暂停

,调用函数

vTaskSuspend(

)进入

挂起

态,需要调用

vTaskResume()

函数来解挂进入

就绪

态;

任务状态转换图

除了运行态,其他的任务状态都有其


任务状态列表



① 就绪列表(哈希表)

:pxReadyTasksLists[x],其中x代表任务优先级数值,每个优先级对应32位变量的一个位(bit=1代表该优先级有就绪的任务);

② 阻塞列表

:pxDelayedTaskList;

③ 挂起列表

:xSuspendedTaskList;
///

3、FreeRTOS的下载

直接从官网上点击“下载”后,默认选择最新的版本即可。
///

4、FreeRTOS源码文件结构解析


FreeRTOS内核目录

:./FreeRTOS

① Demo

:FreeRTOS演示例程;(主要)

② License

:FreeRTOS相关许可;

③ Source

:FreeRTOS源码;(核心)

④ Test

:公用以及移植层测试代码;

内核源码的Demo文件夹

:./FreeRTOS/Demo
提供各种CPU架构和不同厂家SOC的移植例程。

内核源码的Source文件夹

:./FreeRTOS/Source

① include目录

:包含了FreeRTOS的头文件,必须添加;

② portable目录

:包含了FreeRTOS的移植文件,必须添加;

③ croutine.c文件

:协程调度相关,可以选择性使用;

④ event_groups.c文件

:事件触发相关,可以选择;

⑤ list.c文件

:列表实现相关文件,必须添加;

⑥ queue.c文件

:队列实现相关,必须添加;

⑦ stream_buffer.c文件

:流式缓冲区相关文件,可以选择;

⑧ tasks.c文件

:任务处理相关文件,必须添加;

⑨ timers.c文件

:软件定时器实现相关,可以选择;

软硬件桥梁portable目录

:./FreeRTOS/Source/portable

portable目录

提供的FreeRTOS与不同硬件平台的连接桥梁,

实现FreeRTOS可移植的关键



对于STM32这种ARM架构来讲,实际上使用到的是图片中的红圈内容


① Keil

:我们使用的开发平台,它还指向RVDS;

② RVDS

:不同内核芯片的移植文件;

③ MemMang

:内存管理文件;
///


三、FreeRTOS的移植


1、基于正点原子STM32F103开发平台

这里并没有说明FreeRTOS的原理,只是通过移植过程去

感性体验

了一下FreeRTOS系统。移植的具体过程可以参考正点原子提供的

《FreeRTOS开发指南》

第二章;

下面是本次移植过程的个人总结

① 需要在工程中添加 FreeRTOS的源码 以及 板级支持.c文件 ;


设备驱动

其实还是需要

自己编写实现

的,也可以利用HAL库函数;
③ 对于

sysTick中断

,需要添加FreeRTOS提供的的回调函数;因为FreeRTOS是通过sysTick来实现周期任务切换的;
④ FreeRTOS源码已经

实现了svc中断和PandSV中断

,所以必须

删除其他地方的实现

⑤ FreeRTOS其实

只是提供的多任务算法和内存管理算法

,其他都需要自己开发;
///

2、基于华大CPU的系统移植(


揉面机项目





过程讲解

① 需要移植FreeRTOS的源码文件到工程目录中,包括与内核相关的文件;


FreeRTOSConfig.h

文件中的

中断、堆内存、系统时钟频率、空闲任务栈空间以及任务优先级方式

等,需要根据MCU实际资源来配置;
③ 本次实验发现,需要

修改FreeRTOS源码

中 对systick的

配置宏portNVIC_SYSTICK_CLK_BIT_CONFIG

,可能因为内核M0和M3不一样吧(

选择时钟源不一样!!

);
④ 试验中发现,

只允许

使用

软件优先级

来管理任务,无法使用硬件优先级;



从任务调度上来说




软件优先级

不再依靠32位的变量bit位,而是

循环从高到低 检查 对应就绪列表是否为空,第一个不为空的就是目前最高优先级任务

;取出其中一个任务控制块作为新的任务,随后记录目前最高优先级;



对于新建任务来说


,每次添加

新的任务

都需要

和历史最高优先级做对比

⑦ 如果

.s文件

中有

复位向量表与中断控制器

的相关指令,

请屏蔽

///

3、基于STM32G0的移植(


端子机项目





过程讲解

① 根据应用需求的不同,这里的

硬件设备扫描任务优先级要 大于 应用逻辑

② 由于应用上使用了

TIM1发送脉冲中断

,且此中断需要保证即时响应,所以必须

脱离FreeRTOS的管理

///


四、FreeRTOS的系统配置文件


1、参考资料

② 正点原子提供:

《FreeRTOS开发指南》

第三章;

2、基本配置项的了解

使用FreeRTOS系统的工程,必须在工程中创建一个对应的配置文件:

FreeRTOSConfig.h

;该文件提供

各种全局宏

定义来

选择性编译FreeRTOS源码

以及

为工程提供系统相关参数

;配置文件中的 宏定义主要


分为三大类


:config、INCLUDE 和 其他配置项。
下面基于

正点原子提供的


STM32F103


案例中的配置文件

,其他芯片可以基于此案例来修改:

3、与FreeRTOS中断相关的配置项

在配置FreeRTOS的中断配置项之前,需要先知道使用的SOC的中断管理资源;以下是

FreeRTOS中断有关配置项的简介


① configPRIO_BITS

:此宏是用于辅助配置的宏,主要用于辅助配置宏configKERNEL_INTERRUPT_PRIORITY 和宏configMAX_SYSCALL_INTERRUPT_PRIORITY 的;此宏

应定义为


MCU





8


位优先级配置寄存器实际使用的位数

;因为STM32 只使用到了中断优先级配置寄存器的高4 位,因此,此宏应配置为4。

② configLIBRARY_LOWEST_INTERRUPT_PRIORITY

:此宏是用于辅助配置宏configKERNEL_INTERRUPT_PRIORITY 的,此宏

应设置为


MCU


的最低优先等级

;因为STM32 只使用了中断优先级配置寄存器的高4 位,因此MCU 的最低优先等级就是2^4-1=15,因此,此宏应配置为15。

③ configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY

:此宏是用于辅助配置宏configMAX_SYSCALL_INTERRUPT_PRIORITY 的;此宏

适用于配置


FreeRTOS


可管理的最高优先级的中断

,此功能就是

操作


BASEPRI


寄存器来实现

的;此宏的值可以根据用户的实际使用场景来决定。

④ configKERNEL_INTERRUPT_PRIORITY

:此宏应配置为MCU 的最低优先级在中断优先级配置寄存器中的值,




FreeRTOS


的源码中,使用此宏将


SysTick





PenSV


的中断优先级设置为最低优先级

;因为STM32 只使用了中断优先级配置寄存器的高4 位,因此,此宏应配置为最低中断优先级在中断优先级配置寄存器高4 位的表示,即(configLIBRARY_LOWEST_INTERRUPT_PRIORITY<<(8-configPRIO_BITS))。

⑤ configMAX_SYSCALL_INTERRUPT_PRIORITY

:此宏

用于配置


FreeRTOS


可管理的最高优先级的中断,在


FreeRTOS


的源码中,使用此宏来打开和关闭中断

;因为STM32 只使用了中断优先级配置寄存器的高4 位,因此,此宏应配置为(configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY<<(8-configPRIO_BITS))。

⑥ configMAX_API_CALL_INTERRUPT_PRIORITY



此宏为宏


configMAX_SYSCALL_INTERRUPT_PRIORITY


的新名称

,只被用在FreeRTOS 官方一些新的移植当中;此宏与宏configMAX_SYSCALL_INTERRUPT_PRIORITY 是等价的。


五、FreeRTOS任务基础知识


1、FreeRTOS 任务控制块

FreeRTOS中

每一个已创建任务

都包含

一个任务控制块



任务控制块是一个


结构体变量



,FreeRTOS用任务控制块结构体存储任务的属性(

typedef struct tskTaskControlBlock

)。FreeRTOS的任务控制块结构体中包含了很多成员变量,但是,大部分的

成员变量都是可以通过


FreeRTOSConfig.h


配置文件

中的配置项宏定义进行裁剪的。

2、FreeRTOS



创建和删除任务



相关API函数


1、动态方式创建任务

此函数用于使用动态的方式创建任务,任务的任务控制块以及任务的栈空间所需的内存,均由FreeRTOS从FreeRTOS管理的堆中分配,若使用此函数,

需要在


FreeRTOSConfig.h


文件中将宏


configSUPPORT_DYNAMIC_ALLOCATION


配置为


1

;此函数创建的任务会立刻进入就绪态,由任务调度器调度运行;

函数原型如下所示


函数返回值


2、静态方式创建任务

此函数用于使用静态的方式创建任务,任务的任务控制块以及任务的栈空间所需的内存,需要由用户分配提供,若使用此函数,

需要在


FreeRTOSConfig.h


文件中将宏


configSUPPORT_STATIC_ALLOCATION


配置为


1

;此函数创建的任务会立刻进入就绪态,由任务调度器调度运行。

函数原型如下所示


函数返回值


3、任务的删除

此函数用于删除已被创建的任务,被删除的任务将被从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除;

要注意的是,空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存,则需要由用户在任务被删除前提前释放

,否则将导致内存泄露;若使用此函数,

需要在


FreeRTOSConfig.h


文件中将宏


INCLUDE_vTaskDelete


配置为


1



函数原型如下所示


3、FreeRTOS



任务的挂起和恢复



相关API函数


1、挂起任务

此函数用于挂起任务,若使用此函数,

需要在


FreeRTOSConfig.h


文件中将宏


INCLUDE_vTaskSuspend


配置为


1

;无论优先级如何,被挂起的任务都将不再被执行,直到任务被恢复;此函数并不支持嵌套,不论使用此函数重复挂起任务多少次,只需调用一次恢复任务的函数,那么任务就不再被挂起。

函数原型如下所示


2、在任务中恢复被挂起的任务

此函数用于

在任务中恢复

被挂起的任务,若使用此函数,

需要在


FreeRTOSConfig.h


文件中将宏


INCLUDE_vTaskSuspend


配置为


1

;不论一个任务被函数vTaskSuspend()挂起多少次,只需要使用函数vTakResume()恢复一次,就可以继续运行。

函数原型如下所示


3、在中断中恢复被挂起的任务

此函数用于

在中断中恢复

被挂起的任务,若使用此函数,

需要在


FreeRTOSConfig.h


文件中将宏


INCLUDE_xTaskResumeFromISR


配置为


1



必须在FreeRTOS所管理的



中断优先级



中断中才可以恢复任务

;不论一个任务被函数vTaskSuspend()挂起多少次,只需要使用函数vTakResumeFromISR()恢复一次,就可以继续运行。

函数原型如下所示


4、运行任务的切换



中断中恢复任务

后,函数如果

返回pdTRUE

,代表恢复的任务优先级比较高,

需要手动切换执行

。这时候可以调用FreeRTOS提供的特殊函数来实现任务切换。

函数原型如下


void portYIELD_FROM_ISR(BaseType_t xYieldRequired)

;    //

实际上是一个宏定义函数,最终调用portYIELD()


六、FreeRTOS中断管理


1、ARM Cortex-M系列的中断

ARM Cortex-M的NVIC最大可

支持


256


个中断源,其中包括


16


个系统中断和


240


个外部中断

;然而

芯片厂商一般情况下都用不完这些资源

,以正点原子的战舰开发板为例,所使用的

STM32F103ZET6


芯片

就只用到了10个系统中断和60个外部中断。

中断优先级管理

在NVIC的相关结构体中,成员

变量


IP


用于配置外部中断

的优先级,成员变量IP的

定义如下所示

:ARM Cortex-M 使用了8 位宽的寄存器来配置中断的优先等级,这个寄存器就是中断优先级配置寄存器,因此

最大中断的优先级配置范围位


0~255

但是芯片厂商一般用不完这些资源,

对于


STM32

,只用到了中断优先级配置寄存器的高4 位[7:4],低四位[3:0]取零处理,因此STM32 提供了最大2^4=16 级的中断优先等级,

如下图所示



STM32

的中断优先级可以分为

抢占优先级和子优先级


三个 系统中断优先级 配置寄存器

除了外部中断,

系统中断有独立的中断优先级配置寄存器

,分别为

SHPR1





SHPR2





SHPR3

,下面就分别来看一下这三个寄存器的作用。

三个 中断屏蔽 寄存器

ARM Cortex-M有三个用于屏蔽中断的寄存器,分别为

PRIMASK





FAULTMASK





BASEPRI

,下面就分别来看一下这三个寄存器的作用。

PRIMASK

:PRIMASK寄存器有32bit,但只有bit0有效,是可读可写的,将PRIMASK寄存器设置为1用于

屏蔽除


NMI





HardFault




的所有异常和中断,将PRIMASK寄存器清0用于使能中断。

FAULTMASK

:FAULTMASK寄存器有32bit,但只有bit0有效,也是可读可写的,将FAULTMASK寄存器设置为1用于屏蔽




NMI


外的所

有异常和中断,将FAULTMASK寄存器清零用于使能中断。

BASEPRI

:BASEPRI有32bit,但只有




8





[7:0]


有效

,也是可读可写的;BASEPRI寄存器比起PRIMASK和FAULTMASK寄存器直接屏蔽掉大部分中断的方式,BASEPRI寄存器的功能显得更加细腻,BASEPRI

用于设置一个中断屏蔽的阈值

,设置好BASEPRI后,

中断优先级低于


BASEPRI


的中断就都会被屏蔽掉



FreeRTOS


就是使用


BASEPRI


寄存器来管理受


FreeRTOS


管理的中断的


中断控制状态寄存器

中断控制状态寄存器(

ICSR

)的地址为0xE000ED04,用于设置和清除异常的挂起状态,以及获取当前系统正在执行的异常编号,

各比特位的功能描述如下表所示


这个寄存器主要关注


VECTACTIVE





[8:0]


,通过读取


VECTACTIVE


段就能够判断当前执行的代码是否在中断中(



FreeRTOS的中断判断原理






2、FreeRTOS中断管理详解


PendSV和SysTick中断优先级的实现

FreeRTOS使用SHPR3寄存器配置PendSV和SysTick的中断优先级,那么FreeRTOS是如何配置的呢?




FreeRTOS


的源码中有如下定义

接着FreeRTOS

在启动任务调度器的函数

中设置了PendSV和SysTick的中断优先级,

代码如下所示


FreeRTOS中断的屏蔽与启用

FreeRTOS

使用


BASEPRI


寄存器

来管理受FreeRTOS管理的中断,而不受FreeRTOS管理的中断不受FreeRTOS开关中断的影响,那么FreeRTOS开关中断是如何操作的呢?首先来看一下

FreeRTOS


开关中断的宏定义,代码如下所示


屏蔽中断函数vPortRaiseBASEPRI()


解除屏蔽函数vPortSetBASEPRI(0)


FreeRTOS进出临界区

临界区是指那些必须完整运行的区域,在

临界区中的代码必须完整运行,不能被打断

;例如一些使用软件模拟的通信协议,通信协议在通信时,必须严格按照通信协议的时序进行,不能被打断。

FreeRTOS


在进出临界区的时候,通过关闭和打开受


FreeRTOS


管理的中断,以保护临界区中的代码

;FreeRTOS的源码中就包含了许多临界区的代码,这部分代码都是用临界区进行保护,用户在使用FreeRTOS编写应用程序的时候,也要注意一些不能被打断的操作,并为这部分代码加上临界区进行保护。
对于进出临界区,FreeRTOS的源码中有四个相关的宏定义,分别为

taskENTER_CRITICAL()





taskENTER_CRITICAL_FROM_ISR()





taskEXIT_CRITICAL()





taskEXIT_CRITICAL_FROM_ISR(x)

,这四个宏定义分别用于在中断和非中断中进出临界区,

定义代码如下所示

任务级的进出临界区允许嵌套,但

中断级的不允许嵌套

!!

任务调度器的挂起与恢复

这是是对整个任务调度器的操作,

调度器被挂起

,意味这

任务将无法切换

,但是

不需要关闭中断

;函数只能在任务中被调用,

不能在中断处理函数中使用



具体函数如下



vTaskSuspendAll()

//挂起任务调度器;仅仅防止任务之间的资源争夺,

中断仍然可以响应

;支持嵌套使用。


vTaskResumeAll()

//恢复任务调度器,在任意任务中调用;需要

和挂起函数成对出现




返回值



为bpTRUE则不需要手动任务切换,为pdFALSE则需要手动切换任务(



portYIELD_WITHIN_API






七、FreeRTOS的列表与列表项


1、列表与列表项简介


列表(List)

:列表是FreeRTOS中最基本的一种数据结构,其在物理存储单元上是非连续、非顺序的;列表在FreeRTOS中的应用十分广泛,要注意的是,

FreeRTOS


中的列表是一个双向链表

,在list.h文件中,有列表的相关定义,

具体代码如下所示


参数解释

① 包含了两个宏,分别为

listFIRST_LIST_INTEGRITY_CHECK_VALUE





listSECOND_LIST_INTEGRITY_CHECK_VALUE

,这两个宏用于存放确定已知常量,FreeRTOS通过

检查这两个常量的值,来判断列表的数据在程序运行过程中,是否遭到破坏

;类似这样的宏定义在列表项和迷你列表项中也有出现;

该功能一般用于调试,默认是不开启的

② 成员变量

uxNumberOfItems

用于记录列表中

列表项的个数(



不包含




xListEnd





,当往列表中插入列表项时,该值加1;当从列表中移除列表项时,该值减1。
③ 成员变量

pxIndex

用于指向列表中的

某个列表项

,一般

用于遍历列表中的所有列表项

④ 成员变量

xListEnd

是一个迷你列表项;列表中迷你列表项的值

一般被设置为最大值



用于将列表中的所有列表项按升序排序时,排在最末尾

;同时xListEnd

也用于挂载其他插入到列表中的列表项


结构示意


列表项(List Item)

:列表项是用于指向一个数据的列表中的节点,在list.h 文件中,有列表项的相关定义,

具体代码如下所示


参数解释

① 如同列表一样,列表项中也包含了两个用于检测列表项数据完整性的宏定义。
② 成员变量

xItemValue

为列表项的值,这个值

多用于按升序对列表中的列表项进行排序

③ 成员变量

pxNext





pxPrevious

分别用于指向列表中

列表项的下一个列表项和上一个列表项

④ 成员变量

pxOwner

用于

指向包含列表项的对象(



通常是任务控制块





,因此,列表项和包含列表项的对象之间存在双向链接。
⑤ 成员变量

pxContainer

用于指向

列表项所在列表


结构示意图


迷你列表项(Mini List Item)

:迷你列表项也是列表项,但迷你列表项

仅用于标记列表的末尾和挂载其他插入列表中的列表项



用户是用不到迷你列表项的

;在list.h 文件中,有迷你列表项的相关定义,

具体的代码录下所示


参数解释

① 迷你列表项中也同样包含用于检测列表项数据完整性的宏定义。
② 成员变量

xItemValue

为列表项的值(

通常配置为最大值

),这个值多用于按升序对列表中的列表项进行排序。
③ 成员变量

pxNext



pxPrevious

分别用于指向列表中

列表项的下一个列表项和上一个列表项


结构示意


2、相关的API函数


初始化列表

:函数

vListInitialise

()
此函数用于初始化列表,在定义列表之后,需要先对其进行初始化,只有初始化后的列表,才能够正常地被使用;列表初始化的过程,其实就是初始化列表中的成员变量。

函数原型如下所示


函数的实现源码如下


初始化列表项

:函数

vListInitialiseItem

()
此函数用于初始化列表项,如同列表一样,在定义列表项之后,也需要先对其进行初始化;只有初始化好的列表项,才能够被正常地使用;列表项初始化的过程,也是初始化列表项中的成员变量。

函数原型如下所示


函数的实现源码如下


列表项的无序插入

:函数

vListInsertEnd

()
此函数用于将待插入列表的列表项

插入到列表


pxIndex


指针指向列表项的前面

,是一种

无序的插入方法



函数原型如下所示


函数的实现源码如下


列表项升序插入

:函数

vListInsert

()
此函数用于将待插入列表的列表项

按照列表项值升序排序

的顺序,有序地插入到列表中。

函数原型如下所示


函数的实现源码如下


从列表中删除列表项

:函数

uxListRemove

()
此函数用于将列表项从列表项所在列表中移除,

函数原型如下所示


函数的实现源码如下


3、其他用于操作列表和列表项的宏


八、FreeRTOS启动与任务底层原理解析


1、ARM的汇编指令集列表(



来自《Cortex-M3权威指南》






边框加粗的是从


ARMv6T2


才支持的指令。


双线边框的是从


Cortex‐M3


才支持的指令(


v7


的其它款式不一定支持)
//

2、开启任务调度器

这里

总结性的描述整体过程

,具体的源码分析

参考《FreeRTOS开发指南》第八章



创建

空闲任务 和 定时器任务(

如果配置启动软件定时器的话

);


关闭全局中断

,防止中断打断启动进程;


初始化FreeRTOS的全局变量

,包括任务阻塞超时时间、FreeRTOS任务调度器状态等;


调用硬件平台相关函数

,初始化 systick和pendsvc中断,初始化 进入临界区的计数器为0;
⑤ 初始化MSP指针到堆栈起始地址,启动全局中断,并触发一次SVC中断(

ARM汇编

);


找到就绪任务中优先级最高的任务

,将堆栈的寄存器信息恢复到CPU的寄存器中,配置启用PSP指针,配置PC指针指向任务函数,开始执行任务(

ARM汇编

)。
//

3、任务状态列表

在开始介绍FreeRTOS中任务相关的API函数之前,先介绍一下FreeRTOS中的任务状态列表,前面章节说了,

FreeRTOS


中的任务无非就四种状态,分别为运行态、就绪态、阻塞态和挂起态,这四种状态中,除了运行态,其他三种任务状态的任务都有其对应的任务状态列表

;FreeRTOS使用这些任务状态列表来管理处于不同状态的任务。任务状态列表在task.c文件中有定义,

具体的定义代码如下所示


① 就绪态列表

,从定义可以看出,就绪态任务列表,从定义中可以看出,就绪态任务列表是一个数组,数组中元素的个数由宏configMAX_PRIORITY确定,宏configMAX_PRIORITY为配置的系统最大任务优先级。由此可见,FreeRTOS为每个优先等级的任务都分配了一个就绪态任务列表。

② 阻塞态任务列表

,阻塞态任务列表

一共有两个

,分别为是阻塞态任务列表1和阻塞态任务列表2,并且该有两个阻塞态任务列表指针;这么做是为了解决任务阻塞时间溢出的问题,这个会在后续讲解阻塞相关的内容时,具体分析。

③ 挂起任务列表

,被挂起的任务就会被添加到挂起态任务列表中。
//

4、任务创建函数解析(


vTaskCreate()





函数接口原型


过程解析

① 函数xTaskCreate()创建任务,首先

为任务的任务控制块以及任务栈空间申请内存

,如果任务控制块或任务栈空间的内存申请失败,则释放已经申请到的内存,并返回内存申请失败的错误。


调用函数prvInitialiseNewTask()

初始化任务控制块中的成员变量,包括任务函数指针、任务名、任务栈大小、任务函数参数、任务优先级等。
③ 然后

调用函数prvAddNewTaskToReadyList()

,将任务添加到对应优先级的就绪任务列表中。
///

5、任务删除函数解析(


vTaskDelete()





函数接口原型


过程解析

① 使用vTaskDelete()删除任务时,

需要考虑两种情况

,分别为

待删除任务不是当前正在运行的任务

(调用该函数的任务)和

待删除任务为当前正在运行的任




(调用该函数的任务)。第一种情况比较简单,当前正在运行的任务可以直接删除待删除任务;而第二种情况下,待删除任务时无法删除自己的,因此需要将当前任务添加到任务待删除列表中,空闲任务会处理这个任务待删除列表,将待删除的任务统一删除。
② 在待删除任务不是当前正在运行的任务这种情况下,当前正在运行的任务可以删除待删除的任务,因此调用了函数prvDeleteTCB(),将待删除的任务删除。
///

6、挂起任务函数解析(


vTaskSuspend()





函数接口原型


过程解析

① 任务句柄将会从就绪态被转移到挂起态列表;
② 使用函数vTaskSuspend()挂起任务时,如果任务调度器没有运行,并且待挂起的任务又是调用函数vTaskSuspend()的任务,那么pxCurrentTCB需要指向其他优先级最高的就绪态任务;

更新


pxCurrentTCB


的操作是通过调用函数


vTaskSwitchContext()


实现的

③ 如果任务调度器正在运行,且将要挂起的任务正在运行,则需要切换任务。
///

7、恢复任务函数解析(


vTaskResume()





函数接口原型


过程解析

① 将要恢复的任务从挂起列表恢复到就绪列表;
② 如果任务优先级高于当前正在运行的任务,则需要切换任务;
///


九、FreeRTOS任务切换原理


1、PendSV中断服务函数


PendSV





Pended Service Call


,可挂起服务调用

),是一个对RTOS 非常重要的异常。PendSV 的中断优先级是可以编程的,用户可以根据实际的需求,对其进行配置;

PendSV


的中断由将中断控制状态寄存器(


ICSR


)中


PENDSVSET


为置一触发

。PendSV 与SVC 不同,

PendSV


的中断是非实时的,即


PendSV


的中断可以在更高优先级的中断中触发,但是在更高优先级中断结束后才执行

利用PendSV 的这个可挂起特性,

在设计


RTOS


时,可以将


PendSV


的中断优先级设置为最低的中断优先级;


这么一来,


PendSV


的中断服务函数就会在其他所有中断处理完成后才执行。

任务切换时,就需要用到PendSV 的这个特性。

FreeRTOS中的函数原型

:__asm void

xPortPendSVHandler

( void )

函数执行过程解析

① R0寄存器保存当前执行任务的PSP指针(

CPU会在进入中断之前,



自动



保存部分寄存器到任务堆栈

);
② R2保存了任务控制块栈顶指针成员的地址;
③ 利用RO寄存器中PSP指针位置,

手动

将R4~R11寄存器的值入栈到任务堆栈中;
④ 保存当前PSP指针最新堆栈位置到任务控制块的栈顶指针成员变量中;
⑤ 保存R3和R14的值到MSP指针中,一会要用(

R3保存的是当前执行任务控制块变量的地址

);
⑥ 屏蔽FreeRTOS所管理的所有中断;
⑦ 调用

函数vTaskSwitchContext(

)查找下一个任务控制块;
⑧ 重新取消中断屏蔽,从MSP寄存器中恢复R3和R14寄存器(

此时R3所指的实际任务控制块已经被替换了

);
⑨ R1获取到R3所指的任务控制块对象地址;
⑩ R0获取R1中的任务栈顶指针,接着将任务堆栈中的内存出栈到R4~R11中;
11)设备PSP指针执行新的任务堆栈位置;
12)触发PC指针,开始切换执行新的任务;

2、任务选择函数vTaskSwitchContext()


函数原型

:void

vTaskSwitchContext

( void )

函数运行解析

① 判断任务调度器是否运行,如果没运行则标记需要启动,并重复调用PandSV直到运行;
② 调用宏函数taskSELECT_HIGHEST_PRIORITY_TASK()实现具体动作;
③ 通过

优先级就绪任务变量的bit位

,找到存在就绪任务的最高优先级列表并返回;
④ 根据返回的优先级,确认对应列表中是否有就绪任务;
⑤ 从列表中找出尾节点的下个任务的控制块,并返回作为新的执行任务;

3、同等优先级任务时间片轮询的原理

在FreeRTOSConfig.h中,需要

配置


configUSE_PREEMPTION=1 和 configUSE_TIME_SLICING=1

,才可以使能时间片调度;且时间片调度算法

只能发生在任务优先级相同的任务之间


4、个人总结(



重要





在FreeRTOS系统中,无论是

抢占式任务调度算法 还是 时间片轮询算法

,其

主要路径是同一个

:都是通过systick中断触发pandSV中断,然后在中断中 先查看就序列表中,存在任务的最高优先级任务;如果优先级相同,则采取时间片轮询。

在其他情况下

,如任务阻塞,挂起等,则通过

提前触发pandSV中断

来切换任务,并将

当前任务从就序列表中移除



版权声明:本文为weixin_40639467原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。