STM32 ADC轮询模式读取值不稳定问题咨询
问题背景
采用轮询模式读取5个ADC通道,每个通道采集10个样本并计算平均值。发现样本数组存在重复性极强的不稳定现象:初始读数数值较高,之后逐渐降低至最后一个样本。
为调试,已将ADC通道连接的传感器替换为固定电阻以获取固定读数。ADC自校准在首次通道读取前执行,每30秒重复一次校准与5次读取操作。
原始采样数据
首次读数1855,末次读数1850
【原始输出值】
ui32_RawTemp[0]: 1855
ui32_RawTemp[1]: 1855
ui32_RawTemp[2]: 1854
ui32_RawTemp[3]: 1852
ui32_RawTemp[4]: 1852
ui32_RawTemp[5]: 1852
ui32_RawTemp[6]: 1851
ui32_RawTemp[7]: 1851
ui32_RawTemp[8]: 1850
ui32_RawTemp[9]: 1850
原始代码片段(单通道,已移除HAL错误处理)
#define AVG_NUMBER 10
sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5; sConfig.SingleDiff = ADC_SINGLE_ENDED; sConfig.OffsetNumber = ADC_OFFSET_NONE; sConfig.Offset = 0; // 读取通道5上的TEMP1_ADC sConfig.Channel = ADC_CHANNEL_5; ADC_HAL_ErrCode = HAL_ADC_ConfigChannel(&hadc1, &sConfig); if(ADC_HAL_ErrCode != HAL_OK) { ...... } ui32_Temp = 0; for(ui8_AVGCounter=0; ui8_AVGCounter < AVG_NUMBER; ui8_AVGCounter++) { HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 10); ui16_RawTemp[ui8_AVGCounter] = HAL_ADC_GetValue(&hadc1); // 读取ADC寄存器 ui32_Temp = ui32_Temp + (uint32_t)ui16_RawTemp[ui8_AVGCounter]; // 累加所有读取值 ui16_avgADC[0] = (uint16_t)(ui32_Temp / AVG_NUMBER); // 计算平均值 HAL_ADC_Stop(&hadc1); }
优化后情况
在循环中添加vTaskDelay(1)后读数大幅稳定,但1ms延迟时长过大。
优化后代码片段
// 读取通道5上的TEMP1_ADC sConfig.Channel = ADC_CHANNEL_5; sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5; sConfig.SingleDiff = ADC_SINGLE_ENDED; sConfig.OffsetNumber = ADC_OFFSET_NONE; sConfig.Offset = 0; ADC_HAL_ErrCode = HAL_ADC_ConfigChannel(&hadc1, &sConfig); if(ADC_HAL_ErrCode != HAL_OK) { ..... } ui32_Temp = 0; for(ui8_AVGCounter=0; ui8_AVGCounter < AVG_NUMBER; ui8_AVGCounter++) { HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 10); ui32_RawTemp[ui8_AVGCounter] = HAL_ADC_GetValue(&hadc1); // 读取ADC寄存器 HAL_ADC_Stop(&hadc1); ui32_Temp = ui32_Temp + ui32_RawTemp[ui8_AVGCounter]; // 累加所有读取值 vTaskDelay(1); } ui16_avgADC[0] = (uint16_t)((ui32_Temp + (0.5 * AVG_NUMBER)) / AVG_NUMBER); // 四舍五入并计算平均值
优化后采样数据
【新输出值】
ui32_RawTemp[0]: 1855
ui32_RawTemp[1]: 1855
ui32_RawTemp[2]: 1855
ui32_RawTemp[3]: 1855
ui32_RawTemp[4]: 1855
ui32_RawTemp[5]: 1855
ui32_RawTemp[6]: 1855
ui32_RawTemp[7]: 1855
ui32_RawTemp[8]: 1856
ui32_RawTemp[9]: 1855
咨询问题
- 该现象的原因是什么?
- 我的代码是否存在问题?
- 首次读数最不稳定,首次ADC转换前需执行什么操作?
解答
1. 现象原因
核心是ADC内部或外围采样电路的热漂移问题:
- 刚启动ADC时,采样保持电容、内部放大器等器件处于冷态,连续采样产生的功耗会让器件升温,改变其电气特性(如电容值、放大器增益偏移),导致采样值逐渐漂移,直到器件温度稳定后读数才趋于一致。
- 固定电阻测试仍有此现象,排除了传感器本身的问题,进一步指向ADC自身或外围采样电路的热稳定特性。延迟后稳定,是因为给了电路足够时间达到热平衡。
2. 代码存在的问题
- 平均值计算时机错误:原始代码在循环内部每次累加后就计算平均值,属于无效操作,平均值应在所有样本采集完成后统一计算,既节省资源也避免逻辑混乱。
- ADC启停逻辑冗余:每次采样都调用
HAL_ADC_Start()和HAL_ADC_Stop(),频繁启停会导致ADC内部电路反复初始化,加剧热波动和不稳定。可改为一次启动ADC,完成所有采样后再停止。 - 采样间隔无合理控制:原始代码采样间隔极短,电路没有足够恢复时间,连续采样的热积累会加速漂移。
3. 首次ADC转换前的必要操作
- 执行预采样预热:正式采样前连续进行5-10次无效采样,让ADC内部电路达到热稳定,丢弃这些预热样本后再开始正式采样。
- 校准后等待稳定:自校准完成后,不要立即采样,根据芯片手册推荐等待几十到几百微秒,让校准后的电路参数稳定。
- 优化供电稳定性:确保外部采样电路的供电滤波电容足够,避免启动时的电压波动影响采样;必要时可在首次采样前给采样电路预上电一段时间。
内容的提问来源于stack exchange,提问作者IGtti

