4. STM32 ADC多通道采集配置与校准

工业数据采集终端往往需要同时监测多路模拟量,例如 4~20 mA 变送器、热电阻调理输出、电池电压和基准源。STM32 的 ADC 若只按单通道轮询方式使用,CPU 会被采样等待占满,通道间还会互相干扰。本节围绕“多通道如何组织、数据如何搬运、精度如何校准、时间如何分配”四个问题展开,把 ADC 配置成一条稳定的采集流水线。

规则组与注入组:两类通道的分工

STM32 ADC 的通道被划分为规则组(Regular)和注入组(Injected)。规则组是主采集序列,可配置最多 16 个通道,按顺序自动转换,转换结果写入同一个数据寄存器;注入组最多 4 个通道,拥有独立的数据寄存器,可被外部触发打断规则组,常用于需要更高优先级、更快响应的信号,例如过流保护采样或同步采样点。

规则组的关键限制是“结果共用一个寄存器”。如果不用 DMA,每转换完一个通道就必须及时读走,否则会被下一通道覆盖。因此多通道规则采集几乎必然配合 DMA。注入组则相反,4 个通道各有独立结果寄存器(JDR1~JDR4),可在转换结束后一次性读取,适合少量高优先级通道。

一个典型分工示例:规则组顺序采集 6 路慢变信号(温度、压力、液位等),注入组采集 2 路快变保护信号(母线电流、阀位反馈)。规则组由定时器触发,注入组由比较器或外部事件触发,两者互不阻塞。

DMA 循环搬运:让数据自动落位

规则组多通道采集的标准做法是开启 DMA 循环模式(Circular)。ADC 每完成一次转换就发出 DMA 请求,DMA 把 ADC_DR 中的结果搬到内存数组,地址自增,搬完一轮后自动回到数组首地址继续。CPU 完全不参与搬运,只在半满或全满中断里处理数据。

配置要点如下:

  • DMA 方向为外设到存储器,外设地址固定为 ADC 数据寄存器,存储器地址自增。
  • 数据宽度按分辨率选择:12 位结果用半字(16 位)即可,避免字节对齐问题。
  • 循环模式使能,否则一轮结束后 DMA 停止,后续转换结果无处存放。
  • DMA 缓冲区长度等于规则组通道数,保证“通道—数组下标”一一对应。

下面是一段基于 HAL 库的规则组 + DMA 循环采集配置示例,采集 6 个通道,缓冲区 6 个半字:

#define ADC_CH_NUM 6
static uint16_t adc_buf[ADC_CH_NUM];

// ADC 规则组通道顺序:IN0, IN1, IN4, IN5, IN6, IN7
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;
sConfig.SingleDiff   = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;

uint32_t ch_list[ADC_CH_NUM] = {
    ADC_CHANNEL_0, ADC_CHANNEL_1, ADC_CHANNEL_4,
    ADC_CHANNEL_5, ADC_CHANNEL_6, ADC_CHANNEL_7
};
for (int i = 0; i < ADC_CH_NUM; i++) {
    sConfig.Channel = ch_list[i];
    sConfig.Rank    = i + 1;          // 规则组顺序
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}

// 启动 DMA 循环搬运
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_buf, ADC_CH_NUM);

启动后 adc_buf[0] 对应 IN0,adc_buf[1] 对应 IN1,依此类推。若发现通道错位,优先检查 Rank 顺序与数组下标是否一致,而不是怀疑硬件。

过采样:用时间换有效位数

STM32 部分系列内置硬件过采样单元,可对同一通道连续采样 N 次并累加/平均,把结果右移后输出。过采样能抑制随机噪声,提高有效分辨率。例如 12 位 ADC 做 16 倍过采样并右移 2 位,理论上可得到约 14 位有效分辨率,但代价是转换时间变为原来的 16 倍。

过采样配置需关注三个参数:过采样倍数(2~256)、右移位数、触发模式。倍数与右移要匹配:16 倍过采样通常右移 2 位,256 倍右移 4 位。若右移过多会丢失有效位,右移过少则结果溢出。过采样适合低频信号,如温度、称重;对高频动态信号会引入额外延迟,需谨慎使用。

内部参考电压校准:消除供电漂移

