STM32中DMA传输中断功能的关闭方法详解

一、为什么我们要关中断功能呢?

        1.在撰写类似DMA相关串口传输程序时,我们会使用到队列指针的操作,而这个操作绝对不可以被任何行为打断,打断就寄。

       2. 因此我们要在进行队列指针的数据处理时,将全局的中断全部关闭。(很快,就一瞬间开关一下)

3.举个例子:

        例如在某一串口传输函数中,以下操作需原子性:

ENTER_CRITICAL();
if (队列未满) 
{
    txHead = next_head; // 更新队列头
    if (UART空闲)
     {
        启动DMA发送(); // 更新txTail和isUART3Busy
     }
}
EXIT_CRITICAL();

        若此处不关闭中断,DMA完成回调可能在更新 txHead 后立即触发,导致 txTail 与 txHead 不一致。

二、那么我们怎么做呢,答:使用以下代码

#define ENTER_CRITICAL() __disable_irq() // 关闭中断(进入临界区)
#define EXIT_CRITICAL() __enable_irq() // 打开中断(退出临界区)  

1. 底层实现

这两个宏通过操作ARM Cortex-M处理器的
PRIMASK 寄存器 来实现中断开关:

  • __disable_irq():将PRIMASK寄存器设置为1,禁止所有可屏蔽中断。
  • __enable_irq():将PRIMASK寄存器设置为0,重新允许中断。
  • 代码对应的汇编指令:

    // ENTER_CRITICAL() 实际生成的汇编指令CPSID I   // 关闭中断(设置 PRIMASK = 1)
    // EXIT_CRITICAL() 实际生成的汇编指令CPSIE I   // 打开中断(清除 PRIMASK = 0)

    2. 关闭的中断范围

  • 关闭的中断:所有可屏蔽中断(Maskable Interrupts)。

  • 包括GPIO中断、定时器中断、UART中断、DMA中断等用户自定义中断。
  • 不关闭的中断不可屏蔽中断(NMI, Non-Maskable Interrupt)。

  • 例如:硬件故障、看门狗复位等关键事件触发的中断。

  • 3. 关键特性

    (1) 全局开关

  • 关闭的是所有可屏蔽中断,而不是某个特定中断。
  • 无论中断优先级高低,只要属于可屏蔽中断,都会被禁止。
  • (2) 嵌套安全

  • 如果多次调用 ENTER_CRITICAL(),只有第一次调用会生效(PRIMASK已为1)。
  • 示例:

    ENTER_CRITICAL();  // PRIMASK = 1(关闭中断)
    ENTER_CRITICAL();  // PRIMASK仍为1(无操作)// 操作共享资源...
    EXIT_CRITICAL();   // PRIMASK = 0(重新允许中断)

    4. 适用场景

  • 保护共享资源:在操作全局变量(如队列指针txHead、txTail)或外设寄存器时,确保操作原子性。

  • 避免数据竞争:防止主程序与中断服务程序(ISR)同时修改同一数据。

  • 示例:

    void u1_printf_nonblocking(...)
    {    ENTER_CRITICAL(); // 关闭中断    
    // 操作队列指针txHead和txTail    
         EXIT_CRITICAL();  // 恢复中断
    }

    5. 注意事项

    (1) 临界区应尽量简短

  • 关闭中断会导致系统无法响应外部事件,长时间关闭可能影响实时性。
  • 避免在临界区内执行复杂操作(如循环、延时)。
  • (2) 不可屏蔽中断(NMI)仍可能触发

  • 如果代码需要完全避免中断打断(例如关键硬件操作),需结合其他机制(如关闭NMI)。
  • (3) 优先级反转风险

  • 若在低优先级中断中调用 ENTER_CRITICAL(),可能导致高优先级中断被延迟。

  • 6. 替代方案(按需选择)

    (1) BASEPRI 寄存器

  • 仅关闭优先级低于某个阈值的中断,而不是全部。
  • 示例:
  • __set_BASEPRI(0x80) //关闭优先级≥7的中断。

    (2) 操作系统提供的锁

  • 在使用RTOS(如FreeRTOS)时,优先使用任务调度锁(vTaskSuspendAll())代替直接开关中断。
  • 或者使用RTOS提供的互斥锁(如FreeRTOS的 taskENTER_CRITICAL())

  • 总结

  • __disable_irq() 和 __enable_irq() 通过设置PRIMASK寄存器关闭所有可屏蔽中断。
  • 关闭中断的核心目的:确保对共享资源的操作是原子性的,避免数据竞争。
  • 全局中断关闭(PRIMASK):简单粗暴但有效,适合极短时间的临界区保护。
  • 不关闭中断的替代方案:
    1. 使用优先级屏蔽(BASEPRI)仅关闭低优先级中断(适合有严格实时性要求的场景)。
    2. 使用操作系统提供的锁

     

     

     

    作者:鵬止裔羽

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32中DMA传输中断功能的关闭方法详解

    发表回复