FreeRTOS个人笔记-临界值的保护

根据个人的学习方向,学习FreeRTOS。由于野火小哥把FreeRTOS讲得比较含蓄,打算在本专栏尽量细化一点。作为个人笔记,仅供参考或查阅。

配套资料:FreeRTOS内核实现与应用开发实战指南、野火FreeRTOS配套视频源码、b站野火FreeRTOS视频。搭配来看更佳哟!!!   


临界值的保护

临界段简单说明:一段在指向的时候不能被中断的代码段。

那么什么情况下临界段会被打断?

系统调度。在FreeRTOS,系统调度最终也是产生PendSV中断,在PendSV Handler里面实现任务的切换。(实际上也是中断)

外部中断。

所以希望临界段是否被中断取决于中断。

中断一打开,临界值可以被中断。

中断一关闭,临界值不可以被中断。

在初谈CM3内核时,有讲过PRIMASK、FAULTMASK和BASEPRI这三个特殊寄存器。FreeRTOS中断与它们有关。

关中断

FreeRTOS关中断的函数分不带返回值和带返回值两种。

关中断不带返回值

#define portDISABLE_INTERRUPTS()	vPortRaiseBASEPRI()

//不带返回值的关中断函数,不能嵌套,不能在中断里面使用
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
	uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;//待写入BASEPRI的值,即新值,优先级的阀门

	__asm
	{
		msr basepri, ulNewBASEPRI
		dsb
		isb
	}//将configMAX_SYSCALL_INTERRUPT_PRIORITY的值写入BASEPRI寄存器,实现部分关中断。(依据阀门判断)
}

在往BASEPRI写入新值时,不用先将BASEPRI的旧值保存起来,即不管当前中断状态,所以自然不能在中断里面调用。

关中断带返回值

#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的旧值返回,返回的值作为形参传入开中断函数。

开中断

//不带中断保护的开中断函数,与portDISABLE_INTERRUPTS()成对使用
#define portENABLE_INTERRUPTS()					vPortSetBASEPRI( 0 )

//带中断保护的开中断函数,与portSET_INTERRUPT_MASK_FROM_ISR()成对使用
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)	vPortSetBASEPRI(x)

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

开关中断函数我们已经有了,那么我们可以计划了解进入/退出临界值

进入临界值

进入临界值,不带中断保护版本,不能嵌套

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

void vPortEnterCritical( void )
{
	portDISABLE_INTERRUPTS();
	uxCriticalNesting++;            //临界段嵌套计数器,默认初始化0xaaaa aaaa
    
	if( uxCriticalNesting == 1 )    //即一层嵌套
	{
        //要确保当前没有中断活动,即内核外设SCB中的中断和控制寄存器SCB_ICSR的低8位为0
		configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
	}
}

临界段嵌套计数器在调度器启动时会被重新初始化为0:

vTaskStartScheduler()->xPortStartScheduler()->uxCriticalNesting = 0。

进入临界值,带中断保护版本,能嵌套

实际上就是上面的关中断带返回值函数。即要想进入临界值,关带返回值中断。

/* 在 task.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()

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

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

实际上就是上面的开中断带返回值函数。即要想退出临界值,开带返回值中断。

/* 在 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)

临界值的应用

在FreeRTOS中,对临界值的保护出现在两种场合。

中断场合

非中断场合

中断场合的临界值应用

/* 在中断场合,临界段可以嵌套 */
{
    uint32_t ulReturn;

    /* 进入临界段,临界段可以嵌套 */
    ulReturn = taskENTER_CRITICAL_FROM_ISR();

    /* 临界段代码 */

    /* 退出临界段 */
    taskEXIT_CRITICAL_FROM_ISR( ulReturn );
}

非中断场合的临界值应用

/* 在非中断场合,临界段不能嵌套 */
{
    /* 进入临界段 */
    taskENTER_CRITICAL();

    /* 临界段代码 */

    /* 退出临界段*/
    taskEXIT_CRITICAL();
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值