FreeRTOS临界段代码保护和任务调度器的挂起与恢复学习

2024-04-04 17:12

本文主要是介绍FreeRTOS临界段代码保护和任务调度器的挂起与恢复学习,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

FreeRTOS临界段代码保护和任务调度器的挂起与恢复学习

临界段代码保护

所谓临界段代码保护就是指必须完成运行,不能被打断的代码段。比如需要严格按照时序除初始化的外设:IIC、SPI,再或者因为系统自身需求和用户需求。
FreeRTOS 在进入临界段代码的时候需要关闭中断,当处理完临界段代码以后再打开中断。也就是临界区屏蔽了中断,系统调度靠中断,ISR也依靠中断。
在这里插入图片描述
具体调用如上述函数,
临界段代码保护具有以下特点:
1:成对使用
2:支持嵌套
3:要保持临界区代码内容短

任务调度器的挂起和恢复

挂起任务调度器,不需要关闭中断。具体函数为:
在这里插入图片描述

1:它仅仅是防止了任务之间的资源争夺,中断照样可以直接响应;
2:与临界区不一样的是,挂起任务调度器,未关闭中断;
3:挂起调度器的方式,适用于临界区位于任务与任务之间;既不用去延时中断,又可以做到临界区的安全
下面具体来看代码实现:

临界段代码保护内容理解

首先看临界区代码保护的。

void vPortEnterCritical( void )
{portDISABLE_INTERRUPTS();//关闭中断函数uxCriticalNesting++;/*在已经进入一个临界区的情况下再次进入另一个(或相同的)临界区的情况。这在多层函数调用中尤其常见,其中一个函数调用了另一个函数,两者都需要访问共享资源。在这种情况下,为了避免离开第一个临界区时意外地使整个系统对中断敏感,需要跟踪嵌套的深度。一旦嵌套深度回到零,系统才会重新启用中断。*//* This is not the interrupt safe version of the enter critical function so* assert() if it is being called from an interrupt context.  Only API* functions that end in "FromISR" can be used in an interrupt.  Only assert if* the critical nesting count is 1 to protect against recursive calls if the* assert function also uses a critical section. */if( uxCriticalNesting == 1 ){configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );}/*该段代码中的if语句检查是否是第一次进入临界区(uxCriticalNesting == 1),如果是,则执行一个断言(configASSERT)来确保当前不处于中断服务例程(ISR)中。这是因为中断服务例程中不应该调用非从ISR版本的API函数。断言检查的是portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK的结果是否为0,这个操作是用来检测当前是否有激活的中断:*/
}

下面我们来看下portDISABLE_INTERRUPTS()这个函数的具体内容;

/*-----------------------------------------------------------*/static portFORCE_INLINE void vPortRaiseBASEPRI( void ){uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;__asm{/* Set BASEPRI to the max syscall priority to effect a critical* section. */
/* *INDENT-OFF* */msr basepri, ulNewBASEPRIdsbisb
/* *INDENT-ON* */}}
/*-----------------------------------------------------------*/

basepri这个寄存器上一节中断管理提过这个寄存器,用来屏蔽中断的。在这里插入图片描述
这里就是把5到15的中断全部关闭了。因为FreeRTOS管理的中断范围就是5-15。

void vPortExitCritical( void )
{configASSERT( uxCriticalNesting );uxCriticalNesting--;if( uxCriticalNesting == 0 ){portENABLE_INTERRUPTS();}
}

下面我们来看下portENABLE_INTERRUPTS()这个函数的具体内容:

/*-----------------------------------------------------------*/static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI ){__asm{/* Barrier instructions are not used as this function is only used to* lower the BASEPRI value. */
/* *INDENT-OFF* */msr basepri, ulBASEPRI
/* *INDENT-ON* */}}
/*-----------------------------------------------------------*/

在这里插入图片描述
ulBASEPRI入口参数设置为0就是不关闭任何中断。
uxCriticalNesting这个变量就是用于嵌套调用临界区时候使用的变量。这里有这样的一个例子。

