FreeRTOS 临界段和开关中断
以下转载自安富莱电子: http://forum.armfly.com/forum.php
临界段
代码的临界段也称为临界区,一旦这部分代码开始执行,则不允许任何中断打断。为确保临界段代码
的执行不被中断,在进入临界段之前须关中断,而临界段代码执行完毕后,要立即开中断。
FreeRTOS 临界段相关知识补充
FreeRTOS 的源码中有多处临界段的地方, 临界段虽然保护了关键代码的执行不被打断, 但也会
影响系统的实时性。比如此时某个任务正在调用系统 API 函数,而且此时中断正好关闭了,也就是进
入到了临界区中,这个时候如果有一个紧急的中断事件被触发,这个中断就不能得到及时执行,必须
等到中断开启才可以得到执行, 如果关中断时间超过了紧急中断能够容忍的限度, 危害是可想而知的。
FreeRTOS 源码中就有多处临界段的处理,跟 FreeRTOS 一样,uCOS-II 和 uCOS-III 源码中都是有
临界段的,而 RTX 的源码中不存在临界段。 另外,除了 FreeRTOS 操作系统源码所带的临界段以外,用
户写应用的时候也有临界段的问题,比如以下两种:
读取或者修改变量(特别是用于任务间通信的全局变量)的代码,一般来说这是最常见的临界代码。
调用公共函数的代码,特别是不可重入的函数,如果多个任务都访问这个函数,结果是可想而知的。
总之,对于临界段要做到执行时间越短越好,否则会影响系统的实时性。

任务代码临界段处理
FreeRTOS 任务代码中临界段的进入和退出主要是通过操作寄存器 basepri 实现的。进入临界段前操
作寄存器 basepri 关闭了所有小于等于宏定义 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
所定义的中断优先级,这样临界段代码就不会被中断干扰到,而且实现任务切换功能的 PendSV 中断和滴
答定时器中断是最低优先级中断,所以此任务在执行临界段代码期间是不会被其它高优先级任务打断的。
退出临界段时重新操作 basepri 寄存器,即打开被关闭的中断(这里我们不考虑不受 FreeRTOS 管理的更
高优先级中断)。 FreeRTOS 进入和退出临界段的函数如下:
#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()
上面这两个函数是供用户调用的,其中函数 taskENTER_CRITICAL 是进入临界段,函数
taskEXIT_CRITICAL 是退出临界段。 进一步跟踪宏定义的实现如下:
#define portENTER_CRITICAL() vPortEnterCritical()
#define portEXIT_CRITICAL() vPortExitCritical()
再进一步跟踪宏定义的实现如下:

通过上面的两个函数 vPortEnterCritical 和 vPortExitCritical 可以看出,进入临界段和退出临界段是通过
函数调用开关中断函数 portENABLE_INTERRUPTS 和 portDISABLE_INTERRUPTS 实现的。 细心的读者
还会发现上面的这两个函数都对变量 uxCriticalNesting 进行了操作。这个变量比较重要,用于临界段的
嵌套计数。初学的同学也许会问这里直接的开关中断不就可以了吗,为什么还要做一个嵌套计数呢?主要
是因为直接的开关中断方式不支持在开关中断之间的代码里再次执行开关中断的嵌套处理,假如当前我们
的代码是关闭中断的,嵌套了一个含有开关中断的临界区代码后,退出时中断就成开的了,这样就出问题
了。 通过嵌套计数就有效地防止了用户嵌套调用函数 taskENTER_CRITICAL 和 taskEXIT_CRITICAL 时出错。



通过上面的源码实现可以看出,FreeRTOS 的开关全局中断是通过操作寄存器 basepri 实现的,关于这个寄存器,我们已经在前面进行了详细的讲解,这里不再赘述。
使用的时候一定要保证成对使用 。

中断服务程序临界段处理
与任务代码里临界段的处理方式类似,中断服务程序里面临界段的处理也有一对开关中断函数。
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
进一步跟踪宏定义的实现如下:
#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x)
再进一步跟踪宏定义的实现如下:

