03_临界段的保护

2023-05-16

一、临界段的概念

所谓的临界段就是在执行时不能被中断的代码段。在FreeRTOS中,临界段最常出现的地方就是对全局变量的操作。那么什么情况下临界段会被打断?一个是系统调度,还有一个是外部中断。在FreeRTOS中,系统调度最终也是产生PendSV中断,在PendSV Handler中实现任务的切换,所以还是可以归结为中断。既然这样,FreeRTOS对临界段的保护最终还是回归到对中断的开和关的控制。

二、Cortex-M内核快速关中断指令

为了快速地开关中断,Cortex-M内核专门设置了一条CPS指令,它有四种用法,具体如下:

CPSID I;PRIMASK = 1;关中断
CPSIE I;PRIMASK = 0;开中断
CPSID F;FAULTMASK = 1;关异常
CPSIE F;FAULTMASK = 1;开异常

在FreeRTOS中,对中断的开关是通过BASEPRI寄存器来实现的,即大于等于BASEPRI寄存器的值的中断会被屏蔽,小于BASEPRI的值的中断不会被屏蔽,不受FreeRTOS管理。用户可以设置BASEPRI的值来选择性地给一些非常紧急的中断留出余地。《Cortex M3与M4权威指南》中对BASEPRI寄存器的描述如下:

In some cases, you might only want to disable interrupts with priority lower than a certain level. In this case, you could use the BASEPRI register. To do this, simply write the required masking priority level to the BASEPRI register. For example, if you want to block all exceptions with priority level equal to or lower than 0x60,you can write the value to BASEPRI:

__set_BASEPRI(0x60); // Disable interrupts with priority 0x60-0xFF using CMSIS-Core function

For users of assembly language, the same operation can be written as:

MOVS R0, #0x60

**MSR BASEPRI, R0 **; Disable interrupts with priority

三、关中断

FreeRTOS中关中断的函数在portmacro.h文件中实现,分为不带返回值和带返回值两种。

1.不带返回值的关中断函数

/* 不带返回值的关中断函数不能嵌套,不能在中断中使用 */
#define portDISABLE_INTERRUPTS()   vPortRaiseBASEPRI()

static portFORCE_INLINE void vPortRaiseBASEPRI(void)
{
    uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
    __asm
    {
        msr basepri,ulNewBASEPRI
        dsb
        isb
    }
}

不带返回值的意思是在向BASEPRI中写入新值时,不用先将BASEPRI的值保存起来,即不用考虑当前的中断状态是怎么样的,这意味着这个函数不能在中断中使用。第6行的configMAX_SYSCALL_INTERRUPT_PRIORITY是在FreeRTOSConfig.h中定义的一个宏,该宏默认是191,高四位有效即等于0xb0,即十进制的11,所以优先级大于等于11的中断都会被屏蔽,11以内的中断不受FreeRTOS管理。

2.带返回值的中断

/* 带返回值的关中断函数可以嵌套,可以在中断中使用 */
#define portSET_INTERRUPT_MASK_FROM_ISR()   ulPortRaiseBASEPRI()
static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI(void)
{
    uint32_t ulReturn,ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
    __asm
    {
        mrs ulReturn,basepri
        msr basepri,ulNewBASEPRI
        dsb
        isb
    }
    return ulReturn;
}

带返回值的意思是在向BASEPRI写入新值之前,先将BASEPRI的值保存起来,在更新完BASEPRI的值时,将之前的BASEPRI的值返回,返回的值作为参数传入开中断函数中,以此来恢复之前的状态。

四、开中断

开中断函数在portmacro.h中定义。

/* 不带中断保护的开中断函数 */
#define portENABLE_INTERRUPTS() vPortSetBASEPRI(0)
/* 带中断保护的开中断函数 */
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)    vPortSetBASEPRI(x)

static portFORCE_INLINE void vPortSetBASEPRI(uint32_t ulBASEPRI)
{
    __asm
    {
        msr basepri,ulBASEPRI
    }
}

第2行为不带中断保护的开中断函数,直接将BASEPRI的值设置为0,与不带返回值的关中断函数portDISABLE_INTERRUPTS()成对使用。第4行是带中断保护的开中断函数,将上一次关中断时返回的值作为形参重新写入BASEPRI寄存器中,与带返回值的关中断函数portSET_INTERRUPT_MASK_FROM_ISR()成对使用。

五、进入、退出临界段的宏

进入和退出临界段的宏在task.h中定义,具体如下:

/* 进入和退出临界段的宏定义 */
#define taskENTER_CRITICAL()            portENTER_CRITICAL()
#define taskENTER_CRITICAL_FROM_ISR()   portSET_INTERRUPT_MASK_FROM_ISR()

