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 寄存器 来实现中断开关:
代码对应的汇编指令:
// ENTER_CRITICAL() 实际生成的汇编指令CPSID I // 关闭中断(设置 PRIMASK = 1)
// EXIT_CRITICAL() 实际生成的汇编指令CPSIE I // 打开中断(清除 PRIMASK = 0)
2. 关闭的中断范围
关闭的中断:所有可屏蔽中断(Maskable Interrupts)。
不关闭的中断:不可屏蔽中断(NMI, Non-Maskable Interrupt)。
3. 关键特性
(1) 全局开关
(2) 嵌套安全
示例:
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)仍可能触发
(3) 优先级反转风险
6. 替代方案(按需选择)
(1) BASEPRI 寄存器
__set_BASEPRI(0x80) //关闭优先级≥7的中断。

(2) 操作系统提供的锁
总结
- 使用优先级屏蔽(BASEPRI)仅关闭低优先级中断(适合有严格实时性要求的场景)。
- 使用操作系统提供的锁
作者:鵬止裔羽