4. DMA 中断机制:三大中断类型详解
DMA 中断,说白了就是硬件给你发的一个“信号”。它告诉你:活干完了、出错了、或者干到一半了。
我个人习惯把 DMA 中断分成三类:完成中断、错误中断、半传输中断。每种中断的触发时机不同,处理方式也不同。搞不清楚它们,调试时就容易抓瞎。
4.1 完成中断 —— 最常用的那个
这是最常见的中断类型。当 DMA 把设定的数据量全部传输完毕后,硬件就会触发这个中断。
我很多年前调试一个高速数据采集卡时,就遇到过一个典型的完成中断问题。板卡每采集完一帧数据,DMA 就触发完成中断,CPU 在中断里把数据搬到应用程序缓冲区。代码逻辑看着没问题,但一跑起来就丢数据。
后来发现,中断处理函数里做了太多事——又是拷贝数据,又是更新队列指针,还顺带打印调试信息。中断处理时间过长,导致下一个 DMA 传输完成时,上一个中断还没处理完。嗯,这就是典型的“中断丢失”。
4.2 错误中断 —— 不能忽视的信号
错误中断,说白了就是 DMA 干活时出岔子了。常见的错误类型有:
- 总线错误:DMA 访问了无效的地址
- 对齐错误:地址或长度没有按硬件要求对齐
- 传输超时:总线一直拿不到控制权
- 描述符错误:链式传输时,描述符链表出了问题
我曾经在一个项目中犯过这样的错:用 DMA 搬运数据时,源地址指向了一个已经释放的缓冲区。结果 DMA 一启动,立刻触发总线错误中断。系统直接 panic,所有同事都盯着我。
那次之后我养成一个习惯:注册一个错误中断的处理函数,哪怕暂时什么都不做,也要先把它打印出来。否则出了问题,你连从哪里查都不知道。
4.3 半传输中断 —— 双缓冲区的关键
半传输中断,名字很直白——DMA 传输了一半数据时触发。这玩意主要用在音频、视频这类需要“流式”处理的场景。
你想想看,如果一个音频播放程序,要等 DMA 把整个音频文件都搬完才开始播放,那延迟得多大?用户听到的会是“咔咔”的爆音。
有了半传输中断,就可以这么玩:
- 把缓冲区分成两个半区:A 区和 B 区
- DMA 填满 A 区后,触发半传输中断
- CPU 在中断里处理 A 区的数据,同时 DMA 继续填 B 区
- DMA 填满 B 区后,触发完成中断
- CPU 处理 B 区数据,DMA 又回头填 A 区
这样,CPU 和 DMA 就像两个流水线工人,交替工作,永远不会互相等待。
4.4 三种中断类型的对比
为了让你更直观地理解,我整理了一个对比表:
| 中断类型 | 触发时机 | 典型应用场景 | 注意事项 |
|---|---|---|---|
| 完成中断 | 全部数据传输完毕 | 文件传输、内存拷贝 | 处理函数要轻量,避免中断嵌套 |
| 错误中断 | 总线错误、对齐错误等 | 异常处理、系统保护 | 必须清除错误标志,否则死循环 |
| 半传输中断 | 数据传输至一半时 | 音频流、视频流、数据采集 | 缓冲区大小要匹配处理速度 |
4.5 中断优先级与嵌套问题
讲到这里,必须提一下中断优先级。为什么?因为有时候中断之间会“打架”。
假设你同时使能了半传输中断和完成中断。DMA 先触发半传输中断,你正在处理,结果传输完成了,又触发了完成中断。如果完成中断的优先级更高,它就会打断半传输中断的处理函数。
这会造成什么后果?嗯,我在项目中遇到过:半传输中断里正更新缓冲区指针呢,被完成中断抢占了,结果指针状态不一致,数据就乱了。
4.6 中断延迟的根源
聊完了中断类型,我们再来看中断延迟。你可能会问:“为什么我的 DMA 中断,从触发到 CPU 开始处理,要等好几微秒?”
原因有很多,但最常见的是这几个:
- 中断屏蔽:CPU 在处理更高优先级中断时,关掉了全局中断
- 中断嵌套:高优先级中断占用了 CPU,低优先级中断只能排队
- 中断控制器延迟:GIC 或者类似的硬件,分发中断需要时间
- 上下文保存:CPU 进入中断模式前,要保存寄存器等上下文信息
我曾经在一个项目里,看到 DMA 中断延迟长达 20 微秒。排查下来,原来是因为系统里有个定时器中断,每 10 微秒触发一次,优先级还特别高。DMA 中断每次都要等定时器中断处理完才能执行。
解决方案很简单:把 DMA 中断的优先级调到比定时器高。延迟一下子就降到了 3 微秒以内。
4.7 本章知识体系
说了这么多,我们来画一张关系图,把这三类中断的来龙去脉理清楚。
这张图把三类中断的触发、处理和相互关系都画清楚了。你可以看到,完成中断和半传输中断是“好兄弟”,一起实现了双缓冲流水线。而错误中断是“坏消息”,但我们必须认真对待它。