遇玷 发表于 2025-6-8 13:21:04

FreeRTOS简单内核实现4 临界段

@
目录

[*]0、思考与回答

[*]0.1、思考一
[*]0.2、思考二
[*]0.3、思考三

[*]1、关中断

[*]1.1、带返回值
[*]1.2、不带返回值

[*]2、开中断
[*]3、临界段
[*]4、应用

0、思考与回答

0.1、思考一

为什么需要临界段?
有时候我们需要部分代码一旦这开始执行,则不允许任何中断打断,这段代码称为临界段
0.2、思考二

如何实现临界段?

[*]关中断
[*]执行临界区代码
[*]开中断
0.3、思考三

对于 Cotex-M4 内核的处理器如何方便的控制其中断开关?
使用 BASEPRI 寄存器,当该寄存器中的值不为 0 时,处理器将不会处理优先级值大于或等于 BASEPRI 的任何异常,该寄存器相关信息可以在 Cortex-M4 Devices Generic User Guide 手册中找到,具体如下图所示

值得注意的是 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);
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: FreeRTOS简单内核实现4 临界段