1. 为什么需要DMA处理串口数据?
在嵌入式系统中,串口通信是最基础也最常用的外设接口之一。传统的中断方式处理串口数据时,每接收或发送一个字节都会触发一次中断,CPU需要频繁地在主程序和中断服务程序之间切换。我曾在项目中遇到过这样的场景:当系统需要同时处理传感器数据采集、屏幕刷新和网络通信时,串口中断频繁抢占CPU资源,导致其他任务出现明显卡顿。
更严重的是,当串口数据速率较高(比如115200bps及以上)时,中断响应不及时会导致数据丢失。有一次调试工业传感器时,就因为中断处理不及时丢失了关键的温度突变数据。后来改用DMA方案后,不仅数据零丢失,CPU占用率也从原来的70%降到了15%左右。
DMA(直接内存访问)的本质是硬件级的数据搬运工,它能在不占用CPU资源的情况下,自动完成外设和内存之间的数据传输。这就好比在快递仓库里雇了个专职分拣员,快递车(串口)到货后直接由分拣员(DMA)把包裹(数据)搬进仓库(内存),老板(CPU)只需要在合适的时候检查库存即可。
2. DMA双缓冲区设计原理
2.1 传统单缓冲区的痛点
在早期项目中,我习惯使用单缓冲区配合DMA接收数据。但当处理高速数据流时发现一个问题:当DMA正在填充缓冲区时,如果CPU同时读取数据,可能会读到半成品数据;而如果等DMA传输完成再处理,在数据处理期间新数据又会覆盖缓冲区。这种"读写冲突"就像两个人同时往一张纸上写字,结果必然是混乱的。
最典型的案例是在处理Modbus通信时,主机发送的查询帧和从机回复的数据帧会出现部分重叠,导致协议解析失败。后来通过逻辑分析仪抓包才发现,不是通信问题,而是缓冲区管理不当造成的。
2.2 双缓冲区的工作机制
双缓冲区设计采用"乒乓操作"原理,就像餐厅里两个传菜窗口:
- 缓冲区A:DMA当前正在写入的活跃区
- 缓冲区B:CPU正在处理数据的就绪区
当DMA写满缓冲区A时,会自动切换到缓冲区B继续写入,同时触发中断通知CPU处理缓冲区A的数据。这种设计确保了始终有一个缓冲区处于可写入状态,避免了数据覆盖。在实际项目中,我将这种设计应用在GPS数据接收上,即使每秒10次的NMEA语句爆发传输也能稳定处理。
具体实现时需要三个关键变量:
u8 buffer1[1024]; // 缓冲区1
u8 buffer2[1024]; // 缓冲区2
volatile uint8_t active_buffer = 0; // 当前活跃缓冲区标志
3. STM32 DMA配置实战
3.1 硬件环境搭建
以STM32F103系列为例,我们需要配置以下外设:
-
时钟配置:
- 使能DMA1时钟:
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE) - 使能USART2时钟:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENA
- 使能DMA1时钟:


3043

被折叠的 条评论
为什么被折叠?