#define taskEXIT_CRITICAL()             portEXIT_CRITICAL()
#define taskEXIT_CRITICAL_FROM_ISR(x)   portCLEAR_INTERRUPT_MASK_FROM_ISR(x)    

进入和退出临界段的宏分为中断保护和非中断保护两种,但最终都是通过开关中断来实现的。这样多次定义的目的是为了兼容不同的处理器,因为不同的处理器的开关中断的实现是不同的,在task.h中的最终调用的,在port.h中的是根据不同的处理器来实现的。

1.进入临界段

(1)不带中断保护,不能嵌套

/* 在portmacro.h中定义 */
#define portENTER_CRITICAL()        vPortEnterCritical()
/* 在port.c中定义 */
void vPortEnterCritical(void)
{
    portDISABLE_INTERRUPTS();
    uxCriticalNesting++;
    if(uxCriticalNesting==1)
    {
        configASSERT((portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK) == 0);
    }
}

第7行中的uxCriticalNesting是一个port.c中定义的静态变量,默认初始化为0xaaaaaaaa,在调度器启动后会被重新初始化为0,其作用是对临界段嵌套的计数。第8行,如果uxCriticalNesting等于表示当前有一层嵌套,要确保当前没有中断活跃,即啮合外设SCB中的中断和控制寄存器ICSR的低8位要等于0。

(2)带中断保护,可以嵌套

#define portSET_INTERRUPT_MASK_FROM_ISR()   ulPortRaiseBASEPRI()

定义的函数在前面关中断的时候已经提及。

2.退出临界段

(1)不带中断保护,不能嵌套

/* 在portmacro.h中定义 */
#define portEXIT_CRITICAL()         vPortExitCritical()
void vPortExitCritical(void)
{
    configASSERT(uxCriticalNesting);
    uxCriticalNesting--;
    if(uxCriticalNesting == 0)
    {
        portENABLE_INTERRUPTS();
    }
}

(2)带中断保护,可以嵌套

/* 在portmacro.h中定义 */
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)    vPortSetBASEPRI(x)

六、临界段代码的应用

在FreeRTOS中,临界代码的应用出现在两种场合,一种是在中断场合,另一种是在非中断场合。应用示例代码如下:

