> 技术文档 > STM32中如何关闭中断功能(DMA传输为例)_stm32关闭所有中断

STM32中如何关闭中断功能(DMA传输为例)_stm32关闭所有中断


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

        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的中断。

STM32中如何关闭中断功能(DMA传输为例)_stm32关闭所有中断

(2) 操作系统提供的锁

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

总结

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