引言
在工业控制、医疗仪器和测试测量领域,高速数据采集系统是核心模块。传统的轮询或中断方式采集 ADC 数据,CPU 占用率高且实时性差。使用 DMA(直接内存访问)可以实现零 CPU 干预的高速数据采集。
本文将详细介绍基于 DMA 的 ADC 采集系统的设计方法,包括硬件配置、软件实现和性能优化。
系统架构
1.1 系统架构
架构说明:
- 传感器层:输出模拟信号(温度/压力/光电等)
- 信号调理:放大、滤波,调理到 0-3.3V 范围
- ADC:12/14/16-bit 精度,最高 1MSPS 采样率
- DMA 控制器:循环缓冲模式,自动回绕,零 CPU 干预
- 内存缓冲区:双缓冲策略,Buffer[0] 和 Buffer[1] 交替使用
- DSP 处理:FFT、滤波、特征提取等实时算法
触发机制:定时器提供精确采样率(100Hz - 1MHz)
关键优势:DMA 实现零 CPU 占用的高速数据采集
1.2 关键指标
| 参数 | 典型值 | 说明 |
|---|---|---|
| 采样率 | 100kSPS - 10MSPS | 根据应用需求选择 |
| 分辨率 | 12/14/16 bit | ADC 精度 |
| 通道数 | 1-16 | 多通道同步采集 |
| 缓冲大小 | 1KB - 1MB | 根据处理延迟确定 |
DMA 配置详解
2.1 DMA 控制器选择
以 STM32H7 为例:
- DMA1/DMA2:通用 DMA,支持外设到内存传输
- BDMA:基本 DMA,用于低功耗外设
// DMA 配置结构体
DMA_HandleTypeDef hdma_adc1;
void MX_DMA_Init(void)
{
// 启用 DMA 时钟
__HAL_RCC_DMA1_CLK_ENABLE();
// 配置 DMA 通道
hdma_adc1.Instance = DMA1_Stream0;
hdma_adc1.Init.Request = DMA_REQUEST_ADC1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址不递增
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE; // 内存地址递增
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; // 16 位数据
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR; // 循环模式
hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&hdma_adc1);
// 关联 DMA 到 ADC
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
}
2.2 双缓冲策略
工作流程:
- DMA 半传输完成 → 触发
HAL_ADC_ConvHalfCpltCallback - DMA 全传输完成 → 触发
HAL_ADC_ConvCpltCallback - 回调函数 → 设置 buffer_ready 标志
- 主循环 → 检测标志位 → 处理数据
循环缓冲(推荐):
- DMA 自动回绕到缓冲区起始位置
- 适合连续采集场景
- 需要双缓冲或半传输中断处理
普通缓冲:
传输完成后停止
适合单次采集
需要手动重启 DMA
DMA 自动回绕到缓冲区起始位置
适合连续采集场景
需要双缓冲或半传输中断处理
#define ADC_BUFFER_SIZE 1024
uint16_t adc_buffer[ADC_BUFFER_SIZE];
// 启动 DMA 循环采集
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_BUFFER_SIZE);
普通缓冲:
- 传输完成后停止
- 适合单次采集
- 需要手动重启 DMA
2.3 定时器触发配置
使用定时器触发 ADC 转换,实现精确采样:
// 配置定时器(100kHz 触发)
TIM_HandleTypeDef htim_trigger;
void MX_TIM2_Init(void)
{
htim_trigger.Instance = TIM2;
htim_trigger.Init.Prescaler = 83; // 84MHz / 84 = 1MHz
htim_trigger.Init.CounterMode = TIM_COUNTERMODE_UP;
htim_trigger.Init.Period = 9; // 1MHz / 10 = 100kHz
htim_trigger.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
// 配置触发输出
TIM_MasterConfigTypeDef sMasterConfig;
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim_trigger, &sMasterConfig);
HAL_TIM_Base_Start(&htim_trigger);
}
// ADC 配置为外部触发
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T2_TRGO;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
数据处理与同步
3.1 双缓冲策略
#define BUFFER_SIZE 512
uint16_t buffer[2][BUFFER_SIZE]; // 双缓冲
volatile uint8_t buffer_index = 0;
volatile uint8_t buffer_ready = 0;
// DMA 半传输完成回调
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
buffer_index = 0;
buffer_ready = 1;
}
// DMA 传输完成回调
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
buffer_index = 1;
buffer_ready = 1;
}
// 主循环处理
while (1) {
if (buffer_ready) {
ProcessData(buffer[buffer_index], BUFFER_SIZE);
buffer_ready = 0;
}
}
3.2 数据对齐处理
DMA 传输时注意数据对齐:
// 确保缓冲区 32 字节对齐(Cortex-M7 缓存行大小)
uint16_t adc_buffer[1024] __attribute__((aligned(32)));
// 或者使用编译器指令
#pragma data_alignment = 32
uint16_t adc_buffer[1024];
3.3 缓存维护
对于 Cortex-M7,DMA 传输后需要维护 D-Cache:
void DMA_InvalidateBuffer(void *buffer, uint32_t size)
{
SCB_InvalidateDCache_by_Addr(buffer, size);
}
// 在半传输/全传输回调中调用
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
DMA_InvalidateBuffer(adc_buffer, sizeof(adc_buffer));
data_ready = 1;
}
性能优化
4.1 提高采样率
- 使用高速 ADC 内核(如 STM32H7 的 16-bit 5MSPS)
- 降低 ADC 分辨率换取速度(16bit → 12bit)
- 使用多 ADC interleaved 模式
4.2 降低 CPU 占用
- 启用 DMA 中断,仅在缓冲区满时处理
- 使用 DSP 库进行 SIMD 加速
- 将数据处理放在 DTCM 中执行
4.3 降低延迟
- 减小缓冲区大小(但增加中断频率)
- 使用高优先级 DMA 通道
- 启用数据缓存(D-Cache)
常见问题
Q1: DMA 传输数据丢失
原因:缓冲区太小或处理太慢。
解决:增大缓冲区,或使用双缓冲 + 中断处理。
Q2: 采样率不稳定
原因:定时器配置错误或时钟源不稳定。
解决:使用外部晶振,检查定时器预分频器。
Q3: ADC 数据跳变
原因:参考电压波动或接地不良。
解决:使用独立模拟电源,优化 PCB 布局。
总结
基于 DMA 的 ADC 采集系统设计要点:
- 选择合适 DMA 通道:支持外设到内存,循环模式
- 配置定时器触发:实现精确采样间隔
- 使用双缓冲策略:避免数据丢失
- 注意数据对齐:提升缓存效率
- 维护 D-Cache:确保数据一致性
掌握这些技巧,你可以轻松实现 1MSPS 以上的零 CPU 占用数据采集!
本文基于 ST 官方技术文档和实际项目经验整理,结合 2026 年最新技术趋势编写。
参考资料
- STM32H7 Reference Manual
- ARM Cortex-M7 Technical Reference Manual
- STM32CubeH7 HAL 库文档