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-Ⅲ系统中存在大量临界段代码.采用两种方式对临界 ...
随机推荐
- http://m.blog.csdn.net/article/details?id=49132747
http://m.blog.csdn.net/article/details?id=49132747
- Sqlite清空表数据
delete from TableName; //清空数据 where name ='TableName';//自增长ID为0
- Cognos权限Custom Java Provider表结构实例
select * from org_user;USER_ID USER_CODE USER_NAME FULL_NAME EMAIL PWD2 889 zhangsan 张三 123@126.com ...
- Python requests 报错解决集锦
python版本号和ssl版本号都会导致 requests在请求https站点时候会出一些错误,最好使用新版本号. 1 Python2.6x use requests 一台老Centos机器上跑着古老 ...
- C++ 11 - STL - 函数对象(Function Object) (上)
1. 定义 在STL中,可以把函数传递给算法,也可以把函数对象传递给算法. 那么,什么是函数对象呢? 我们来看下它的声明: class X { public: // define function c ...
- 深入单例模式 - Java实现
单例模式可能是代码最少的模式了,但是少不一定意味着简单,想要用好.用对单例模式,还真得费一番脑筋.本文对Java中常见的单例模式写法做了一个总结,如有错漏之处,恳请读者指正. 饿汉法 顾名思义,饿汉法 ...
- post请求乱码
jsp页面中 <meta http-equiv="content-type" content="text/html; charset=UTF-8"& ...
- 局域网Ubuntu与WinXP实现文件共享
时间:2008-11-28 11:27:55 从新立得软件包管理器中安装Samba和Smbfs.Samba是在Unix系统中用于共享文件和打印机的应用软件.Smbfs is a filesyste ...
- Linux命令-帮助命令:help
which cd 查找cd命令的位置 which umask 查找umask命令的位置 Shell就是命令解释器,就是当你敲了一个命令,谁来把这个命令传递给内核,内核运行结束后返回一个结果,是谁把这个 ...
- 对象语义与值语义、资源管理(RAII、资源所有权)、模拟实现auto_ptr<class>、实现Ptr_vector
一.对象语义与值语义 1.值语义是指对象的拷贝与原对象无关.拷贝之后就与原对象脱离关系,彼此独立互不影响(深拷贝).比如说int,C++中的内置类型都是值语义,前面学过的三个标准库类型string,v ...