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-Ⅲ系统中存在大量临界段代码.采用两种方式对临界 ...
随机推荐
- SPSS Clementine 数据挖掘入门1
SPSS Clementine是Spss公司收购ISL获得的数据挖掘工具.在Gartner的客户数据挖掘工具评估中,仅有两家厂商被列为领导者:SAS和SPSS.SAS获得了最高ability to e ...
- Hadoo生态中pHive HBase 项目的区别
http://jenmhdn.iteye.com/blog/1678789 导读:Apache Hive是一个构建于Hadoop(分布式系统基础架构)顶层的数据仓库,Apache HBase是运行于H ...
- PHP实现双向链表
看了很久数据结构但是没有怎么用过,在网上看到了关于PHP的数据结构,学习了一下,与大家一起分享一下.上一次分享了链表,这次来补充说一下双向链表. 简短不割 ...
- [Unit Testing for Zombie] 06. Using Factory
FACTORIES Convert the zombies fixture to a Factory Girl Factory called :zombie. test/fixture/zombies ...
- HAWQ技术解析(八) —— 大表分区
一.HAWQ中的分区表 与大多数关系数据库一样,HAWQ也支持分区表.这里所说的分区表是指HAWQ的内部分区表,外部分区表在后面"外部数据"篇讨论. 在数据仓库应用中 ...
- T-SQL 之 语法元素
一.标识符 在T-SQL语言中,对SQLServer数据库及其数据对象(比如表.索引.视图.存储过程.触发器等)需要以名称来进行命名并加以区分,这些名称就称为标识符. 通常情况下,SQLServer数 ...
- SQL语言 之 事务控制
一.概述 事务是一些数据库操作的集合,这些操作由一组相关的SQL语句组成(只能是 DML 语句),它们是一个有机的整体,要么全部成功执行,要么全部不执行.事务时数据库并发控制和恢复技术的基本单位. 事 ...
- iPhone真机调试流程
大致流程:绑定设置ID--绑定电脑---绑定appbundle ID mac----钥匙串----证书助理-----从证书机构请求颁发证书-----填写邮件----生成证书到桌面 数据线连接设备--- ...
- 深入理解C++的动态绑定和静态绑定
转自:http://blog.csdn.net/chgaowei/article/details/6427731 为了支持c++的多态性,才用了动态绑定和静态绑定.理解他们的区别有助于更好的理解多态性 ...
- linux 16进制 产看文件
hexdump - ascii, decimal, hexadecimal, octal dump 查看十六机制的首选工具. -c 每单元以字节为单位,显示出对应的ASCII码 -C 每单元以字 ...