拓冰建站拓冰建站
首页 / 资讯中心 / 正文

五. 临界段

什么是临界段其实就是一段在执行的时候不能被 中断 打断的代码段。那么什么情况下临界段会被打断一个是系统调度一个是外部中断。这里需要解释一下系统调度是在PendSV中完成归根结底系统调度是一个PendSV中断。因此上FreeRTOS对临界段的保护 最终还是回到 对中断的开和关的控制。只要把中断关掉某段程序在执行的时候就没法响应 该中断那么就可以对这个临界段保护当临界段执行完了再去执行该中断。这里我们复习下 上节讲的Cortex-M内核中 快速开关中断指令//CPS指令用法 CPSID I ;PRIMASK1 ;关中断 CPSIE I ;PRIMASK0 ;开中断 CPSID F ;FAULTMASK1 ;关异常 CPSIE F ;FAULTMASK0 ;开异常这里我们要说明的是CPS指令: IE中断使能/打开中断 ID中断失能/关闭中断 Iprimask F:faultmaskCortex-M内核里其实是有三个中断屏蔽寄存器它们分别是PRIMASK、FAULTMASK和BASEPRI。它们之间的描述见下表名字功能描述PRIMASK这是一个只有单一比特的寄存器。置1后关掉所有异常只留下NMI和硬FAULT可以响应。缺省值为0表示没有关中断FAULTMASK这也是个只有一个位的寄存器。置1后只有NMI可以响应所有其它的异常甚至是硬FAULT也统统闭嘴。缺省值为0表示没有关异常。BASEPRI这个寄存器最多有9位由表达优先级的位数决定。它定义了被屏蔽优先级的阈值。当它被设成某个值后所有优先级号大于等于此值的中断都被关优先级号越大优先级越低。若被设成0则不关闭任何中断缺省值为0.实际上FreeRTOS中对中断的开和关是通过操作BASEPRI寄存器实现的。凡是大于或者等于BASEPRI值的中断都不会被响应小于BASEPRI值的中断不会被屏蔽为什么要使用BASEPRI是为了处理紧急的事情。这里我们举一个四轴飞行器的例子他对实时性要求很高假如MPU6050值要实时交给系统关中断的时间不能太久四轴防撞墙需要实时响应就可以设置防撞墙的优先级更高一点让它屏蔽中断时候是无法屏蔽防撞墙的中断响应就可以了。下面介绍关中断和开中断它们分别有不带返回值和带返回值的定义见下面代码//关中断 在portmacro.h中定义 分不带返回值和带返回值两种 /* 不带返回值的关中断函数不能嵌套不能在中断里面使用 */ #define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() static portFORCE_INLINE void vPortRaiseBASEPRI(void) { uint32_t ulNewBASEPRI configMAX_SYSCALL_INTERRUPT_PRIORITY; __asm { msr basepri,ulNewBASEPRI dsb isb } } /* 带返回值的关中断函数可以嵌套可以在中断里面使用 */ #define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI() ulPortRaiseBASEPRI(void) { uint32_t ulReturn, ulNewBASEPRI configMAX_SYSCALL_INTERRUPT_PRIORITY; __asm { mrs ulReturn, basepri msr basepri, ulNewBASEPRI dsb isb } return ulReturn; }这里我们要知道一个小知识点凡是看到FROM_ISR结尾的函数或者宏定义它都是在中断中使用的没有为什么这只是FreeRTOS的一种命名以上代码有一个宏定义configMAX_SYSCALL_INTERRUPT_PRIORITY,在FreeRTOSConfig.h中定义是一个要写入BASEPRI中的值。该宏默认为191高4位有效即0xb0高4位也就是11也就是说优先级大于11的中断都会被屏蔽11以内的中断都可以正常响应。//开中断 在portmacro.h中定义 分不带中断保护和带中断保护两种 /* 不带中断保护的开中断函数 */ #define portENABLE_INTERRUPTS() vPortSetBASEPRI(0) /* 带中断保护的开中断函数 */ #define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x) void vPortSetBASEPRI(uint32_t ulBASEPRI) { __asm { msr basepri,ulBASEPRI } }根据对 开中断代码 的理解我们知道开中断的函数就是将传进来的形参更新到BASEPRI寄存器中那么根据传进来的形参不同分为开中断保护和不带开中断保护版本当传进来的参数是0时表示不带开中断保护此时BASEPRI的值为0表示不关闭任何中断当传进来的值是上一次关中断时保存的BASEPRI的值则表示开中断保护。关中断ulPortRaiseBASEPRI //设置11 返回0 会响应5 6 7 8ulPortRaiseBASEPRI //设置5 返回11 不会响应5 6 7 8开中断vPortSetBASEPRI(11) //传入11 此时basepri值11 会响应5 6 7 8vPortSetBASEPRI(0) //关闭所有屏蔽 打开所以中断对“进入临界段”和“退出临界段”进行学习理解它进入临界段 也分为不带中断保护版本和带中断保护版本。其中不带中断保护版本不能嵌套。具体代码如下/* 进入临界段不带中断保护版本不能嵌套 */ /* 以下代码仅供理解使用不代表在同一个代码页中 */ /* 在task.h中定义 */ #define taskENTER_CRITICAL portENTER_CRITICAL() /* 在portmacro.h中定义 */ #define portENTER_CRITICAL() vPortEnterCirtical() /* 在port.c中定义 */ void vPortEnterCritical(void) { portDISABLE_INTERRUPTS(); uxCriticalNesting;//(1) if(uxCriticalNesting 1)//(2) { //configASSERT((portNVIC_INT_CTRL_REG portVECTACTIVE_MASK) 0); } }对以上代码需要解释的是1uxCriticalNesting在port.c中定义的静态变量表示的是临界段嵌套计数器默认初始值为0xaaaaaaaa在调度器他会被重新初始化为0vTaskStartScheduler()-xPortStartScheduler()-uxCriticalNesting 0.2如果uxCriticalNesting1即一层嵌套要确保当前没有中断活跃即内核外设SCB中的中断和控制寄存器SCB_ICSR的低8位要等于0.带中断保护版本可以嵌套。具体代码如下/* 进入临界段带中断保护版本可以嵌套 */ /* 以下代码仅供理解使用不代表在同一个代码页中 */ /* 在task.h中定义 */ #define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR() /* 在portmacro.h中定义 */ #define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI() /* 在portmacro.h中定义 */ 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; }那么怎么退出临界段呢也分为不带中断保护和带中断保护版本。/* 退出临界段不带中断保护版本不能嵌套 */ /* 以下代码仅供理解使用不代表在同一个代码页中 */ /* 在task.h中定义 */ #define taskEXIT_CRITICAL() portEXIT_CRITICAL() /* 在portmacro.h中定义 */ #define portEXIT_CRITICAL() vPortExitCritical() /* 在port.c中定义 */ void vPortExitCritical(void) { configASSERT(uxCriticalNesting); uxCriticalNesting--; if(uxCriticalNesting 0) { portENABLE_INTERRUPT(); } }/* 退出临界段带中断保护版本可以嵌套 */ /* 以下代码仅供理解使用不代表在同一个代码页中 */ /* 在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) /* 在portmacro.c中定义 */ static portFORCE_INLINE void vPortSetBASEPRI(uint32_t ulBASEPRI) { __asm { msr basepri,ulBASEPRI } }那么我们可以在哪里使用临界段呢在FreeRTOS中对临界段的保护会出现在以下两个场合中一种是在中断场合中一种是在非中断场合。具体代码见下面/* 在中断场合临界段可以嵌套 */ { uint32_t ulReturn; /* 进入临界段临界段可以嵌套 */ ulReturn taskENTER_CRITICAL_FROM_ISR(); /* 临界段代码 */ /* 退出临界段 */ taskEXIT_CRITICAL_FROM_ISR(ulReturn); } /* 在非中断场合临界段不能嵌套 */ { /* 进入临界段临界段可以嵌套 */ taskENTER_CRITICAL(); /* 临界段代码 */ /* 退出临界段 */ taskEXIT_CRITICAL(); }本章内容要充分理解这将会为后面的内容做好铺垫
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门