第一次接触EDMA是在2015年开发TI DSP项目时,当时需要处理雷达传感器产生的高速数据流。传统DMA在传输大批量矩阵数据时频繁触发中断,导致CPU负载居高不下。直到同事推荐使用EDMA3控制器,才真正体会到什么叫做"解放CPU"的数据传输。
**EDMA(Enhanced Direct Memory Access)**本质上是一种智能化的DMA升级方案。与传统DMA相比,最大的区别就像手动挡汽车和自动挡汽车——传统DMA需要CPU频繁介入换挡(配置参数),而EDMA自带"变速箱"(参数自动重载)和"导航系统"(多维地址计算)。举个例子,在图像处理场景中,传统DMA传输1920x1080的图像需要CPU干预1080次,而EDMA只需配置一次参数就能完成整个帧传输。
具体来看,EDMA的核心增强体现在三个方面:
在实际项目中,EDMA通常承担三类任务:
TI官方文档将EDMA控制器比作"快递分拣中心",这个比喻非常贴切。以我调试过的AWR1843雷达芯片为例,其EDMA系统包含:
**EDMACC(通道控制器)**就像分拣中心的调度系统,我习惯把它分为五个关键模块:
**EDMATC(传输控制器)**则是真正的运输车队,其核心部件包括:
c复制// 典型EDMATC寄存器组结构
struct EDMATC_Regs {
uint32_t SRC_ADDR; // 源地址寄存器
uint32_t DST_ADDR; // 目的地址寄存器
uint32_t ACNT_BCNT; // 第一二维度计数
uint32_t CCNT_LINK; // 第三维度计数+链接地址
uint32_t SRC_DST_BIDX;// 帧内地址增量
uint32_t SRC_DST_CIDX;// 帧间地址增量
uint32_t OPT; // 传输选项
};
PaRAM是EDMA最精妙的设计之一。在毫米波雷达项目中,我常用它实现三种高级玩法:
乒乓缓冲配置示例:
c复制// PaRAM set 0: 缓冲区A配置
{
.srcAddr = bufA_addr,
.dstAddr = procUnit_addr,
.aCnt = 256, // 每个数据块256字节
.bCnt = 32, // 32行
.srcBidx = 256,
.dstBidx = 0, // 目的地址连续
.link = 1 // 传输完成后跳转到set 1
}
// PaRAM set 1: 缓冲区B配置
{
.srcAddr = bufB_addr,
.dstAddr = procUnit_addr,
.aCnt = 256,
.bCnt = 32,
.srcBidx = 256,
.dstBidx = 0,
.link = 0 // 跳转回set 0
}
这种配置下,EDMA会在AB缓冲区间自动切换,CPU只需检查传输完成中断即可获取最新数据,完全避免缓冲区冲突。
在医疗超声设备开发中,我们曾用A同步模式处理B超探头数据。具体场景是:
对应的PaRAM配置关键参数:
code复制ACNT = 128 // 每条扫描线128个采样点
BCNT = 256 // 每帧256条线
CCNT = 30 // 30帧数据
SRCBIDX = 128 // 线内采样点间隔
DSTBIDX = 4 // 存储为float32格式
SRCCIDX = 32768 // 帧间隔=256线×128采样点
这种模式下,每个探头触发事件只传输一条扫描线,适合对实时性要求极高的场景。但要注意事件风暴问题——30帧数据需要触发7680次(256×30),可能造成事件队列溢出。
对比之下,在工业CT图像重建时,AB同步模式更为高效。以2048×2048探测器阵列为例:
c复制// 典型AB同步配置
void configABSync() {
EDMA3_SetPaRAM(0, {
.opt = EDMA3_OPT_AB_SYNC, // 设置AB同步模式
.srcAddr = detector_addr,
.dstAddr = ddr_addr,
.aCnt = 2048*2, // 每行2048像素(16bit)
.bCnt = 2048, // 2048行
.srcBidx = 2048*2,
.dstBidx = 2048*2,
.link = 0
});
}
只需一次触发就能传输完整帧数据,传输效率提升2000倍以上。但要注意内存带宽占用——单次传输8MB数据(2048×2048×2),需要确保目的存储器有足够带宽。
在TI C6678 DSP上实测发现,EDMA性能与以下因素强相关:
优化前后对比表:
| 优化项 | 传输4K图像耗时(ms) | 带宽利用率 |
|---|---|---|
| 原始配置 | 2.45 | 38% |
| 三维传输 | 1.82 | 51% |
| 地址64B对齐 | 1.12 | 83% |
| 缓存预热+三维 | 0.97 | 96% |
踩过最深的坑是事件队列溢出。某次雷达信号处理中,EDMA突然停止工作,调试发现:
解决方案是增加软件事件合并层:
c复制// 事件合并算法示例
void onRadarPulse() {
static int event_count = 0;
event_count++;
if(event_count >= BCNT) {
EDMA3_TriggerManualEvent(EDMA_CHANNEL);
event_count = 0;
}
}
另一个典型问题是参数更新冲突。曾遇到PaRAM在传输中被CPU修改导致的数据错乱,最终通过以下方式解决:
c复制// 安全的PaRAM更新流程
void updatePaRAM() {
EDMA3_DisableChannel(EDMA_CHANNEL); // 停止通道
while(EDMA3_IsChannelActive()); // 等待传输完成
memcpy(PaRAM_shadow, new_params, sizeof(PaRAM_set));
EDMA3_UpdatePaRAM(EDMA_CHANNEL, PaRAM_shadow);
EDMA3_EnableChannel(EDMA_CHANNEL); // 重新启用
}
在毫米波雷达项目实践中,EDMA的合理配置能使系统性能提升3-5倍。建议开发者重点关注传输维度设计与事件触发机制的配合,这往往是性能突破的关键点。对于更复杂的场景,可以尝试结合QDMA的自动触发特性,实现零CPU干预的数据流处理。