原理:为什么用VREFINT反推VDDA

STM32的ADC参考电压来自VDDA引脚。当电池电压下降时,VDDA随之变化,导致ADC转换结果失去绝对参考。VREFINT是芯片内部一个与电源无关的带隙基准电压,典型值1.20V(具体见数据手册),连接至ADC的固定内部通道(如STM32F1为通道17)。

  • 测量VREFINT得到ADC原始值 adc_vrefint
  • 已知VREFINT典型电压 VREFINT_CAL(如1.20V)
  • 则当前VDDA = VREFINT_CAL × 满量程 / adc_vrefint
  • 再用VDDA作为参考去换算电池分压后的ADC值,即可得到真实电池电压

该方法无需外部基准芯片,仅需一次出厂校准(或利用芯片内置校准值),成本极低。

关键校准数据来源

STM32在系统存储区(System Memory)中预置了VREFINT的校准值 VREFINT_CAL,该值是在VDDA=3.3V、温度25℃条件下测得的ADC原始值。不同系列地址不同,例如STM32F1为 0x1FFFF7BA(16位)。使用该值可消除芯片间VREFINT的个体差异。

  • 若芯片无内置校准值,可自行在已知VDDA(如3.3V)下测量一次VREFINT ADC值作为校准
  • 校准值仅代表典型条件,实际应用中建议结合软件滤波

配置步骤

  1. 使能ADC时钟与GPIO时钟,配置电池检测引脚为模拟输入
  2. 配置ADC为单次转换或扫描模式,使能内部通道VREFINT
  3. 设置采样时间足够长(VREFINT内阻较大,建议≥17.1μs)
  4. 依次转换VREFINT通道和电池通道,读取原始值
  5. 用公式计算VDDA与电池电压

完整代码示例(以STM32F1 HAL库为例)

#include "stm32f1xx_hal.h"

ADC_HandleTypeDef hadc1;

#define VREFINT_CAL_ADDR   ((uint16_t*)0x1FFFF7BA)  // 内置校准值地址
#define VREFINT_TYPICAL    1.20f                     // 典型VREFINT电压
#define ADC_FULL_SCALE     4095.0f                   // 12位ADC
#define BATT_DIV_RATIO     2.0f                      // 分压比(R1+R2)/R2

// 读取指定ADC通道的原始值
static uint16_t adc_read_channel(uint32_t channel)
{
    ADC_ChannelConfTypeDef sConfig = {0};
    sConfig.Channel = channel;
    sConfig.Rank = ADC_REGULAR_RANK_1;
    sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; // 长采样
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);
    HAL_ADC_Start(&hadc1);
    HAL_ADC_PollForConversion(&hadc1, 10);
    uint16_t val = HAL_ADC_GetValue(&hadc1);
    HAL_ADC_Stop(&hadc1);
    return val;
}

// 获取当前VDDA电压(单位V)
float get_vdda(void)
{
    uint16_t vrefint_raw = adc_read_channel(ADC_CHANNEL_VREFINT);
    uint16_t vrefint_cal = *VREFINT_CAL_ADDR;
    // 公式:VDDA = 3.3V * VREFINT_CAL / vrefint_raw
    // 等价于用典型值:VDDA = VREFINT_TYPICAL * FULL / raw
    // 推荐使用内置校准值以消除个体差异:
    return 3.3f * (float)vrefint_cal / (float)vrefint_raw;
}

// 获取电池电压(单位V)
float get_battery_voltage(void)
{
    float vdda = get_vdda();
    uint16_t batt_raw = adc_read_channel(ADC_CHANNEL_0); // 假设电池接PA0
    return vdda * (float)batt_raw / ADC_FULL_SCALE * BATT_DIV_RATIO;
}

注意事项

  • 采样时间:VREFINT内部阻抗较高,采样时间不足会导致读数偏低,建议≥17.1μs(如239.5周期@14MHz)
  • 多次平均:单次测量噪声较大,建议对VREFINT和电池通道各采样8~16次取平均
  • 温度影响:VREFINT随温度有微小漂移(典型±20ppm/℃),高精度场景需温度补偿
  • VDDA范围:确保VDDA在数据手册规定范围内(通常2.0V~3.6V),过低时VREFINT可能失效
  • 分压电阻:电池分压电阻不宜过大(建议≤100kΩ),否则ADC输入阻抗影响精度;同时考虑漏电流对电池的消耗
  • 校准值地址:不同STM32系列地址不同,务必查阅对应参考手册
  • 低功耗场景:测量时短暂开启ADC,完成后关闭以节省功耗

总结

利用VREFINT反推VDDA,无需外部基准即可实现电池电压的绝对测量,成本低、精度满足多数应用。核心是使用芯片内置校准值消除个体差异,并保证足够采样时间与软件滤波。掌握该方法后,可轻松扩展至低电量报警、充电管理等功能。