/* 在中断场合使用,临界段可以嵌套 */
{
    uint32_t ulReturn;
    /* 进入临界段,临界段可以嵌套 */
    ulReturn = taskENTER_CRITICAL_FROM_ISR();
    
    /* 临界段代码 */
    
    /* 退出临界段 */
    taskEXIT_CRITICAL_FROM_ISR();
}
/* 在非中断场合,临界段不能嵌套 */
{
    /* 进入临界段 */
    taskENTER_CRITICAL();
    
    /* 临界段代码 */
    
    /* 退出临界段 */
    taskEXIT_CRITICAL();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

03_临界段的保护 的相关文章

  • c++模板类/模板函数的声明与定义应该放在头文件里

    c 43 43 模板类 模板函数的声明与定义应该放在头文件里 xff0c 不要分开来写类中函数的声明与定义 xff08 比如在 H文件里声明某个成员函数 xff0c 在 CPP文件里定义该成员函数 xff09 xff0c 这样会导致连接错误
  • STM32的GPIO端口配置八种模式的理解

    文章目录 一 GPIO的结构框图二 八种模式1 模拟输入 xff08 GPIO Mode AIN xff09 2 浮空输入 xff08 GPIO Mode IN FLOATING xff09 3 上拉输入 xff08 GPIO Mode I
  • ESP32 micro-usb 多数据量并简单加密的串口通信

    文章目录 前言一 ESP32和上位机的环境二 上位机部分1 串口通信的准备2 数据的准备与发送 三 下位机部分1 ESP32串口通信准备2 数据的接收四 同时测试 总结 前言 最近在研究单片机与上位机的串口通信 xff0c 刚好手头有一块E
  • boost之跨平台 错误处理

    system C 43 43 中处理错误的最佳方式是使用异常 xff0c 但操作系统和许多底层AP工不具有这个能力 xff0c 它们一般使用更通用也更难以操作的错误代码来表示出错的原因 xff0c 不同的操作系统的错误代码通常不是兼容的 x
  • 记录一下vector基本用法(简单易懂)

    vector容器的初始化 vector的使用首先需要加一个头文件 include lt vector gt xff1b vector lt int gt a 最一般的初始化方法 xff0c 就是定义一个容器啊a xff1b vector l
  • MPU6050基本原理介绍及程序配置

    一 MPU6050简介 1 内部主要结构 xff1a 陀螺仪 加速度计 数字运动处理器DMP xff08 Digital Motion Processor xff09 PS MPU6050还含有第二IIC接口 xff0c 用于连接一个 第三
  • Robomaster上位机视觉摘要——比赛规则篇

    本文是笔者多日来总结的2023赛季中针对上位机组的比赛规则摘要 xff0c 力求一文让你看懂上位机在赛场中的飒爽身影 目录 电力元件 电池 遥控器 激光 涂装 机器人 飞镖 雷达 空中机器人 工程机器人 哨兵机器人 英雄与步兵机器人 视觉应
  • C语言:结构体——关于内存字节对齐图文详解

    前言 xff1a 我们在学到c语言内存管理的时候总是一遍惊叹 xff0c 其聪明的内存管理策略 xff0c 一遍抱怨其难以理解的方法 xff0c 网上的资料要不讲究的太详细 xff0c 要不没能讲解清楚 xff0c 今天我们根据实例来学习一
  • 结构体+联合体 详解

    文章目录 一 结构体1 结构体变量2 特殊声明3 结构体的引用1 嵌套调用2 自引用 三 结构体的初始化四 结构体的内存对齐1 用法2 练习题3 修改对齐数 五 位段1 用法2 练习题 六 联合体1 用法2 练习题1 正常算法题2 用联合体
  • Ubuntu20.04——一篇文章让你从零配置VINS_Mono环境以及运行(2023年最新)

    注 xff1a 文末包含该文章涉及的所有安装包的网盘链接 零 换源 xff08 也可以先不换 xff0c 后面觉得下载慢再换也行 xff09 1 备份原来的源 sudo cp etc apt sources list etc apt sou
  • 学C语言推荐的书和软件—C Primer Plus和Dev C++

    写这个的目的是复习巩固C Primer Plus的知识 xff0c 我会一直更新这个系列 对于这本书 xff0c 入门C语言是完全够了 xff0c 后面的链表 队列和二叉树比较综合 xff0c 难度大一些 用这本书学C语言非常好 xff0c
  • mavlink python

    from pymavlink import mavutil Create the connection m 61 mavutil mavlink connection 39 udpin 0 0 0 0 14550 39 dir m mav
  • C++Vector浅析,Vector用法大全

    vector基本概念 功能 xff1a vector数据结构和数组非常相似 xff0c 也成为单端数组 vector与普通数组的区别 xff1a 不同之处在与数组是静态空间 xff0c 而vector可以动态扩展 动态扩展 xff1a 并不
  • 网络通信--Linux

    文章目录 网络通信的基础通信模型IP地址和端口port 网络套接字网络字节序初识UDP与TCP两种协议sockaddr结构体家族认识一些网络常用基础函数 UDP实现简单通信TCP实现简单通信总结 网络通信的基础 网络通信是建立在多层协议之下
  • Git分支和版本(标签)

    目录 一 Git分支 1 1 四大环境 xff08 分支 xff09 1 1 1 分支和标签的关系 1 2 分支的作用 1 3 演示分支 1 3 1 在Gitee中创建项目 1 3 2 克隆到本地 1 3 3 建立分支 1 3 4 切换分支
  • 前端必会算法——栈和队列

    上一篇 前端必会算法 标准快速排序 栈和队列 栈 xff08 Stack xff09 可以理解为是一个箱子 xff0c 存放东西的容器 栈结构的特点 xff1a 先入后出 xff0c 栈相当于一个箱子 xff0c 先放进去的东西被压在了下面
  • OpenMV的单颜色识别讲解

    OpenMV的官方教程 xff1a 寻找色块 xff1b single color rgb565 blob tracking示例讲解 xff1b 视频讲解 需要提前看的文章 xff1a 程序烧录 xff1b 颜色阈值设置 目录 thresh

随机推荐

  • STM32CubeMX串口通讯

    串口的简单介绍 RS 232与TTL 根据通讯使用的电平标准不同 xff0c 串口通讯可分为 TTL 标准及 RS 232 标准 而STM32的串口是TTL电平标准的 如果需要使用到RS 232则需要一个电平转换芯片 单工通信 半双工通信和
  • 自制超简易通讯协议(中断接收)

    前言 在做蓝牙语音小车时 xff0c 总遇到各式各样的问题 本文主题是笔者在解决串口不够并且数据量小的问题时突发奇想自定义一个简易的通讯协议 由于是用89c51做的主控 xff0c 外设的资源比较紧张 串口只有一个 xff0c 但两个系统需
  • 【Java杂谈】Iterator(迭代器)的使用

    x1f3b8 Iterator是什么 xff1f 迭代器是Java提供的一种访问集合的方法 xff0c Iterator 是 Java 迭代器最简单的实现 xff0c 常用来访问ArrayList HashMap等类的对象 Iterator
  • orb 纯背景物体识别

    include lt chrono gt include lt iostream gt include lt opencv2 core core hpp gt include lt opencv2 features2d features2d
  • Windows逆向安全(一)之基础知识(九)

    汇编比较三种循环 众所周知 xff0c 在C语言可以使用可以使用三种循环 xff0c 分别是 xff1a while do while和for 本文从汇编的角度出发 xff0c 观察这三种循环的差异 范例代码 先贴出三种循环的代码 xff0
  • c++读取yolov5模型进行目标检测(读取摄像头实时监测)

    文章介绍 本文是篇基于yolov5模型的一个工程 xff0c 主要是利用c 43 43 将yolov5模型进行调用并测试 xff0c 从而实现目标检测任务 任务过程中主要重点有两个 xff0c 第一 版本问题 xff0c 第二配置问题 一
  • ROS初学(二):ROS的三种基本通信机制(来自赵虚左老师)

    一 综述 ROS引入通信机制 xff0c 是为了实现ROS节点 xff08 进程 xff09 之间的通信 利用ROS进程的分布式框架 xff0c 可以使得每个进程独立的工作 xff0c 甚至分布于不同的主机工作 机器人上的各种传感器 xff
  • webpack 5.5.1 compiled with 1 error in 63 ms

    新建项目空白目录 xff0c 并运行 npm init y 命令 xff0c 初始化包管理配置文件 package json 新建 src 源代码目录 新建 src gt index html 首页和 src gt index js 脚本文
  • TypeError: Class constructor ServeCommand cannot be invoked without ‘new‘

    安装webpack插件 webpack dev server配置时将package json里面webpack改为webpack serve后 xff0c 运行npm run 脚本名 时出现 TypeError Class construc
  • Field ‘browser‘ doesn‘t contain a valid alias configuration

    意思是字段 39 browser 39 不包含有效的别名配置 意思就是说你打包的css路径不对 这个是我的代码 这里js里面导入的css路径不对 xff0c css文件夹不是和index js平级 xff0c 应该是上级所以正确的代码应该是
  • Failed to load resource: the server responded with a status of 404 (Not Found)

    问题场景 xff1a 编写路由模块 解决方法 xff1a 1 先检测路径是否有写错 2 将vscode中开的插件vetur关闭 3 上面方法不管用时 xff0c 将浏览器插件关闭 xff0c 例如油猴 有什么错误望大佬指出 xff01 xf
  • Vue3.js【未完成】

    Vue3 js 如何关闭烦人的vscode的提示框 https blog csdn net liuyuemozhu article details 101056556 ES6模块化与异步编程高级用法 ES6模块化 1 回顾 xff1a no
  • npm ERR! code ERESOLVEnpm ERR! ERESOLVE unable to resolve dependency tree

    npm下载报错 xff0c 这个是因为npm版本问题 xff0c 例如我下载element ui运行npm i element ui S 因为版本问题我就无法下载 xff0c 解决办法就是在末尾加上 legacy peer deps 即np
  • 前端面试题

    浏览器 输入一个URL到页面过程中发生了什么 xff08 高频 xff09 首先在浏览器中输入URL 查找缓存 xff1a 浏览器先查看浏览器缓存 系统缓存 路由缓存中是否有该地址页面 xff0c 如果有则显示页面内容 如果没有则进行下一步
  • MATALAB动态爱心代码

    第一步 xff1a 先下载MATLAB软件 xff08 该代码只适用于此软件 xff0c 要下载软件才可运行 xff09 第二步 xff1a 点击主页 新建脚本 xff08 可自己给脚本命名 xff0c 我命名的是aixin xff09 第
  • Linux相关博文中使用的头文件

    收录博文中看到的已经封装好的文件时 xff0c 方便各位查看 Log hpp 日志信息 pragma once include lt iostream gt include lt cstdio gt include lt cstdarg g
  • 构造函数和析构函数执行顺序

    析构函数运行顺序 xff1a 1 主函数结束 xff1b 2 函数调用结束 xff1b 3 用delete删除 xff08 new创建的 xff09 对象时 注释代码运行结果 输入坐标6 xff0c 3 xff0c 6 xff0c 2 xf
  • 27.【C/C++ 最全vector数组的用法 (详解)】

    vector动态数组 xff08 一 xff09 什么是vector xff1f xff08 二 xff09 vector的作用是什么 xff08 三 xff09 经典用法 1 vector函数的定义 代码展示 效果展示 2 vector的
  • 析构函数运行顺序

    全局变量 xff0c 静态局部变量 xff0c 局部变量空间的堆分配和栈分配 其中全局变量和静态局部变量时从 静态存储区中划分的空间 xff0c 二者的区别在于作用域的不同 xff0c 全局变量作用域大于静态局部变量 xff08 只用于声明
  • 03_临界段的保护

    一 临界段的概念 所谓的临界段就是在执行时不能被中断的代码段 在FreeRTOS中 xff0c 临界段最常出现的地方就是对全局变量的操作 那么什么情况下临界段会被打断 xff1f 一个是系统调度 xff0c 还有一个是外部中断 在FreeR