FreeRTOS中临界段的保护(笔记)

news2024/11/26 18:45:10

目录

    • 临界段的定义
    • Cortex-M内核快速关开关中断的指令
    • 关中断
    • 开中断
    • 进入临界段的宏
    • 退出临界段的宏
    • 进入临界段,不带中断保护, 不能嵌套
    • 进入临界段,带中断保护版本,可以嵌套
    • 退出临界段,不带中断保护版本,不能嵌套
    • 退出临界段,带中断保护版本,可以嵌套
    • 临界段代码的应用

临界段的定义

临界段是指在执行时不能被中断的代码段。在FreeRTOS中,临界段最经常出现的地方就是对全局变量的操作。

Q1:什么情况下临界段会被打断?
系统调度和外部中断。
在FreeRTOS中,系统调度最终也是产生PendSV中断,在PendSVHandler中实现任务的切换,所以还是归结为中断。
FreeRTOS对临界段的保护就是对中断的开和关的控制。

Cortex-M内核快速关开关中断的指令

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

PRIMASK 、 FAULTMAST 和 BASEPRI 是Cotex-M内核中3个中断屏蔽寄存器。

在这里插入图片描述

关中断

FreeRTOS关中断函数在portmacro.h中定义,分为带返回值和不带返回值两种。

/*不带返回值的关中断函数不能嵌套,不能在中断中使用 */
#define portDISABLE_INTERRUPTS()	vPortRaiseBASEPRI()
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. */
		msr basepri, ulNewBASEPRI
		dsb
		isb
	}
}

不带返回值的意思是,在向BASEPRI写入新值时,不用先将BASEPRI的值保存起来,即不用考虑当前中断状态是怎么样的,这意味着这样的函数不能在中断中调用。

#define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 /* 高四位有效,即等于0xb0,或者是11 */
它是在FreeConfig.h中定义的宏,即大于等于11的中断都会被屏蔽。
ps:这个值可以自己设置,实际开发中,一般设置为5.
在FreeRTOS中,凡是看到FROM_ISR结尾的函数/宏定义都是在中断中使用的。

#define portSET_INTERRUPT_MASK_FROM_ISR()		ulPortRaiseBASEPRI()
static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

	__asm
	{
		/* Set BASEPRI to the max syscall priority to effect a critical
		section. */
		mrs ulReturn, basepri
		msr basepri, ulNewBASEPRI
		dsb
		isb
	}

	return ulReturn;
}

带返回值的关中断函数可以嵌套,可以在中断中使用。带返回值的意思是,在BASEPRI写入新值时,先将BASEPRI的值先保存起来,在更新完成BASEPRI的值时,将之前保存好的BASEPRI的值返回,作为形参传入开中断函数中。

开中断

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

/*不带中断保护的开中断函数*/
#define portSET_INTERRUPT_MASK_FROM_ISR()		ulPortRaiseBASEPRI()
/*带中断保护的开中断函数*/
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)	vPortSetBASEPRI(x)
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. */
		msr basepri, ulBASEPRI
	}
}

根据ulBASEPRI 的参数,选择保护和不带中断中断保护的版本
不带中断保护的开中断函数,直接将BASEPRI设置为0,与portDISABLE_INTERRUPTS()成对使用。
带中断保护的开中断函数,将上一次关中断保存的BASEPRI的值作为形参,与portSET_INTERRUPT_MASK_FROM_ISR()成对使用

进入临界段的宏

/*进入临界段,不带中断保护版本,不能嵌套*/
/* 在task.h中定义 */
#define taskENTER_CRITICAL()		portENTER_CRITICAL()

/* 在portmacro.h中定义 */
#define portENTER_CRITICAL()		vPortEnterCritical()


/*进入临界段,带中断保护版本,可以嵌套*/
/* 在portmacro.h中定义 */
#define 	taskENTER_CRITICAL_FROM_ISR()  portSET_INTERRUPT_MASK_FROM_ISR()

/* 在portmacro.h中定义 */
#define     portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()

退出临界段的宏

/*退出临界段,不带中断保护版本,不能嵌套*/
/* 在task.h中定义 */
#define taskEXIT_CRITICAL()		portEXIT_CRITICAL()

/* 在portmacro.h中定义 */
#define portEXIT_CRITICAL()		vPortExitCritical()

/*退出临界段,带中断保护版本,可以嵌套*/
/* 在task.h中定义 */
#define 	taskEXIT_CRITICAL_FROM_ISR( x ) 	portCLEAR_INTERRUPT_MASK_FROM_ISR( x )

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

进入临界段,不带中断保护, 不能嵌套

/*task.h中定义*/
#define taskENTER_CRITICAL()  portEXIT_CRITICAL()
/*在portmacro.h中定义*/
#define portENTER_CRITICAL()		vPortEnterCritical()
/*在port.c中定义*/
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 );
	}
}
/*在portmacro.h中定义*/
#define portDISABLE_INTERRUPTS()				vPortRaiseBASEPRI()
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. */
		msr basepri, ulNewBASEPRI
		dsb
		isb
	}
}

