基于 STM32 的 ADC 多通道采样时采样电容保持时间与通道串扰的边界测试方法

一、问题背景

在嵌入式系统中,STM32 的 ADC 常被用于多通道模拟信号采集。当通道切换频繁时,采样电容(Sample-and-Hold Capacitor)的充电时间不足,会导致前一个通道的残余电荷影响当前通道的采样值,即产生通道串扰。这种串扰在高速采集或高阻抗信号源时尤为明显,严重时会造成数据失真。

二、原理剖析

2.1 采样电容的工作机制

STM32 的 ADC 采用逐次逼近型(SAR)架构,内部有一个采样电容。采样阶段,开关闭合,电容充电至输入电压;保持阶段,开关断开,电容电压被比较器逐次比较。采样时间(Sampling Time)必须足够长,使电容电压稳定到输入电压的精度范围内(通常为 1/2 LSB)。

2.2 通道串扰的根源

多通道采集时,ADC 在通道间切换。若采样时间过短,电容未完全放电或充电,残留电荷会叠加到下一次采样值上。串扰大小取决于:

  • 采样时间(可配置的采样周期数)
  • 通道切换顺序(相邻通道的电压差)
  • 信号源阻抗(影响充电时间常数)

2.3 边界测试的必要性

不同 STM32 型号、不同时钟频率、不同供电电压下,最优采样时间不同。通过边界测试,可以找到“串扰可接受”的最小采样周期,从而在保证精度的前提下提高采样速率。

三、测试方法设计

3.1 测试原理

构造两个通道:CH0 接高电平(如 3.3V),CH1 接低电平(如 0V)。交替采样,并改变采样周期。当采样周期较短时,CH1 的采样值会因 CH0 的残留而偏高,串扰率 = (V_CH1_实际 - V_CH1_理想) / (V_CH0 - V_CH1)。记录不同采样周期下的串扰率,找到串扰率 < 1 LSB 的最小采样周期。

3.2 硬件准备

  • STM32 开发板(以 STM32F103 为例)
  • 两个稳定的直流电源(或分压电阻)
  • 示波器(可选,用于验证)

3.3 软件配置步骤

  1. 初始化 ADC:配置为扫描模式,通道序列为 CH0, CH1。
  2. 设置采样时间:通过寄存器调整采样周期数(如 1.5、7.5、13.5、28.5、41.5、55.5、71.5、239.5 周期)。
  3. 循环采集:每次切换采样时间,采集多组数据,计算平均值和串扰率。
  4. 记录结果:输出串扰率与采样周期的关系。

四、完整代码示例

以下代码基于 STM32 HAL 库,演示了自动扫描不同采样时间并输出串扰率。

#include "stm32f1xx_hal.h"

ADC_HandleTypeDef hadc1;

static void ADC_Init(void)
{
    __HAL_RCC_ADC1_CLK_ENABLE();
    hadc1.Instance = ADC1;
    hadc1.Init.ScanConvMode = ENABLE;          // 扫描模式
    hadc1.Init.ContinuousConvMode = DISABLE;    // 单次转换
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.NbrOfConversion = 2;             // 两个通道
    HAL_ADC_Init(&hadc1);

    // 配置通道0和通道1,采样时间在循环中修改
    ADC_ChannelConfTypeDef sConfig = {0};
    sConfig.Channel = ADC_CHANNEL_0;
    sConfig.Rank = 1;
    sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5; // 初始值
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);

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

// 设置采样时间(通过修改通道配置)
static void SetSamplingTime(uint32_t samplingTime)
{
    ADC_ChannelConfTypeDef sConfig = {0};
    sConfig.Channel = ADC_CHANNEL_0;
    sConfig.Rank = 1;
    sConfig.SamplingTime = samplingTime;
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);

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

// 读取两个通道的转换值
static void ReadADC(uint16_t *ch0, uint16_t *ch1)
{
    HAL_ADC_Start(&hadc1);
    HAL_ADC_PollForConversion(&hadc1, 10);
    *ch0 = HAL_ADC_GetValue(&hadc1);
    HAL_ADC_PollForConversion(&hadc1, 10);
    *ch1 = HAL_ADC_GetValue(&hadc1);
    HAL_ADC_Stop(&hadc1);
}

int main(void)
{
    HAL_Init();
    ADC_Init();

    // 假设 CH0 接 3.3V,CH1 接 0V,参考电压 3.3V,12位分辨率
    uint32_t samplingTimes[] = {
        ADC_SAMPLETIME_1CYCLE_5,
        ADC_SAMPLETIME_7CYCLES_5,
        ADC_SAMPLETIME_13CYCLES_5,
        ADC_SAMPLETIME_28CYCLES_5,
        ADC_SAMPLETIME_41CYCLES_5,
        ADC_SAMPLETIME_55CYCLES_5,
        ADC_SAMPLETIME_71CYCLES_5,
        ADC_SAMPLETIME_239CYCLES_5
    };
    const char *timeNames[] = {"1.5", "7.5", "13.5", "28.5", "41.5", "55.5", "71.5", "239.5"};

    for (int i = 0; i < 8; i++) {
        SetSamplingTime(samplingTimes[i]);
        uint32_t sum0 = 0, sum1 = 0;
        const int samples = 100;
        for (int j = 0; j < samples; j++) {
            uint16_t ch0, ch1;
            ReadADC(&ch0, &ch1);
            sum0 += ch0;
            sum1 += ch1;
        }
        float avg0 = (float)sum0 / samples;
        float avg1 = (float)sum1 / samples;
        // 理想情况下 ch1 应为 0,串扰率 = avg1 / avg0 * 100%
        float crosstalk = (avg1 / avg0) * 100.0f;
        printf("Sampling time: %s cycles, CH0=%.2f, CH1=%.2f, Crosstalk=%.3f%%\n",
               timeNames[i], avg0, avg1, crosstalk);
    }

    while (1) {}
}

五、测试结果分析

  • 当采样周期从 1.5 增加到 239.5 时,串扰率应逐渐下降。
  • 通常,当采样周期 ≥ 13.5 时,串扰率可降至 0.1% 以下(对应 12 位 ADC 的 1 LSB 约为 0.024%)。
  • 若串扰率始终较高,需检查信号源阻抗,必要时增加外部电容或使用缓冲器。

六、注意事项

  • 信号源阻抗:高阻抗源会增大充电时间常数,需更长采样时间。建议信号源阻抗 < 10kΩ。
  • 通道顺序:将电压接近的通道相邻排列,可减小串扰影响。
  • 参考电压稳定性:测试时务必使用稳定的参考电压,否则结果失真。
  • 多次采样取平均:为消除噪声,每次测试应采集多次取平均值。
  • 不同型号差异:STM32F1 与 F4 系列采样电容值不同,需重新测试。
  • 使用 DMA:实际项目中建议使用 DMA 连续采集,减少 CPU 干预,但测试时需注意时序一致性。

七、总结

通过上述边界测试方法,开发者可以量化采样电容保持时间与通道串扰的关系,为多通道 ADC 应用选择最优采样周期。该方法简单实用,可移植到其他 STM32 系列,是嵌入式系统设计中的重要调试手段。