Free RTOS学习随笔(1),临界区代码

旧城冷巷雨未停 提交于 2020-08-16 03:25:56

基本介绍

临界区代码指的是那些必须完整执行、不能被打断的代码,比如在初始化一些外设的时候,需要按照严格的时序进行,这样的代码是不能被中断打断的。
Free RTOS在进入临界区代码时,需要关闭中断,当临界区代码完整执行后再打开中断。
临界区代码一定要尽可能精简,进入临界区关闭中断后,优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断不会被及时响应。
简单的说原理嘛,就是要运行临界区代码的时候,先把全局中断关了,防止运行临界区代码的时候,被中断打断,等代码运行完了,再把全局中断打开。


Free RTOS中临界区代码常用函数

taskENTER_CRITICAL();			//任务级临界代码保护
taskEXIT_CRITICAL();	
taskENTER_CRITICAL_FROM_ISR();	//中断级临界代码保护
taskEXIT_CRITICAL_FROM_ISR();

任务级临界代码保护

调用方式

taskENTER_CRITICAL()taskEXIT_CRITICAL()是任务级的临界代码保护,顾名思义,一个是进入临界区,一个是退出临界区。在使用时,一定要成对使用,并且保证不能在临界区停留过久,进入临界区后应当尽快退出。
一般使用方式:

void taskCritical_test(void){
	while(1){
		taskENTER_CRITICAL();
		/*这里是你的任务代码*/
		taskEXIT_CRITICAL();
	}
}

实现原理

首先我们找到taskENTER_CRITICAL()taskEXIT_CRITICAL的源码,发现它是一个宏定义:

#define taskENTER_CRITICAL()		portENTER_CRITICAL()
#define taskEXIT_CRITICAL()			portEXIT_CRITICAL()

然后我们再找到这两个函数的源码:

#define portENTER_CRITICAL()					vPortEnterCritical()
#define portEXIT_CRITICAL()						vPortExitCritical()

然后发现它又是个宏定义。。。
最后我们发现函数vPortEnterCritical的源码是这样的:

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 );
	}
}
/*————————————————————————————————————————————————————————————————————————————————————*/
void vPortExitCritical( void )
{
	configASSERT( uxCriticalNesting );
	uxCriticalNesting--;
	if( uxCriticalNesting == 0 )
	{
		portENABLE_INTERRUPTS();
	}
}

其中portDISABLE_INTERRUPTS()portENABLE_INTERRUPTS()顾名思义就是关闭和开启中断,它们是通过操作basepri寄存器实现的。
另外,两个函数中都有一个变量uxCriticalNesting,这是一个全局变量,用于临界端的嵌套计数。
为什么要对嵌套进行计数呢?比如我们在关中断的代码中嵌套了一个含有开中断的函数,那么这时,中断就被打开了,这显然不是我们想要的。加上这个计数就可以防止这种情况的发生。

中断级临界代码保护

调用方式

taskENTER_CRITICAL_FROM_ISR()taskEXIT_CRITICAL_FROM_ISR()是用于中断服务程序中的函数,而且该中断的优先级必须低于configMAX_SYSCALL_INTERRUPT_PRIORITY

//定时器3中断服务函数
void TIM3_IRQHandler(void){
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)=SET){
		statusValue = taskENTER_CRITICAL_FROM_ISR();
		/*你的代码*/
		taskEXIT_CRITICAL_FROM_ISR(statusValue);
	}
}

实现原理

可以看到在示例中我们定义了一个变量statusValue,为何?我们看源码,经过两个跟上面taskENTER_CRITICAL()taskEXIT_CRITICAL差不多宏定义,我们最终追溯到源码为:

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
	}
}
/*-----------------------------------------------------------*/

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;
}

是汇编wsl我查了网上的相关博客,大概意思是说,它们也是通过操控basepri寄存器控制中断的开关。但这里也跟上面的taskENTER_CRITICAL()taskEXIT_CRITICAL有不同之处,它们并没有使用嵌套计数,而是用了另一种方法:通过保存和恢复寄存器 basepri的数值就可以实现嵌套使用。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!