通过上面的源码可以看出,中断服务程序里面的临界段代码的开关中断也是通过寄存器 basepri 实现的。
初学的同学也许会问,这里怎么没有中断嵌套计数了呢?是的,这里换了另外一种实现方法,通过保存和
恢复寄存器 basepri 的数值就可以实现嵌套使用。如果大家研究过 uCOS-II 或者 III 的源码,跟这里的实
现方式是一样的,具体看下面的使用举例。
使用举例:
使用的时候一定要保证成对使用


开关中断的实现
FreeRTOS 也专门提供了一组开关中断函数,实现比较简单,其实就是前面 15.2 小节里面临界段进
入和退出函数的精简版本,主要区别是不支持中断嵌套。 具体实现如下:
#define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS()
#define taskENABLE_INTERRUPTS() portENABLE_INTERRUPTS()
进一步跟踪宏定义的实现如下:
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 )
函数 vPortRaiseBASEPRI 和 vPortSetBASEPRI 的源码实现如下:
从上面的源码可以看出, FreeRTOS 的全局中断开关是通过操作寄存器 basepri 实现的,关于这个寄存器,
我们已经在前面进行了详细的讲解,这里不再赘述。
使用的时候一定要保证成对使用
BSP 板级支持包中开关中断的特别处理
前面为大家讲解了 FreeRTOS 临界段的处理方法和开关中断方法,加上了 FreeRTOS 操作系统后,我
们实际编写的外设驱动又该怎么修改呢?因为外设驱动编写时,有些地方有用到开关中断操作,这里以此
教程配套的 STM32F103,F407 和 F429 开发板为例进行说明,这三种开发板的外设驱动的编写架构都是
统一的,用户只需将 bsp.h 文件里面的宏定义:

