高效实现单次非扫描多通道ADC数据采集的硬件与软件技术(STM32CubeMX开发)
一、需求背景与应用场景
单次非扫描多通道ADC采集指在一次触发信号下,依次采集多个指定通道的模拟信号(每个通道仅采集1次),适用于:
- 多传感器分时采样(如温度、压力、电压同步监测);
- 低功耗场景(避免连续扫描浪费功耗);
- 动态通道选择(按需采集特定通道,非固定序列)。
传统扫描模式(Scan Mode)虽能自动完成多通道序列转换,但存在固定通道顺序、无法动态跳过无效通道的局限。非扫描模式通过软件控制通道切换,结合DMA/中断实现高效采集,灵活性更高。
二、硬件技术:STM32 ADC核心特性与配置
1. STM32 ADC工作模式
- 单次转换(Single Conversion):触发一次,完成一个通道转换后停止;
- 非扫描模式(Scan Mode Disabled):规则组仅1个通道,需软件手动切换通道;
- 触发源:软件触发(SWSTART)或外部触发(如定时器、GPIO)。
核心优势:通过软件依次设置通道,实现“按需采集”,避免扫描模式的无用通道转换。
2. 硬件关键配置
(1)ADC时钟与采样时间
- 时钟源:APB2时钟(STM32F4为84MHz,F1为72MHz),分频后需≤14MHz(保证精度);
- 采样时间:根据信号源阻抗设置(如高阻抗信号需更长采样时间,12位ADC推荐≥15 cycles)。
(2)参考电压
- 使用内部参考电压(VREFINT) 或外部精密基准源(如REF3030),确保转换精度(±1LSB内)。
(3)通道与引脚分配
- 多通道需分配不同GPIO引脚(如PA0=ADC1_IN0,PA1=ADC1_IN1,PA2=ADC1_IN2);
- 避免通道间串扰:模拟信号走线远离开关信号,加滤波电容(0.1μF)。
(4)DMA与中断
- DMA:配置为“单次传输模式”,每个通道转换完成后自动将结果存入内存,减少CPU干预;
- 中断:使能“转换完成中断(EOC)”,实时处理数据。
三、软件技术:STM32CubeMX配置与代码实现
1. STM32CubeMX配置步骤(以STM32F407为例)
(1)时钟树配置
- 系统时钟:HSE=8MHz → PLL×168 → 168MHz SYSCLK;
- APB2时钟:84MHz(ADC挂载于APB2,分频系数=2,ADC时钟=84/2=42MHz,需再分频至≤14MHz,故设置ADC预分频=4 → 42/4=10.5MHz)。
(2)ADC参数配置
- 模式:单次转换(Single Conversion)、非扫描(Scan Mode Disabled);
- 数据对齐:右对齐(12位ADC,0~4095);
- 通道配置:添加3个规则通道(如IN0、IN1、IN2),采样时间=144 cycles(10.5MHz下,采样时间=144/10.5≈13.7μs,兼顾速度与精度)。
(3)DMA配置
- 模式:正常模式(Normal,单次传输)、外设到内存、数据宽度=16位(匹配ADC数据);
- 通道:ADC1对应DMA2 Stream0 Channel0。
(4)中断配置
- 使能“ADC1全局中断”和“DMA2 Stream0中断”,优先级中等。
(5)生成工程
选择MDK-ARM或STM32CubeIDE,生成HAL库工程。
2. 核心代码实现
(1)ADC与DMA初始化(自动生成,略作修改)
// adc.c(STM32CubeMX生成后修改)
#include "adc.h"
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
uint16_t adc_values[3]; // 存储3通道数据(IN0~IN2)
void MX_ADC1_Init(void) {
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; // 10.5MHz
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = DISABLE; // 非扫描模式
hadc1.Init.ContinuousConvMode = DISABLE; // 单次转换
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 软件触发
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1; // 非扫描模式,仅1个通道
hadc1.Init.DMAContinuousRequests = DISABLE; // DMA单次传输
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; // 单次转换完成中断
HAL_ADC_Init(&hadc1);
// 配置通道0(IN0)
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_144CYCLES;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
// 配置DMA
hdma_adc1.Instance = DMA2_Stream0;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
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;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_NORMAL; // 单次传输
hdma_adc1.Init.Priority = DMA_PRIORITY_MEDIUM;
HAL_DMA_Init(&hdma_adc1);
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
}
(2)多通道采集函数(软件控制通道切换)
// 采集3个通道(IN0~IN2)
void ADC_MultiChannel_SingleShot(uint16_t *data) {
// 通道0
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_144CYCLES;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&data[0], 1); // DMA传输1个数据
while (HAL_ADC_GetState(&hadc1) != HAL_ADC_STATE_READY); // 等待转换完成
// 通道1
sConfig.Channel = ADC_CHANNEL_1;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&data[1], 1);
while (HAL_ADC_GetState(&hadc1) != HAL_ADC_STATE_READY);
// 通道2
sConfig.Channel = ADC_CHANNEL_2;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&data[2], 1);
while (HAL_ADC_GetState(&hadc1) != HAL_ADC_STATE_READY);
}
(3)主程序调用
// main.c
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
uint16_t adc_data[3];
while (1) {
// 单次非扫描多通道采集
ADC_MultiChannel_SingleShot(adc_data);
// 数据处理(如转换为电压:V = adc_data * 3.3 / 4095)
float v0 = adc_data[0] * 3.3f / 4095;
float v1 = adc_data[1] * 3.3f / 4095;
float v2 = adc_data[2] * 3.3f / 4095;
HAL_Delay(1000); // 1秒采集1次
}
}
参考代码 高效实现单次非扫描多通道ADC数据采集的硬件与软件技术+STM32cubemx开发 www.youwenfan.com/contentcns/180210.html
四、高效实现技巧
1. 减少通道切换开销
- 预配置通道参数:将各通道的采样时间、排名(Rank)预先存入数组,避免重复配置;
- 使用DMA双缓冲:配置2个DMA缓冲区,当前通道转换时,CPU可处理上一通道数据。
2. 优化采样时间
- 高阻抗信号(如传感器输出):增加采样时间(如480 cycles),确保电容充电完成;
- 低阻抗信号:缩短采样时间(如15 cycles),提高转换速度。
3. 低功耗设计
- 采集完成后关闭ADC时钟(
__HAL_ADC_DISABLE(&hadc1)); - 使用STM32的“低功耗模式”,仅在采集时唤醒ADC。
4. 数据校验
- 利用ADC的溢出标志(OVR) 和校准功能,确保数据有效性;
- 对关键通道进行中值滤波(如3次采样取中值),减少噪声。
五、测试与验证
1. 测试工具
- 示波器:测量ADC输入信号波形,验证采样时间是否足够;
- 万用表:测量输入电压,与ADC转换结果对比(误差≤±1LSB);
- 逻辑分析仪:抓取SPI/I2C通信(若用外部ADC),验证时序。
2. 性能指标
| 指标 | 非扫描模式(软件控制) | 扫描模式(自动序列) |
|---|---|---|
| 通道切换时间 | 2~5μs(软件配置) | 0.5~1μs(硬件自动) |
| 灵活性 | 高(动态选通道) | 低(固定序列) |
| CPU占用率 | 中(需软件干预) | 低(DMA自动) |
六、总结
单次非扫描多通道ADC采集通过软件控制通道切换实现灵活采样,结合STM32CubeMX的直观配置,可高效完成多传感器数据采集。核心在于:
- 硬件配置:合理设置ADC时钟、采样时间、DMA单次传输;
- 软件优化:预配置通道参数、使用中断/DMA减少CPU干预;
- 场景适配:根据信号特性(阻抗、速度)调整采样策略,平衡精度与效率。