一、日志采集的痛点:传统方式为什么拖垮了你的CPU?
做嵌入式开发的朋友,肯定都跟日志打过交道。调试阶段还好说,一旦到了产品阶段,日志采集就成了一个让人头疼的问题。
我个人习惯在项目初期就把日志方案定下来,不然后面改起来,那叫一个痛苦。你想想看,一个跑着实时控制算法的系统,突然因为打印日志卡顿了那么几毫秒,后果可能很严重。
1.1 传统日志采集的“三板斧”
咱们先看看最常见的几种做法:
- printf 大法:简单粗暴,哪里需要打哪里。但 printf 是阻塞的,一个字符没发完,CPU 就得等着。
- 串口轮询发送:在 main 循环里不断查串口状态,有数据就发。这玩意儿就像你一边写代码一边时不时看手机,效率能高吗?
- 中断发送:比轮询好一点,但每个字节都触发一次中断,频繁的上下文切换照样吃掉大量 CPU 时间。
说白了,这些方法的核心问题都一样:CPU 亲自下场干苦力。每次发送一个字节,CPU 都要参与搬运、状态检查、中断处理。对于动辄几十 KB 的日志数据,这个开销是惊人的。
核心矛盾:日志采集需要占用 CPU 时间,而实时任务也需要 CPU 时间。两者争抢同一个资源,必然导致冲突。
1.2 一个真实的“血泪史”
我曾经接手过一个电机控制项目,前任工程师用 printf 打日志,波特率设到了 115200。结果呢?电机在低速运行时一切正常,一旦加速到 3000 转,系统就频繁丢步。查了三天,最后发现是 printf 占用了太多 CPU 时间,导致 PWM 更新不及时。
嗯,这里要注意:日志采集不是“顺便”做的事,它是有代价的。代价就是 CPU 的宝贵周期。
1.3 量化分析:传统方式到底有多“贵”?
咱们来算一笔账。假设串口波特率 115200,发送一个字节需要大约 87 微秒。如果采用阻塞式发送,CPU 在这 87 微秒里什么都干不了。
| 日志方式 | 每字节 CPU 占用 | 发送 1KB 日志耗时 | 对实时任务影响 |
|---|---|---|---|
| printf(阻塞) | 87 μs(100%) | 89 ms | 严重,完全阻塞 |
| 串口轮询 | 60 μs(70%) | 61 ms | 中等,占用主循环 |
| 中断发送 | 15 μs(17%) | 15 ms | 较轻,但中断频繁 |
| DMA 方式 | 2 μs(2%) | 2 ms | 几乎无影响 |
看到差距了吧?DMA 方式下,CPU 只需要把数据地址告诉 DMA 控制器,然后就可以去干别的事了。剩下的搬运工作,DMA 全包了。
我的建议:如果你的系统里有一个周期性的实时任务(比如 1ms 的控制循环),那么日志采集的 CPU 占用率最好控制在 5% 以内。超过这个数,你就该考虑 DMA 方案了。
1.4 为什么 DMA 能解决这个问题?
DMA 的全称是 Direct Memory Access,直接存储器访问。它的核心思想很简单:让硬件干硬件的活,让 CPU 干 CPU 的活。
具体来说,DMA 控制器是一个独立于 CPU 的硬件模块。它可以自己从内存里读数据,然后直接送到外设(比如串口)的发送寄存器里。整个过程不需要 CPU 参与,CPU 只需要在开始的时候告诉 DMA:“嘿,帮我把这 100 个字节发出去”,然后就可以转身去处理实时任务了。
为什么会这样?因为 DMA 和 CPU 是并行工作的。CPU 在跑控制算法的时候,DMA 在背后默默地把日志数据搬走。两者互不干扰。
下面这张图可以帮你理解 DMA 的工作流程:
从这张图可以看出,整个流程分三步:
- CPU 配置 DMA:告诉 DMA 要搬多少数据、从哪里搬、搬到哪里去。这一步很快,只需要写几个寄存器。
- DMA 读取内存:DMA 自己从日志缓冲区里把数据读出来。此时 CPU 可以去做别的事。
- DMA 写入串口:DMA 把数据写到串口的发送寄存器里,串口自动把数据发出去。
注意:DMA 虽然好,但不是万能的。它需要占用一条总线带宽。如果你的系统里已经有多个 DMA 通道在跑(比如 ADC 采集、显示刷新),要注意总线冲突的问题。我曾在项目中遇到过 DMA 通道优先级设置不当,导致日志数据被其他 DMA 传输打断的情况。
1.5 什么时候该用 DMA 日志?
不是所有场景都需要上 DMA。我个人总结了几条经验:
- 日志数据量大:每秒超过 1KB 的日志输出,建议用 DMA。
- 有硬实时任务:控制周期在 1ms 以内,且对抖动敏感的系统,必须用 DMA。
- 多任务环境:RTOS 下多个任务同时打日志,用 DMA 可以避免任务切换带来的延迟。
- 低功耗场景:DMA 可以在 CPU 休眠时继续工作,适合电池供电的设备。
反过来说,如果你的项目只是简单打印几个调试信息,波特率 9600,一天也打不了几行,那用 printf 也没啥问题。别为了用 DMA 而用 DMA,工具要选对场景。
1.6 小结
传统日志采集的痛点,说白了就是 CPU 被当成了搬运工。而 DMA 方案的核心思路,就是把搬运工作交给专门的硬件去做,让 CPU 回归它的本职工作——处理实时任务。
这一章我们分析了问题的根源和 DMA 的基本原理。下一章,我会带你看看具体的硬件配置和代码实现,包括 DMA 通道的选择、缓冲区的设计、以及如何避免常见的坑。
一句话总结:日志采集不该成为实时系统的瓶颈。用 DMA,把 CPU 的时间还给真正需要它的地方。