void doSomethingElse() {vPortEnterCritical(); // 进入临界区// 对共享资源进行一些操作vPortExitCritical(); // 退出临界区
}void doSomething() {vPortEnterCritical(); // 进入临界区// 对共享资源进行一些操作doSomethingElse(); // 调用另一个需要保护的函数// 可能还有更多操作vPortExitCritical(); // 退出临界区
}void mainFunction() {doSomething(); // 调用需要保护的函数
}

在这个例子中,当mainFunction调用doSomething时,我们首先进入一个临界区,此时uxCriticalNesting从0变为1。然后,在doSomething函数内部,我们调用了doSomethingElse,它也试图进入临界区。由于我们已经在一个临界区内,uxCriticalNesting此时会增加到2,而不是尝试禁用中断,因为它们已经被禁用了。当doSomethingElse完成其操作并调用vPortExitCritical时,它会减少uxCriticalNesting的值,将其从2变回1,但不会重新启用中断,因为我们仍然处于外部doSomething函数的临界区内。只有当doSomething也完成其操作,调用vPortExitCritical,并将uxCriticalNesting减少到0时,中断才会被重新启用。

任务调度器的挂起和恢复代码理解

void vTaskSuspendAll( void )
{/* A critical section is not required as the variable is of type* BaseType_t.  Please read Richard Barry's reply in the following link to a* post in the FreeRTOS support forum before reporting this as a bug! -* https://goo.gl/wu4acr *//* portSOFTWARE_BARRIER() is only implemented for emulated/simulated ports that* do not otherwise exhibit real time behaviour. */portSOFTWARE_BARRIER();/* The scheduler is suspended if uxSchedulerSuspended is non-zero.  An increment* is used to allow calls to vTaskSuspendAll() to nest. */++uxSchedulerSuspended;/* Enforces ordering for ports and optimised compilers that may otherwise place* the above increment elsewhere. */portMEMORY_BARRIER();
}

在这里插入图片描述
具体过程如上图所示。

这篇关于FreeRTOS临界段代码保护和任务调度器的挂起与恢复学习的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/876369

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

MySQL数据库函数之JSON_EXTRACT示例代码

《MySQL数据库函数之JSON_EXTRACT示例代码》:本文主要介绍MySQL数据库函数之JSON_EXTRACT的相关资料,JSON_EXTRACT()函数用于从JSON文档中提取值,支持对... 目录前言基本语法路径表达式示例示例 1: 提取简单值示例 2: 提取嵌套值示例 3: 提取数组中的值注意

CSS3中使用flex和grid实现等高元素布局的示例代码

《CSS3中使用flex和grid实现等高元素布局的示例代码》:本文主要介绍了使用CSS3中的Flexbox和Grid布局实现等高元素布局的方法,通过简单的两列实现、每行放置3列以及全部代码的展示,展示了这两种布局方式的实现细节和效果,详细内容请阅读本文,希望能对你有所帮助... 过往的实现方法是使用浮动加

JAVA调用Deepseek的api完成基本对话简单代码示例

《JAVA调用Deepseek的api完成基本对话简单代码示例》:本文主要介绍JAVA调用Deepseek的api完成基本对话的相关资料,文中详细讲解了如何获取DeepSeekAPI密钥、添加H... 获取API密钥首先,从DeepSeek平台获取API密钥,用于身份验证。添加HTTP客户端依赖使用Jav

Java实现状态模式的示例代码

《Java实现状态模式的示例代码》状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来... 目录一、简介1、定义2、状态模式的结构二、Java实现案例1、电灯开关状态案例2、番茄工作法状态案例

nginx-rtmp-module模块实现视频点播的示例代码

《nginx-rtmp-module模块实现视频点播的示例代码》本文主要介绍了nginx-rtmp-module模块实现视频点播,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录预置条件Nginx点播基本配置点播远程文件指定多个播放位置参考预置条件配置点播服务器 192.