一、先理解STM32 ADC多通道的几种工作模式

STM32的ADC支持规则组注入组两类通道。规则组类似于常规轮询,最多16个通道;注入组类似中断优先,可插入到规则转换中。多通道采样最常用的是规则组+扫描模式,配合DMA可以无CPU干预地完成所有通道的循环采集。

关键概念:

  • 扫描模式(Scan Mode):自动按顺序转换规则组中的所有通道。
  • 不连续模式(Discontinuous Mode):每次触发只转换指定数量的通道,常用于节省功耗或配合定时器分批次采样。
  • 采样时间:影响ADC采样电容的充电时间,对高阻抗信号源尤其重要。
  • 校准(Calibration):STM32片内ADC自带校准功能,用于消除电容匹配误差。

二、配置步骤(基于STM32CubeMX + HAL库)

  1. 选择ADC实例:在CubeMX中启用ADC1,将需要的通道(如CH0、CH1、CH2)配置为规则通道,并使能扫描模式。
  2. 设置转换长度:Number Of Conversion = 3,表示规则组中有3个通道。
  3. 配置采样时间:根据信号源内阻调整,通常设置55.5或84周期(即ADC时钟下)。若信号源高阻,建议加大到239.5周期。
  4. 开启DMA:将ADC1的DMA请求添加到循环模式,外设地址为ADC->DR,内存地址为自定义数组。
  5. ADC校准:在初始化后调用HAL_ADCEx_Calibration_Start()。注意此函数会阻塞一段时间,建议放在启动后主循环前。

三、完整代码示例(STM32F1 / F4系列通用)

以下代码适用于STM32F4系列,使用ADC1的CH0~CH2采样三个模拟信号,DMA循环传输到数组,主循环中计算滑动平均滤波结果。

// adc_config.h
#define ADC_CH_NUM  3

extern uint16_t adc_raw[ADC_CH_NUM];
extern float adc_filtered[ADC_CH_NUM];

void ADC_Init(void);
void ADC_Filter(void);

// adc_config.c
#include "adc_config.h"
#include "main.h"

static ADC_HandleTypeDef hadc1;
static DMA_HandleTypeDef hdma_adc1;

uint16_t adc_raw[ADC_CH_NUM];
float adc_filtered[ADC_CH_NUM];

#define FILTER_WINDOW 8
static uint16_t filter_buf[ADC_CH_NUM][FILTER_WINDOW];
static uint8_t filter_index = 0;

void ADC_Init(void)
{
    __HAL_RCC_ADC1_CLK_ENABLE();
    __HAL_RCC_DMA2_CLK_ENABLE();

    // 配置DMA(ADC1的DMA请求映射到DMA2 Stream0)
    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_CIRCULAR;
    hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
    HAL_DMA_Init(&hdma_adc1);
    __HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);

    // ADC1配置
    hadc1.Instance = ADC1;
    hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
    hadc1.Init.Resolution = ADC_RESOLUTION_12B;
    hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
    hadc1.Init.ContinuousConvMode = ENABLE;  // 连续采样
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.NbrOfConversion = ADC_CH_NUM;
    HAL_ADC_Init(&hadc1);

    // 配置三个规则通道
    ADC_ChannelConfTypeDef sConfig = {0};
    sConfig.SamplingTime = ADC_SAMPLETIME_84CYCLES;  // 根据信号源内阻调整
    sConfig.Channel = ADC_CHANNEL_0;
    sConfig.Rank = 1;
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);

    sConfig.Channel = ADC_CHANNEL_1;
    sConfig.Rank = 2;
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);

    sConfig.Channel = ADC_CHANNEL_2;
    sConfig.Rank = 3;
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);

    // 校准
    HAL_ADCEx_Calibration_Start(&hadc1);

    // 启动ADC并开启DMA
    HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_raw, ADC_CH_NUM);
}

// 滑动平均滤波(每通道)
void ADC_Filter(void)
{
    filter_index = (filter_index + 1) % FILTER_WINDOW;
    for (int i = 0; i < ADC_CH_NUM; i++) {
        filter_buf[i][filter_index] = adc_raw[i];
        uint32_t sum = 0;
        for (int j = 0; j < FILTER_WINDOW; j++) {
            sum += filter_buf[i][j];
        }
        adc_filtered[i] = (float)sum / FILTER_WINDOW;
    }
}

四、校准注意事项

  • STM32的ADC校准适用于所有转换模式,但在连续扫描+DMA模式下,校准不会影响实时数据流,需在启动转换前完成。
  • 有些系列(如F4)校准前需要先将ADC使能,且等待稳定时间。HAL库函数已封装,直接调用即可。
  • 若使用不同参考电压或环境温度变化大,建议定期重新校准。

五、滤波策略:硬件过采样 vs 软件滤波

  • 硬件过采样:部分STM32支持过采样(oversampling),通过硬件累加+移位提高有效分辨率,适合低噪声信号。但每个通道会增加转换时间,且需要精确配置。
  • 软件滤波:灵活多样,滑动平均、中值滤波、IIR低通滤波等。对于工频干扰(50/60Hz),可结合定时器触发与整数周期采样。
  • 推荐做法:先使用硬件过采样(如果支持)或增加采样时间降低噪声,再叠加简单的滑动平均或移动平均,计算代价小。

六、常见问题与注意事项

  • 规则组转换序列溢出:如果NbrOfConversion设置小于实际配置的Rank数,会漏掉通道。务必保证两者相等。
  • DMA数据错位:当ADC通道转换顺序与DMA缓冲写入不匹配时,会让数据张冠李戴。必须保持通道顺序和数组索引一致。
  • 注入组冲突:如果同时使用注入组且优先级高于规则组,会导致规则组转换被中断,时间轴抖动。若非必要,建议关闭注入组。
  • 高阻抗信号:采样时间过短会导致充电不足,产生非线性误差。可使用运放跟随器或延长采样周期。
  • 参考电压噪声:VREF+引脚上必须接高质量去耦电容,否则会影响所有通道的转换精度。
  • 多ADC同步:如果需要同时采样多个通道,可考虑多ADC同步模式(如使用ADC1和ADC2),但要注意外设映射冲突。

七、总结

STM32 ADC多通道采样的核心在于正确配置扫描模式、通道顺序和采样时间,并利用DMA卸载CPU。校准能消除固定偏移误差,而滤波则是抑制噪声的有效手段。实际项目中应根据信号特性和实时性要求,灵活组合硬件过采样、软件滤波和触发方式。希望本文的示例与经验能帮助你在开发中少走弯路。