#define USE_FreeRTOS 1
将中断开关设置改成了条件编译的形式,这样在使用裸机或者使用 FreeRTOS 时,切换自如。此宏定
义配置为 1 表示使用 FreeRTOS 的开关中断 API 函数,配置为 0 表示使用裸机的方式开关中断。
采用 taskENTER_CRITICAL()和 taskEXIT_CRITICAL()实现开关中断
因为 BSP 驱动包的源码基本没有在中断里面进行开关中断,都是在中断以外,所以开关中断是采用的
任务代码里面临界段的处理函数,而且支持嵌套调用。
大家写的工程代码也可以采用类似的方案,方便裸机和 FreeRTOS 的切换,或者采用其它适合自己的
方案。 另外要注意,因为 FreeRTOS 存在不受其控制的更高优先级中断,用户需要根据实际情况进行特别
处理,可以不采用 FreeRTOS 的开关中断函数,而是直接使用__set_PRIMASK 实现全局中断的开关。
FreeRTOS 临界段和开关中断的更多相关文章
- 【转载】ucos临界段
其实很简单: 临界段就是不可中断的程序段,比如从UART中读取当前传递回来的值,如果有UART中断,此时这个值又会改变.同样临界段就是保护这类全局变量,如在读取时间节拍时,不应该被时钟更新时钟 ...
- 8.0-uC/OS-III临界段
1.临界段 (临界段代码,也叫临界区,是指那些必须完整连续运行,不可被打断的代码段) 锁调度器,可以执行ISR,开启调度器不可执行ISR: (1).临界段代码,也称作临界域,是一段不可分割的代码. u ...
- FreeRTOS不允许在中断服务程序和临界段中执行不确定的性的操作
举例 等待事件标志组的任务,要是在中断服务程序中设置事件标志组,但不知道当前有多少个任务在等待此事件标志,这个操作即为不确定性操作,为了不在中断服务程序中执行此不确定性操作,只在中断服务程序中给一确定 ...
- FreeRTOS 中断配置和临界段
中断屏蔽寄存器 PRIMASK.FAULTMASK和BASEPRI 1.PRIMASK:这是个只有1个位的寄存器.当它置1时, 就关掉所有可屏蔽的异常,只剩下 NMI和硬fault可以响应.它的缺省值 ...
- RTX——第11章 临界段,任务锁和中断锁
以下内容转载自安富莱电子: http://forum.armfly.com/forum.php 临界段代码的临界段也称为临界区,一旦这部分代码开始执行,则不允许任何中断打断.为确保临界段代码的执行不被 ...
- RTX临界段,中断锁与任务锁
临界段 代码的临界段也称为临界区,一旦这部分代码开始执行,则不允许任何中断打断.为确保临界段代码的执行不被中断,在进入临界段之前须关中断,而临界段代码执行完毕后,要立即开中断. ...
- FreeRTOS临界保护
下面的是个人笔记,所有的话都适用于我本人理解,可能存在不对的地方. 进入临界保护(支持嵌套):taskENTER_CRITICAL(); 退出临界保护(支持嵌套):taskEXIT_CRITICAL( ...
- 002_FreeRTOS临界段代码
(一)临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段 (二)FreeRTOS 与 临 界 段 代 码 保 护 有 关 的 函 数 有 4 个,两个是任务级的临界段代码保护,两个是中断 ...
- μC/OS-Ⅲ中的临界段代码
临界段代码(critical sections),也叫临界区(critical region),是指那些必须完整连续运行,不可被打断的代码段.μC/OS-Ⅲ系统中存在大量临界段代码.采用两种方式对临界 ...
随机推荐
- DICOM中的入门概念
DICOM标准是医学影像界技术人员逃不掉的标准.本系列专题是JATI对DICOM标准的阐述,力图使PACS管理员和软件工程师都能理解. DICOM标准的提出者DICOM标准委员会是ISO组织的合作者. ...
- Windows之权限的继承性 累加性 优先性 交叉性及四项基本原则
Windows NT以后的文件,及文件夹共享设置有以下特性:继承性.累加性.优先性.交叉性. 继承性是说下级的目录在没有经过重新设置之前,是拥有上一级目录权限设置的.这里还有一种情况要说明一下 ...
- TensorFlow进阶(五)---图与会话
图与会话 图 tf.Graph TensorFlow计算,表示为数据流图.一个图包含一组表示 tf.Operation计算单位的对象和tf.Tensor表示操作之间流动的数据单元的对象.默认Graph ...
- 为什么Domain controller上的time synchronization非常重要?
虚拟机默认情况下所拥有的资源都是不同的, 比如说CPU clock. 在一个忙碌的系统中, 虚拟机甚至可能在很短的一段时间内被拒绝分配资源给它, 这种情况还可能发生在高系统负荷, VMotion, B ...
- 修改elementUI组件样式无效的问题研究
问题背景:el-tabs的选项卡默认字体是14px,大了,想改成12px,结果在style里面加样式总是不生效. 解决:样式放到app.vue里面,样式就生效了 .panel-content .el- ...
- Node.js:模块系统、函数
为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统. 模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的.换言之,一个 Node.js 文件就是一个模块, ...
- sqllite3
OS X自从10.4后把SQLite这套相当出名的数据库软件,放进了作业系统工具集里.OS X包装的是第三版的SQLite,又称SQLite3.这套软件有几个特色: 软件属于公共财(public do ...
- Samba简单教程+Samba中文显示问题
1.检测安装Samba服务(1).检查Samba服务包是否已安装:rpm -qa | grep samba(2).如未安装,则插入第一张安装盘,执行:mount /mnt/cdromcd /cdrom ...
- 更新后android studio 2.0 构建gradle卡在Refreshing Gradle Project 解决办法
首先打开android studio项目 找到项目目录gradle\wrapper\gradle-wrapper.properties这个文件 你会看到 #Wed Apr 10 15:27:10 PD ...
- 取石子 (四)_nyoj_161(博弈-奇异矩阵).java
取石子 (四) 时间限制: 1000 ms | 内存限制: 65535 KB 难度: 4 描述 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是 ...