ps: uxCriticalNesting是prot.c中定义的静态变量,表示临界段嵌套计数器,默认初始化为0xaaaaaaaa,在调度器启动时会被重新初始化为0:vTaskScheduler() -> xPortStartScheduler() -> uxCriticalNesting = 0
如果uxCriticalNesting=1,即一层嵌套,要确保当前没有中断活跃,即内核外设SCB中的中断和控制寄存器SCB_ICSR的低8位要等于0.

进入临界段,带中断保护版本,可以嵌套

/*在task.c中定义*/
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()

/*在portmacro.h中定义*/
#define portSET_INTERRUPT_MASK_ISR() u1PortRaiseBASEPRI()

static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
	uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

	__asm
	{
		/* Set BASEPRI to the max syscall priority to effect a critical
		section. */
		mrs ulReturn, basepri
		msr basepri, ulNewBASEPRI
		dsb
		isb
	}

	return ulReturn;
}

退出临界段,不带中断保护版本,不能嵌套

/*在task.c中定义*/
#define taskEXIT_CRITCAL()                      portEXIT_CRITCAL()
/*在portmacro.h中定义*/
#define portEXIT_CRITICAL()						vPortExitCritical()
/*在port.c中定义*/
void vPortExitCritical( void )
{
	//configASSERT( uxCriticalNesting );
	uxCriticalNesting--;
    
	if( uxCriticalNesting == 0 )
	{
		portENABLE_INTERRUPTS();
	}
}
/*在portmacro.h中定义*/
#define portENABLE_INTERRUPTS()					vPortSetBASEPRI( 0 )
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. */
		msr basepri, ulBASEPRI
	}
}

退出临界段,带中断保护版本,可以嵌套

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


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. */
		msr basepri, ulBASEPRI
	}
}

临界段代码的应用

/*在中断场合,临界段可以嵌套*/
{
	unit32_t ulReturn;
	/*进入临界段,临界段可以嵌套*/
	ulReturn =taskENTER_CRITCAL_FROM_ISR();
	
	/*临界段代码*/
	
	/*退出临界段*/
	taskEXIT_CRITICAL_FROM_ISR(ulReturn );

}
/*在非中断场合,临界段不能嵌套*/
{

	/*进入临界段,临界段可以嵌套*/
	taskENTER_CRITICAL();
	
	/*临界段代码*/
	
	/*退出临界段*/
	taskEXIT_CRITICAL();

}

学习于《FreeRTOS内核实现与应用开发实战指南–基于stm32》
b站视频地址:https://www.bilibili.com/video/BV1Jx411X7NS/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/423868.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构与算法】堆的实现(附源码)

目录 一.堆的概念及结构 二.接口实现 A.初始化 Heapinit 销毁 Heapdestroy B.插入 Heappush 向上调整 AdjustUp 1.Heappush 2.AdjustUp C.删除 Heappop 向下调整 AdjustDown D.堆的判空 Heapempty 堆顶数据 Heaptop 堆的大小 Heapsize 三.源码 Heap.h He…

Windows通过RDP异地远程桌面Ubuntu【内网穿透】

