4. DMA 传输配置:源地址/目的地址设置、传输宽度与传输计数中断
DMA(Direct Memory Access)传输配置是 RAW 图采集系统中连接采集前端与内存后端的核心桥梁。配置错误将直接导致数据错位、带宽浪费或帧丢失。本节从源/目的地址、传输宽度、传输计数与中断三个维度展开,结合 FPGA 与嵌入式 DSP 的典型寄存器模型进行讲解。
核心原则:DMA 配置必须与 RAW 图的数据格式(Bayer 模式、位宽)、行缓存策略(Line Buffer)以及内存的突发传输特性(Burst Length)严格对齐,否则即使时钟频率满足要求,也无法实现零拷贝、零等待的采集链路。
4.1 源地址与目的地址设置
DMA 传输的源地址通常指向采集前端(如 CMOS 传感器接口、FPGA 内部的 FIFO 或 AXI-Stream 接口)的地址空间;目的地址则指向系统内存(DDR3/DDR4 或 SRAM)中的帧缓冲区。在 RAW 图采集场景中,地址配置需关注以下要点:
- 源地址固定 vs. 递增:传感器接口通常为固定地址(如 FIFO 的读端口),而内存目的地址必须递增,以顺序存储像素数据。
- 地址对齐:目的地址必须与传输宽度对齐。例如 32 位传输时,目的地址需 4 字节对齐;若使用 AXI 总线,还需满足 16 字节或 64 字节的突发对齐要求。
- 行地址跳跃(Stride):RAW 图通常按行存储,每行像素数可能不等于内存中的行跨度(Stride)。DMA 需支持每传输完一行后,目的地址自动加上 Stride 值,跳过行尾填充字节。
实战提示:在 Xilinx VDMA(Video DMA)中,源地址和目的地址分别通过
VSize(垂直尺寸)和 HSize(水平尺寸)配合 Stride 寄存器实现二维地址映射。配置时务必先设置 Stride,再设置帧起始地址,否则可能导致行尾地址计算错误。
4.2 传输宽度配置(8/16/32 位)
传输宽度决定了 DMA 每次总线事务搬运的数据位数。在 RAW 图采集系统中,传输宽度的选择直接影响总线利用率和帧率上限。下表对比了三种常见配置的适用场景与性能特征:
| 传输宽度 | 适用 RAW 图位深 | 单次传输像素数 | 总线利用率 | 典型场景 |
|---|---|---|---|---|
| 8 位 | 8-bit RAW (如 RAW8) | 1 像素 | 低(总线带宽浪费) | 低分辨率、低帧率传感器 |
| 16 位 | 10/12/14-bit RAW (如 RAW10/RAW12/RAW14) | 1 像素(高位对齐) | 中等 | 主流工业相机、手机 ISP |
| 32 位 | 8/10/12-bit RAW 打包 | 2~4 像素(取决于打包格式) | 高 | 高分辨率、高帧率采集 |
避坑指南:当 RAW 图位宽不是 8/16/32 的整数倍时(如 RAW10),直接使用 16 位传输会导致每像素浪费 6 位带宽。此时应使用打包模式(如 4 个 RAW10 像素打包为 5 字节,再通过 32 位传输搬运),或使用 FIFO 位宽转换器将数据对齐到 16/32 位边界。
4.3 传输计数与中断配置
传输计数(Transfer Count)决定了 DMA 在一次触发后搬运的数据总量。在 RAW 图采集中,传输计数通常与一帧图像的总字节数或一行像素的总字节数对应。中断则用于通知 CPU 传输完成,触发帧处理或下一帧配置。
4.3.1 传输计数设置策略
- 帧传输模式:传输计数 = 帧宽度(像素)× 帧高度(行)× 每像素字节数。适用于整帧采集完成后才需要处理数据的场景。
- 行传输模式:传输计数 = 帧宽度(像素)× 每像素字节数。每传输完一行触发一次中断,适用于需要逐行处理(如去噪、坏点校正)的低延迟场景。
- 链表传输模式(Scatter-Gather):通过描述符链表将多行或帧的传输计数分散到多个 DMA 描述符中,实现非连续内存地址的连续传输。
性能优化点:在 FPGA 中实现 DMA 控制器时,传输计数寄存器通常为 16 位或 32 位。对于 4K 分辨率 RAW 图(4096×2160,每像素 2 字节),单帧数据量约 16.6 MB。若使用 16 位计数寄存器(最大 65535),需分多次传输或使用计数扩展模式。建议在系统设计阶段优先选择 32 位计数寄存器。
4.3.2 中断配置与处理
中断配置是 DMA 效率优化的关键环节。过多中断会消耗 CPU 资源,过少中断则可能导致帧处理延迟。常见中断类型包括:
| 中断类型 | 触发条件 | 适用场景 | 中断频率 |
|---|---|---|---|
| 传输完成中断 | DMA 传输计数归零 | 帧采集完成、行采集完成 | 每帧/每行一次 |
| 错误中断 | 总线错误、地址越界、FIFO 溢出 | 异常检测与恢复 | 偶发 |
| 半传输中断 | 传输计数达到一半 | 双缓冲切换(Ping-Pong Buffer) | 每帧两次 |
| 描述符完成中断 | 链表中的单个描述符传输完成 | Scatter-Gather 模式 | 每描述符一次 |
4.4 典型配置代码示例
以下代码展示了在嵌入式系统中配置 DMA 传输的典型流程,基于 STM32 的 DMA 控制器寄存器模型,但逻辑适用于大多数 FPGA/DSP 平台:
// DMA 传输配置结构体
typedef struct {
uint32_t src_addr; // 源地址(传感器 FIFO 地址)
uint32_t dst_addr; // 目的地址(帧缓冲区地址)
uint32_t transfer_size; // 传输计数(字节数)
uint8_t width; // 传输宽度:0=8位, 1=16位, 2=32位
uint8_t src_inc; // 源地址递增模式:0=固定, 1=递增
uint8_t dst_inc; // 目的地址递增模式:0=固定, 1=递增
uint8_t irq_enable; // 中断使能:0=禁用, 1=使能
} DMA_Config_t;
// DMA 初始化函数
void DMA_Init(DMA_Config_t *cfg) {
// 1. 设置源地址和目的地址
DMA->SAR = cfg->src_addr; // 源地址寄存器
DMA->DAR = cfg->dst_addr; // 目的地址寄存器
// 2. 设置传输宽度
switch (cfg->width) {
case 0: // 8 位
DMA->CR &= ~(3 << 8); // 清除宽度位
DMA->CR |= (0 << 8); // 设置 8 位
break;
case 1: // 16 位
DMA->CR &= ~(3 << 8);
DMA->CR |= (1 << 8); // 设置 16 位
break;
case 2: // 32 位
DMA->CR &= ~(3 << 8);
DMA->CR |= (2 << 8); // 设置 32 位
break;
}
// 3. 设置地址递增模式
if (cfg->src_inc) {
DMA->CR |= (1 << 6); // 源地址递增使能
} else {
DMA->CR &= ~(1 << 6); // 源地址固定
}
if (cfg->dst_inc) {
DMA->CR |= (1 << 7); // 目的地址递增使能
} else {
DMA->CR &= ~(1 << 7); // 目的地址固定
}
// 4. 设置传输计数
DMA->CNDTR = cfg->transfer_size; // 传输计数寄存器
// 5. 配置中断
if (cfg->irq_enable) {
DMA->CR |= (1 << 1); // 传输完成中断使能
NVIC_EnableIRQ(DMA_IRQn); // 使能 NVIC 中断
} else {
DMA->CR &= ~(1 << 1); // 禁用中断
}
// 6. 使能 DMA
DMA->CR |= (1 << 0); // DMA 使能位
}
// DMA 中断服务函数
void DMA_IRQHandler(void) {
if (DMA->ISR & (1 << 1)) { // 检查传输完成标志
// 清除中断标志
DMA->IFCR |= (1 << 1);
// 帧处理回调
FrameProcessingCallback();
// 重新配置下一帧(如果需要连续采集)
DMA_Config_t next_cfg = {
.src_addr = SENSOR_FIFO_ADDR,
.dst_addr = NEXT_FRAME_BUFFER,
.transfer_size = FRAME_SIZE_BYTES,
.width = 2, // 32 位传输
.src_inc = 0, // 源地址固定
.dst_inc = 1, // 目的地址递增
.irq_enable = 1
};
DMA_Init(&next_cfg);
}
}
调试建议:在首次配置 DMA 时,建议先使用 8 位传输宽度和较小的传输计数(如 64 字节)进行验证。通过逻辑分析仪或芯片内置的 DMA 跟踪功能,检查源地址、目的地址和传输计数是否按预期变化。确认无误后再切换到 32 位传输和全帧计数,以降低调试复杂度。