# 基于 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 库,演示了自动扫描不同采样时间并输出串扰率。 ```c #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 系列,是嵌入式系统设计中的重要调试手段。