文章目录前言1. ubuntu安装XRDP2.局域网测试连接3. Ubuntu安装cpolar内网穿透4.cpolar公网地址测试访问5.固定域名公网地址前言 XRDP是一种开源工具,它允许用户通过Windows RDP访问Linux远程桌面。 除了Windows RDP外,xrdp工具还接受来自其他RDP客户端(…

文心一格,百度AI作画产品

文章目录AIGC什么是AI作画?Prompt文心一格使用方法注册账号使用AI绘图AIGC的未来发展结语AIGC AIGC(AI Generated Content)是指利用人工智能生成内容。是利用人工智能来生成你所需要的内容,GC的意思是创作内容。与之相对应的概念中…

ElasticSearch索引文档写入和近实时搜索

一、基本概念 1.Segments In Lucene 众所周知,ElasticSearch存储的基本单元Shard,ES中一个Index可能分为多个Shard,事实上每个Shard都是一个Lucence的Index,并且每个Lucene Index由多个Segment组成,每个Segment事实上…

【JS运算】分组求和/平均值(reduce函数)

对于数组求和的问题,使用reduce函数能够最快的解决 如果你还不会reduce函数,可以看这一篇: reduce函数的使用 思路 reduce函数对相同group的值进行迭代求和 将分组的总和除以组里的个数得到平均值,然后存储起来 Sum函数&#x…

Linux ubuntu更新meson版本

问题描述 在对项目源码用meson进行编译时,可能出现以下错误 meson.build:1:0: ERROR: Meson version is 0.45.1 but project requires > 0.58.0. 或者 meson_options.txt:1:0: ERROR: Unknown type feature. 等等,原因是meson版本跟设置的不适配。 …

Linux 学习总结(92)—— Linux 高效率使用技巧

1、跳转目录优雅顺滑 1.1 bd 命令 快速回到 Bash 中的特定父目录,而不是多余地键入 cd ../../..。如果在此路径中/home/radia/work/python/tkinter/one/two并且想快速转到目录 python,只需键入: bd python或者仅输入目录的前几个字母,如匹…

锁子甲 bulid+sim

链接: youtube 分析:洒一堆点——copy 模型——点和模型符合一定规律 点和点的距离符合上述图中的关系 (横纵) 横向 但是我们要横向10个点够了: 用modulo 除余 纵向 这里用除法向上取整 /10 eg : 0-9 得0 10-19 得1…

【逗号你真的懂吗?】C++与JAVA中逗号的区别

文章目录一、先上结论二、C中的逗号逗号运算符和逗号表达式三、JAVA中的逗号四、实战验证情况一:在定义(或声明)变量时利用逗号CJAVA情况二:在for循环条件中使用逗号CJAVA情况三:在函数形参参数列表中使用逗号CJAVA情况…

WPF_Application

文章目录Application1 Application类1.1 定义1.2 示例1.3 附注2 Application常用属性2.1 Current2.2 Dispatcher3 总结Application 1 Application类 1.1 定义 该类封装了一个WPF应用程序。 该类派生自DispatcherObject,实现了IQueryAmbient接口。 1.2 示例 以…

ICLR Spotlight | 卷积网络上的首个BERT/MAE预训练,ResNet也能用

“删除-再恢复” 形式的自监督预训练可追溯到 2016 年,早于 18 年的 BERT 与 21 年的 MAE。然而在长久的探索中,这种 BERT/MAE 式的预训练算法仍未在卷积模型上成功(即大幅超过有监督学习)。本篇 ICLR Spotlight 工作 “Designing…

PPP协议相关的知识

这只是我自己在学习时的总结,对于我有用的知识点,希望可以和大家分享,主要学习的文章如下,如有兴趣也可以去了解一下其他作者写的ppp协议的知识点。 PPP协议详解https://blog.csdn.net/m0_49864110/article/details/124987932?o…

Spring Boot 之四:使用Feign实现微服务间的交互

系列目录(持续更新。。。) Spring Cloud:什么是微服务 Spring Cloud之一:注册与发现-Eureka工程的创建 Spring Cloud之二:服务提供者注册到Eureka Server Spring Cloud之三:Eureka Server添加认证 Spr…

Talk预告 | 清华大学交叉信息研究院助理教授赵行:基于视觉感知的自动驾驶运动预测

本期为TechBeat人工智能社区第481期线上Talk! 北京时间3月15日(周三)20:00,清华大学交叉信息研究院助理教授——赵行的Talk将准时在TechBeat人工智能社区开播! 他与大家分享的主题是: “基于视觉感知的自动驾驶运动预测”,届时将…

Dish - TS:减轻时间序列预测中分布偏移的一般范式

摘要 时间序列预测中的分布偏移(TSF)指的是序列分布随时间发生变化,这很大程度上阻碍了TSF模型的性能。现有针对时间序列中分布偏移的研究主要限于分布的量化,更重要的是忽略了回视窗口和预测窗口(horizon windows&am…

Cross Attentional Audio-Visual Fusion for Dimensional Emotion Recognition阅读笔记

Abstract 摘要——多模态分析最近引起了人们对情感计算的极大兴趣,因为它可以提高情感识别相对于孤立的单模态方法的整体准确性。 最有效的多模态情绪识别技术有效地利用各种免费的信息源,例如面部、声音和生理模式,以提供全面的特征表示。 …

谷歌云服务器centos9的docker部署chat-web,实现自己的ChatGPT

谷歌云服务器centos9的docker部署chat-web,实现自己的ChatGPT 前提条件:准备一个境外服务器和chatgpt的key。(网上教程很多) 1.更新yum yum update2.下载docker-ce的repo curl https://download.docker.com/linux/centos/dock…

IDEA Eval Reset 使用方法

IDEA Eval Reset 使用方法 idea eval reset 使用方法安装插件 离线安装方式 1、下载插件 下载地址:https://plugins.zhile.io/files/ide-eval-resetter-2.1.6.zip2、安装插件直接下载插件 zip 包(macOS 可能会自动解压,然后把 zip 包丢进回收…

文旅+虚拟数字人,开启数字文旅发展新方向

随着元宇宙概念爆发,对接虚拟与现实的虚拟数字人正成为国内数字文旅发展的新突破口!无论是当地文旅机构、文旅品牌还是政府部门来说,虚拟数字人具有独特的可塑性和创新性,利用文旅虚拟数字人广泛的应用场景,把文旅虚拟…

第十九章 案例TodoList之组件拆分

光说不练假把式,接下来我们将练习一个案例TodoList,让我们熟悉react。 以上是该案例的静态示例,其功能有: 在输入框输入任务,按回车键新增一个任务项鼠标移动在单个任务项上面,出现删除按钮点击删除按钮&a…