工业现场 VDDA 常由 LDO 提供,存在温漂和负载波动。若直接用 VDDA 作为参考计算电压,误差会直接进入结果。STM32 内部有一个出厂校准的参考电压通道 VREFINT,其典型值约 1.21 V,且在芯片出厂时把该通道在 3.3 V 下的 12 位校准值写入系统存储区。

校准思路是:先采集 VREFINT 通道的原始码值,再读取出厂校准值,反推出当前实际 VDDA:

// VREFINT_CAL 为出厂校准值,典型地址 0x1FFF75AA
#define VREFINT_CAL_ADDR  0x1FFF75AAUL
#define VREFINT_CAL_VOLT  1210UL   // 单位 mV,示例值

uint16_t vref_raw = adc_buf_vref[0];   // VREFINT 通道原始码
uint16_t vref_cal = *(uint16_t *)VREFINT_CAL_ADDR;

// 当前 VDDA(mV)= 校准电压 × 校准码 / 实测码
uint32_t vdda_mv = (VREFINT_CAL_VOLT * vref_cal) / vref_raw;

// 用实际 VDDA 换算任意通道电压
uint32_t ch_mv = (uint32_t)adc_buf[i] * vdda_mv / 4095UL;

这样即使 VDDA 从 3.30 V 漂到 3.24 V,换算结果仍能保持稳定。注意 VREFINT 通道也要纳入规则组或单独采集,且采样时间要足够长(建议 ≥ 71.5 周期),否则内部参考尚未建立稳定。

采样时间优化:在精度与速度之间取舍

采样时间决定采样保持电容的充电时长。信号源内阻越大,所需采样时间越长。经验公式为:采样时间 ≥ (R_source + R_ADC) × C_ADC × ln(2^N),其中 N 为目标精度位数。工程上常用做法是:低阻信号(运放直驱)用 7.5~15 周期,中阻信号用 41.5~71.5 周期,高阻或内部通道用 247.5 周期以上。

采样时间过短会导致读数偏低且通道间串扰明显,表现为“后一个通道受前一个通道影响”。若发现相邻通道读数相关性强,应优先增大采样时间,而不是改滤波算法。

STM32 多通道 ADC 采集与校准数据链 模拟输入 IN0 温度 IN1 压力 IN4 液位 IN5 电池 IN6 电流 IN7 VREFINT 规则组 Regular Rank1 → Rank6 顺序转换 结果共用 ADC_DR 定时器触发 过采样 16× / 右移 2 采样时间 71.5 周期 注入组 Injected JDR1~JDR4 独立结果 可打断规则组 过流保护快通道 DMA 循环搬运 外设→存储器 地址自增 半字宽度 Circular 模式 半满/全满中断 adc_buf[6] 内存数组 [0] IN0 [1] IN1 [2] IN4 [3] IN5 [4] IN6 [5] VREF 下标 = Rank - 1 循环覆盖,无需重装 CPU 只读不搬 内部参考电压校准 VREFINT 实测码 vref_raw | 出厂校准码 vref_cal | 校准电压 1210 mV VDDA = 1210 × vref_cal / vref_raw 通道电压 = 原始码 × VDDA / 4095 采样时间选择:低阻信号 7.5~15 周期 | 中阻信号 41.5~71.5 周期 | 高阻/内部通道 ≥247.5 周期 判断依据:相邻通道读数强相关 → 增大采样时间;VDDA 漂移 → 用 VREFINT 实时反推;通道错位 → 检查 Rank 与数组下标

配置检查与结果判读

完成上述配置后,可用以下顺序快速验证:先单独采集 VREFINT,确认反推的 VDDA 在 3.2~3.4 V 之间;再给已知通道加固定电压,检查换算值与万用表读数偏差是否在 ±1% 以内;最后观察多通道连续采集时相邻通道是否串扰。若偏差偏大,按“采样时间 → 参考电压校准 → 过采样设置”的顺序排查,而不是先改软件滤波。

规则组通道数、DMA 缓冲区长度、Rank 顺序三者必须一致。任何一处改动都要同步更新,否则会出现数据错位或缓冲区越界。
过采样会成倍增加转换时间。若规则组 6 通道、每通道 16 倍过采样、采样时间 71.5 周期,单轮总时间约为单次转换的 16×6 倍,必须重新核算定时器触发周期,避免 DMA 尚未搬完就被下一次触发覆盖。