STM32F072C8U6 ADC读取异常排查:值与示波器不符且读取不稳定
STM32F072C8U6 ADC采样异常问题排查
我在读取STM32F072C8U6 MCU引脚的ADC值时,遇到两个问题:读取值与示波器测量结果不一致,且存在间歇性读取失败的情况。多次重新配置ADC初始化均未解决问题。相关代码如下:
读取电压代码
bool PM_ReadVoltage(const TestPointSpec *spec, float *val, bool raw) { bool success = false; ADC_ChannelConfTypeDef channelConf; HAL_ADC_Stop(&hadc); HAL_ADCEx_Calibration_Start(&hadc); channelConf.Rank = 1; channelConf.SamplingTime = CONFIG_ADC_SAMPLE_TIME; channelConf.Channel = spec->adcChannel; if (HAL_ADC_ConfigChannel(&hadc, &channelConf) != HAL_OK) { return false; } HAL_ADC_Start(&hadc); HAL_StatusTypeDef rc = HAL_ADC_PollForConversion(&hadc, 500); if (rc == HAL_OK) { uint32_t res = HAL_ADC_GetValue(&hadc); #if CONFIG_CALIB_MODE == 1 COM_Trace("Raw %s=%d", spec->name, res); #endif if (!raw && spec->isHalf) { *val = (float)res * gAdcResolution * 2; } else { *val = (float)res * gAdcResolution; } } else { goto out; } *val += *val * spec->testPointVolLinearCalib; success = true; out: channelConf.Rank = ADC_RANK_NONE; HAL_ADC_ConfigChannel(&hadc, &channelConf); HAL_ADC_Stop(&hadc); HAL_Delay(100); return success; }
HAL初始化代码
static void ADC1_Init(void) { ADC_ChannelConfTypeDef sConfig = {0}; hadc.Instance = ADC1; hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1; hadc.Init.Resolution = ADC_RESOLUTION_12B; hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD; hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV; hadc.Init.LowPowerAutoWait = DISABLE; hadc.Init.ContinuousConvMode = ENABLE; hadc.Init.DiscontinuousConvMode = DISABLE; hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; hadc.Init.DMAContinuousRequests = DISABLE; hadc.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN; if (HAL_ADC_Init(&hadc) != HAL_OK) { Error_Handler(); } HAL_ADCEx_Calibration_Start(&hadc); HAL_Delay(10); sConfig.Channel = ADC_CHANNEL_VREFINT; sConfig.Rank = 1; sConfig.SamplingTime = CONFIG_ADC_SAMPLE_TIME; const uint16_t VREFIN_CAL = *((uint16_t*)0x1FFFF7BA); if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) { Error_Handler(); } HAL_ADC_Start(&hadc); if (HAL_ADC_PollForConversion(&hadc, 500) == HAL_OK) { uint32_t val = HAL_ADC_GetValue(&hadc); uint32_t vdda = (3300UL * (uint32_t)VREFIN_CAL) / val; PM_SetVDDA(vdda / 1000.0f); } sConfig.Rank = ADC_RANK_NONE; HAL_ADC_ConfigChannel(&hadc, &sConfig); HAL_ADC_Stop(&hadc); }
GPIO ADC配置
#define ADC_PIN_NUM 7 #define ADC_INPUT_3 { .port = GPIOA, .pin = GPIO_PIN_3 } #define ADC_INPUT_4 { .port = GPIOA, .pin = GPIO_PIN_4 } #define ADC_INPUT_5 { .port = GPIOA, .pin = GPIO_PIN_5 } #define ADC_INPUT_6 { .port = GPIOA, .pin = GPIO_PIN_6 } #define ADC_INPUT_7 { .port = GPIOA, .pin = GPIO_PIN_7 } #define ADC_INPUT_8 { .port = GPIOB, .pin = GPIO_PIN_0 } #define ADC_INPUT_9 { .port = GPIOB, .pin = GPIO_PIN_1 }
问题分析与修复建议
- 校准操作滥用:每次采样前执行
HAL_ADCEx_Calibration_Start会破坏ADC稳定性,校准只需在初始化时执行一次。移除PM_ReadVoltage中的校准调用。 - 连续转换模式冲突:初始化时开启连续转换模式,但采样逻辑是单次触发,会导致数据溢出或读取旧值。将
ContinuousConvMode改为DISABLE。 - 采样时间不足:若
CONFIG_ADC_SAMPLE_TIME过小,无法让输入电压稳定。建议设置为最长采样时间(如ADC_SAMPLETIME_239CYCLES_5)验证偏差问题。 - 冗余通道配置:采样完成后无需将通道Rank设为
ADC_RANK_NONE,简化流程,直接在下次采样时重新配置通道即可。 - VDDA校准精度:单次采样VREFINT易受干扰,建议多次采样取平均值后计算VDDA,提高转换精度。
内容的提问来源于stack exchange,提问作者jayy_nguyen
相关产品推荐
相关产品推荐

