FreeRTOS简单内核实现4 临界段
0、思考与回答
0.1、思考一
为什么需要临界段?
有时候我们需要部分代码一旦这开始执行,则不允许任何中断打断,这段代码称为临界段
0.2、思考二
如何实现临界段?
- 关中断
- 执行临界区代码
- 开中断
0.3、思考三
对于 Cotex-M4 内核的处理器如何方便的控制其中断开关?
使用 BASEPRI 寄存器,当该寄存器中的值不为 0 时,处理器将不会处理优先级值大于或等于 BASEPRI 的任何异常,该寄存器相关信息可以在 Cortex-M4 Devices Generic User Guide 手册中找到,具体如下图所示
![](https://images.cnblogs.com/cnblogs_com/blogs/814236/galleries/2416638/o_240818023951_Kernel_4.1.png)
值得注意的是 STM32 的 BASEPRI 寄存器做了一些修改,只使用了其高 4 位,低 4 位的数据没有使用,所以对于 STM32 在使用 BASEPRI 寄存器对中断进行屏蔽时,需要考虑到写入的高 4 位数据才是正确的数据,感兴趣的可以阅读 为何修改BASEPRI寄存器无效? 这篇文章
1、关中断
1.1、带返回值
Keil 版本
/* portMacro.h */ #define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI() // 带返回值关中断,将当前中断状态作为返回值返回 static __inline uint32_t ulPortRaiseBASEPRI(void) { uint32_t ulReturn,ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; __asm { mrs ulReturn,basepri // 保存中断时 BASEPRI 寄存器的值 msr basepri,ulNewBASEPRI // 屏蔽 优先级值 大于等于 11 的中断 dsb isb } return ulReturn; }
CLion 版本
static __inline uint32_t ulPortRaiseBASEPRI(void) { uint32_t ulReturn,ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; __asm volatile ( "mrs ulReturn,basepri \n" "msr basepri,ulNewBASEPRI \n" "dsb \n" "isb \n" ); return ulReturn; }
1.2、不带返回值
Keil 版本
/* portMacro.h */ #define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() // 不带返回值关中断 static __inline void vPortRaiseBASEPRI(void) { uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; __asm { msr basepri,ulNewBASEPRI dsb isb } }
CLion 版本
static __inline void vPortRaiseBASEPRI(void) { uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; __asm volatile ( "msr basepri, %0 \n" "isb \n" : : "r" (ulNewBASEPRI) : "memory" ); }
2、开中断
Keil 版本
/* portMacro.h */ // 设置 BASEPRI 为 0 开所有中断 #define portENABLE_INTERRUPTS() vPortSetBASEPRI(0) // 设置 BASEPRI 为进入中断时的值则恢复原来的中断状态 #define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x) // 开中断 static __inline void vPortSetBASEPRI(uint32_t ulBASEPRI) { __asm volatile { msr basepri,ulBASEPRI } }
CLion 版本
static __inline void vPortSetBASEPRI(uint32_t ulBASEPRI) { __asm volatile ( "msr basepri, %0 \n" : "r" (ulBASEPRI) : "memory" ); }
3、临界段
/* task.h */ #define taskENTER_CRITICAL() portENTER_CRITICAL() #define taskEXIT_CRITICAL() portEXIT_CRITICAL() #define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR() #define taskEXIT_CRITICAL_FROM_ISR(x) portCLEAR_INTERRUPT_MASK_FROM_ISR(x)
/* portMacro.h */ extern void vPortEnterCritical(void); extern void vPortExitCritical(void); #define portENTER_CRITICAL() vPortEnterCritical() #define portEXIT_CRITICAL() vPortExitCritical()
/* port.c */ // 中断嵌套计数器 static UBaseType_t uxCriticalNesting = 0xAAAAAAAA; // 进入临界区 void vPortEnterCritical(void) { portDISABLE_INTERRUPTS(); uxCriticalNesting++; if(uxCriticalNesting==1) { // configASSERT((portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK) == 0); } } // 退出临界区 void vPortExitCritical(void) { // configASSERT(uxCriticalNesting); uxCriticalNesting--; if(uxCriticalNesting == 0) { portENABLE_INTERRUPTS(); } }
4、应用
普通场合
// 进入临界区直接屏蔽优先级号大于 11 的中断 taskENTER_CRITICAL(); // 退出时直接设置 BASEPRI 寄存器的值为 0 taskEXIT_CRITICAL();
中断场合
uint32_t ulReturn; // 进入临界区前保存 BASEPRI 寄存器的值 ulReturn = taskENTER_CRITICAL_FROM_ISR(); // 退出临界区时恢复 BASEPRI 寄存器的值 taskEXIT_CRITICAL_FROM_ISR(ulReturn);
合集:
FreeRTOS简单内核实现
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!