ADC电量检测与校准

ADC电量检测与校准方案分析
本文详细探讨了ADC电量检测的原理和误差影响,提出上电校准和满电校准两种策略,旨在提高电池电量检测的准确性,减少板级差异。通过校准系数的计算和存储,确保电量呈现步进变化,从而优化电池电量的监测过程。

ADC电量检测方案


1. 需求概述
1.1 目的
  • 提高ADC采样的准确性,消除板级差异。
1.2 背景
  • 实现采集的电压信息准确,覆盖电池容量0~100。
  • 在进入充电或退出充电时,消除跳跃大的电量跳变。
  • 保证各样品之间的一致性,消除板级间差异。
1.3 定义
  • kR:Adc分压网络的电阻系数比
  • kV:Adc参考电压与精度的系数比
  • k:分压网络电阻系数与adc参考电压的乘积
1.4 参考资料

ADC采样电路

2. 问题分析
2.1 采样原理
kR = 1 + R21 / R20

已知R21 = 2.2M,R20 = 1.5M,计算理论值 kR = 2.467。

Vbat = kR * Vdet

考虑R21和R20存在1%误差,则:

kRmax = 1 + (1.01*R21) / (0.99*R20) = 1.012kR
kRmin = 1 + (0.99*R21) / (1.01*R20) = 0.988kR

假设adc的基准电压为Vref,精度为Prec,adc采集到的adc数值为AdcValue,则:

kV = Vref /Prec
Vdec = kV * AdcValue
Vbat = kR * kV * AdcValue

仅考虑采样电阻的偏差

VbatMax = 1.012Vbat;
VbatMin = 0.988Vbat;
2.2 误差影响

如下表,为某型号电池电压与电量的对应关系

电压电量电压电量电压电量电压电量电压电量
3.00103.21913.33323.41633.4744
3.51753.54763.57273.58783.5999
3.608103.616113.622123.628133.63414
3.643153.648163.655173.660183.66519
3.669203.674213.678223.682233.68624
3.690253.693263.695273.698283.70129
3.704303.707313.709323.712333.71434
3.717353.719363.721373.725383.72839
3.730403.733413.736423.739433.74344
3.746453.749463.753473.757483.76149
3.765503.770513.774523.779533.78354
3.787553.792563.797573.802583.80859
3.813603.818613.824623.830633.83664
3.842653.848663.855673.861683.86869
3.874703.881713.889723.897733.90474
3.912753.919763.927773.934783.94379
3.952803.960813.968823.978833.98884
3.997854.007864.017874.027884.03689
4.045904.055914.064924.074934.08494
4.095954.105964.116974.128984.14199

如下图,为该型号的电池曲线
电量百分比
从图中可看出,当电量在30%附近时变化最缓慢,取实际电池电压为3.7V时

VbatMax = 1.012Vbat = 1.012 * 3.7 = 3.7444V;
VbatMin = 0.988Vbat = 0.988 * 3.7 = 3.6556V;
VbatMax - VbatMin = 3.744 - 3.6556 = 0.0888V = 88.8mV;

因此不理想的状态下,板级偏差可达88.8mv,此时分别对应的百分比是44%和14%,同一块电池在这两类情况下测量的电量相差达到30%。

3. 方案设计
3.1 校准原理

因为板级之间存在差异,需要校准kA * kV的系数乘积,又

Vbat = kR * kV * AdcValue = kR * Vref / Prec * AdcValue

从上述公式中可知,Adc的精度Prec是固定的,kR * Vref是造成差异性的原因,令

k = kR * Vref

则有

Vbat = k * AdcValue / Prec

在此方案中,因此给定已知的Vbat,则根据采样的adc数值可推出系数k。

3.2 校准接口

读取校准系数

/**
 * @fn Bool ReadAdcCoefficient(DWord *pdwCalibMark, float *pfCoefficient)
 * @brief Read the calibration mark and calibration coefficient of ADC
 * @details If the reading fails, the contents of the passed-in parameters will not be modified
 * @param[in] pdwCalibMark is Memory address of calibration mark
 * @param[in] pfCoefficient is Memory address of calibration coefficient
 * @return TRUE if read successfully, FALSE if not
 * @note None
 * @attention The valid byte of Flash read by this function is 8 lengths
 */
static Bool ReadAdcCoefficient(DWord *pdwCalibMark, float *pfCoefficient)
{
    Bool bRet = TRUE;
    UDat4Byte UDat;
    STFlashPage stFlashPage = {0u};

    //读取Flash的某一页内容 一共64字节
    bRet = FlashReadPage(&stFlashPage, FLASH_PAGE_MIN_NUM);

    //读取成功且长度正确 将读取的校准标记和校准系数进行替换
    if((bRet == TRUE) && (stFlashPage.wLen == 8u))
    {
        *pdwCalibMark = stFlashPage.adwDat[0u];
        UDat.dwDat = stFlashPage.adwDat[1u];
        *pfCoefficient = UDat.fDat;
    }
    //读取失败 保留之前的采样系数
    else
    {
        bRet = FALSE;
    }

    return bRet;
}

写入校准系数

/**
 * @fn Bool WriteAdcCoefficient(Word wAdcCalibValue, float fCalibBatVol)
 * @brief The calibration coefficient is calculated and stored by the incoming adc
 * and the expected voltage value
 * @details If the calculated coefficient is unreasonable, it will not be stored
 * @param[in] wAdcCalibValue: Adc value for calibration
 * @param[in] fCalibBatVol: Voltage for calibration
 * @return Returns TRUE if writing is successful, otherwise returns FALSE
 * @note None
 * @attention The reasonable interval range is adjusted according to the demand
 */
static Bool WriteAdcCoefficient(Word wAdcCalibValue, float fCalibBatVol)
{
    Bool bRet = TRUE;
    UDat4Byte UDat;
    STFlashPage stFlashPage = {0u};

    //通过传入校准的采样值和预期电压值计算校准系数
    UDat.fDat = fCalibBatVol / wAdcCalibValue * ADC_PRECISION;

    //系数在合理范围内
    if((UDat.fDat >= ADC_MIN_COEFFICIENT) && (UDat.fDat <= ADC_MAX_COEFFICIENT))
    {
        stFlashPage.adwDat[0u] = ADC_CALIBRATION_MARK;
        stFlashPage.adwDat[1u] = UDat.dwDat;
        stFlashPage.wLen = 8u;
        bRet = FlashWritePage(&stFlashPage, FLASH_PAGE_MIN_NUM);
    }
    else
    {
        bRet = FALSE;
    }

    return bRet;
}
3.3 上电校准方案

上电给定稳定的校准电源,校准方法如下:

上电读取Flash判断是否已校准?

是:通过给定的输入电压进行校准系数k,将系数k存储到Flash,并标记校准完成;
否:读取校准系数k。

    //读取校准系数
    ReadAdcCoefficient(&g_dwAdcCalibMark, &g_fAdcKRVCoefficient);
    if(g_dwAdcCalibMark != ADC_CALIBRATION_MARK)
    {
        //没有校准过 进入出厂校准模式
        if(WriteAdcCoefficient(ADC_GetSmpleValue(ADC_CHANNEL1, 5u), ADC_FACTORY_CALIB_BAT))
        {
            ReadAdcCoefficient(&g_dwAdcCalibMark, &g_fAdcKRVCoefficient);
        }
    }

优点:

  • 校准一次即可。

缺点:

  • 依赖给定的校准电压,与生产流程息息相关,给定校准电压的一致性决定了系数的可靠性。
3.4 满电校准方案

重新上电时使用默认校准系数k,不需要校准源,满电时自校准,校准方法如下:

每次采样过程中判断是否在充电状态

  • 存储充电过程中采集的最大adcValue值;
  • 如果检测到电压满电,通过已知的满电电压和最大的adcValue值校准系数k;
  • 如果新的校准系数k在合理范围内,则存储校准系数k到内部Flash中。
  1. 存储充电过程中采集的最大adcValue值。
    //从非充满到充满状态时  写入新的采样系数
    if((bIsChgFull == ENABLE) && (g_bChargFull != ENABLE))
    {
        byBatPercent = CHARGED_FULL_BAT;
        //新的系数保存 判断是否在合理范围里 不合理将使用默认的参数
        if(TRUE == WriteAdcCoefficient(s_wMaxAdcVal, ADC_FULL_CALIB_BAT))
        {
            ReadAdcCoefficient(&g_dwAdcCalibMark, &g_fAdcKRVCoefficient);
        }
        s_wMaxAdcVal = 0u;
    }

    //充满状态变化
    if(bIsChgFull != g_bChargFull)
    {
        g_bChargFull = bIsChgFull;
    }

优点:

  • 自动校准系数,不依赖校准电压和生产流程。

缺点:

  • 第一次充满前使用默认系数k,可能是不准的。
3.5 上电或满电校准方案

MCU实现自校准功能,通过上电校准或满电时校准,校准方法如下:

满足上电校准方案逻辑,只允许出厂时校准一次;
满足满电校准方案逻辑,每次满电重新校准。

优点:

  • 自动校准系数,不强制依赖校准电压和生产流程。

缺点:

  • 如果未进行上电校准,第一次充满前使用默认系数k,可能是不准的。
3.6 确保电量呈现步进变化
  • 周期性检测电池容量(30s),根据当前的采集到的电压值,计算电量百分比;
  • 考虑到电量可能检测不到100%的情况,当软件检测到充满时,将电量百分比强制变更为100%;
  • 当处于充电状态时,此次采集的电量百分比大于当前百分比,百分比自增1;
  • 当处于放电状态时,此次采集的电量百分比小于当前百分比,百分比自减1。
    //充电且满足电量百分比在向上变化 每次加1个百分比
    if((bIsCharge == ENABLE) && (byBatPercent > g_byBatteryPercentage))
    {
        g_byBatteryPercentage += 1u;
    }
    //当没在充电 电量百分比只能每次向下掉1个百分比
    else if((bIsCharge == DISABLE) && (byBatPercent < g_byBatteryPercentage))
    {
        g_byBatteryPercentage -= 1u;
    }

优点:

  • 无复杂的滤波方法,效率高。
  • 保证每次变化的电量百分比不超过预期。
  • 对插入充电和拔除充电有比较好的效果。

缺点:

  • 与实际采集有偏差,带延迟。

2021-08-18 李不清的烦恼,总结篇。

<think>我们已知用户使用的是SWM34SRET6微控制器(尽管可能存在笔误,但以用户提供的型号为准),并希望使用ADC测量电池电量。 根据引用内容,测量电池电量通常有两种方法: 1. 使用电量采集芯片(库仑计) - 精度高,但成本较高 2. 使用电阻分压法进行ADC采集,然后计算电压,再根据电池的放电曲线估算电量 - 成本低,但精度受ADC和分压电阻影响 用户的问题聚焦于使用ADC测量电池电量,因此我们将重点介绍电阻分压法。 步骤: 1. 电路设计:电阻分压电路,将电池电压分压到微控制器ADC的输入范围内。 2. ADC采集:使用微控制器的ADC模块采集分压后的电压。 3. 电压计算:根据分压比例反推电池电压。 4. 电量估算:根据电池的放电曲线(电压-电量关系)估算剩余电量。 详细说明: 一、电路设计 由于电池电压可能超过微控制器ADC的参考电压(例如3.3V),因此需要使用电阻分压。典型电路如下: ``` 电池正极 ---+--- R1 ---+--- ADC输入引脚 | | R2 | | | 电池负极 ---+----------+--- GND ``` 分压比:$ V_{adc} = V_{battery} \times \frac{R2}{R1+R2} $ 要求:$ V_{adc} $ 必须小于等于微控制器的ADC参考电压(通常为3.3V)。 例如,如果电池最高电压为12V,我们选择R1=30kΩ, R2=10kΩ,则分压比为1/4,ADC输入电压最大为12V/4=3V,在安全范围内。 注意: - 为了降低功耗,电阻值应选择较大(通常百千欧级),但注意ADC输入阻抗和漏电流的影响。 - 在ADC输入引脚处并联一个电容(例如0.1μF)以滤除噪声。 - 如果电池电压较低(如3.7V锂电池),则可以直接连接ADC(但注意不要超过参考电压)。 二、ADC采集计算 以SWM34SRET6为例,其ADC为12位精度,参考电压为3.3V(具体参考数据手册)。 步骤: 1. 初始化ADC,设置通道、采样时间等。 2. 启动ADC转换,获取原始值(0~4095)。 3. 计算ADC输入电压:$ V_{adc} = \frac{ADC\_value \times V_{ref}}{4095} $ 4. 计算电池电压:$ V_{battery} = V_{adc} \times \frac{R1+R2}{R2} $ 三、电池电量估算 电池的电压剩余电量的关系是非线性的,需要根据电池的放电曲线来估算。通常需要制作一个查找表(LUT)或使用经验公式。 例如,对于3.7V锂电池(如18650),典型放电曲线如下: - 4.2V 满电(100%) - 3.7V 约50% - 3.3V 耗尽(0%) 我们可以将电压区间划分为若干段,用线性插值估算电量。例如: 电压 >= 4.2V -> 100% 电压 >= 4.0V -> 90% ... 以此类推 注意:不同电池的放电曲线有差异,需参考电池规格书。 四、软件滤波 由于ADC采集可能存在噪声,通常需要采用软件滤波。常用方法: - 多次采样取平均 - 滑动平均滤波 - 中值滤波 五、低功耗考虑(如果应用需要) 根据引用[1],低功耗产品中需要注意: - 分压电阻的阻值要足够大(例如兆欧级)以减少电流消耗。 - 在不需要采样时,可以通过MOS管切断分压电路(参考引用[1]视频中的电路)。 六、代码示例(基于SWM34的ADC配置) 参考SWM34的固件库,以下为ADC初始化和采集的示例代码: ```c #include "SWM34.h" void ADC_Init(void) { ADC_InitStructure adc_init; ADC_Open(ADC, ADC_REF_INT, ADC_ALIGN_RIGHT); // 使用内部参考电压,右对齐 ADC_InitStructureInit(&adc_init); adc_init.clk_div = 8; // 时钟分频 adc_init.sampleTime = 55; // 采样时间(周期数) ADC_Init(ADC, &adc_init); ADC_ChannelEnable(ADC, ADC_CHANNEL0, ENABLE); // 使能通道0 } uint32_t ADC_Read(void) { ADC_Start(ADC); while(ADC_IsBusy(ADC)); // 等待转换完成 return ADC_ReadData(ADC, ADC_CHANNEL0); } int main(void) { uint32_t adc_value; float v_adc, v_battery; const float R1 = 30000.0; // 30kΩ const float R2 = 10000.0; // 10kΩ const float ref_voltage = 3.3; // 参考电压3.3V SystemInit(); ADC_Init(); while(1) { adc_value = ADC_Read(); v_adc = (adc_value * ref_voltage) / 4095.0; v_battery = v_adc * (R1 + R2) / R2; // 根据v_battery估算电量(此处需根据电池特性实现) // ... // 延时 for(int i=0; i<1000000; i++); } } ``` 七、注意事项 - 电阻精度:使用1%精度的电阻以保证分压比准确。 - 温度影响:电阻和电池电压都可能受温度影响,高精度应用需考虑温度补偿。 - 参考电压:如果微控制器内部参考电压精度不够,可以使用外部高精度参考电压源。 八、相关问题 根据以上内容,生成相关问题: §§相关问题§§ 1. 如何选择分压电阻的阻值以平衡功耗和精度? 2. 如何制作锂电池的电压-电量查找表? 3. 在低功耗应用中,如何设计分压电路的电源控制? 4. SWM34SRET6的ADC参考电压有哪些选择?如何提高ADC测量精度?</think>以下是基于SWM34SRET6微控制器的电池电量检测系统设计指南,结合ADC测量技术和电阻分压法实现: ### 一、系统设计原理 1. **测量原理** - 采用电阻分压法将电池高压降至MCU的ADC输入范围(0-3.3V) - ADC采集后通过公式反推实际电压: $$ V_{battery} = \frac{ADC_{value} \times V_{ref}}{4095} \times \frac{R1 + R2}{R2} $$ - 根据电池放电曲线(电压-电量关系)估算剩余电量[^2] 2. **电路拓扑** ```mermaid graph LR A[电池+] --> B[分压电路] B --> C[ADC输入引脚] C --> D[SWM34SRET6] D --> E[数据处理] E --> F[电量显示/输出] B --> G[GND] ``` ### 二、硬件电路设计 #### 1. 分压电路设计(以12V电池为例) ``` 电池+ --- R1 ---+--- ADC_IN | | R2 C1(0.1μF) | | GND GND ``` - **元件选型**: - R1 = 100kΩ(精度1%) - R2 = 33kΩ(精度1%) - C1 = 0.1μF陶瓷电容(消除高频噪声) - **分压比计算**: $$ \frac{V_{ADC}}{V_{bat}} = \frac{R2}{R1+R2} = \frac{33}{133} ≈ 0.248 $$ - 12V电池 → ADC输入最大2.98V(在3.3V安全范围内) #### 2. 低功耗优化设计 - 增加MOS管控制电路(关键时断开分压电路): ``` 电池+ --- MOSFET_Gate | MOSFET_Source --- R1 --- ADC_IN | GND ``` - 控制逻辑: - 采样前:GPIO输出高电平→MOS导通 - 采样后:GPIO输出低电平→MOS截止 - 降低静态电流至μA级[^1] ### 三、软件实现(基于SWM34库) #### 1. ADC配置代码 ```c #include "SWM34.h" void ADC_Init(void) { ADC_InitStructure adc_init; // 配置ADC通道0(PA0引脚) GPIO_Init(GPIOA, PIN0, 0, 0, 0); // 模拟输入模式 ADC_InitStructureInit(&adc_init); adc_init.clk_div = 8; // 时钟分频 adc_init.sampleTime = 55; // 采样时间=55周期 ADC_Open(ADC, ADC_REF_INT, ADC_ALIGN_RIGHT); // 内部参考/右对齐 ADC_Init(ADC, &adc_init); ADC_ChannelEnable(ADC, ADC_CHANNEL0, ENABLE); } uint16_t Read_BatteryVoltage(void) { ADC_Start(ADC); while(ADC_IsBusy(ADC)); // 等待转换完成 return ADC_ReadData(ADC, ADC_CHANNEL0); } ``` #### 2. 电压计算电量估算 ```c #define R1 100000.0 #define R2 33000.0 #define V_REF 3.3 float Get_BatteryLevel(void) { uint16_t adc_val = Read_BatteryVoltage(); float adc_voltage = (adc_val * V_REF) / 4095.0; float bat_voltage = adc_voltage * (R1 + R2) / R2; // 锂电池电压-电量对应表(需根据实际电池特性校准) const float voltage_levels[] = {3.0, 3.3, 3.6, 3.9, 4.2}; // 电压点 const uint8_t soc_levels[] = {0, 20, 50, 80, 100}; // 对应电量% // 线性插值计算电量 for(int i=0; i<4; i++) { if(bat_voltage >= voltage_levels[i] && bat_voltage < voltage_levels[i+1]) { return soc_levels[i] + (soc_levels[i+1]-soc_levels[i]) * (bat_voltage - voltage_levels[i]) / (voltage_levels[i+1]-voltage_levels[i]); } } return (bat_voltage < voltage_levels[0]) ? 0 : 100; } ``` ### 四、精度提升关键措施 1. **ADC校准技术** ```c // VREF内部校准(上电执行一次) ADC_VrefintEnable(ENABLE); delay_ms(10); uint16_t vref_cal = ADC_ReadData(ADC, ADC_CHANNEL_VREF); V_REF = 1.2 * 4095 / vref_cal; // 更新实际参考电压 ``` 2. **数字滤波算法** ```c #define SAMPLE_SIZE 16 uint16_t filtered_ADC(void) { uint32_t sum = 0; for(int i=0; i<SAMPLE_SIZE; i++) { sum += Read_BatteryVoltage(); delay_us(10); } return sum / SAMPLE_SIZE; // 均值滤波 } ``` 3. **温度补偿(需NTC传感器)** $$ V_{corrected} = V_{measured} \times [1 + 0.003 \times (T - 25)] $$ - 其中T为当前温度(℃),0.003为典型温度系数 ### 五、设计验证步骤 1. 使用万用表测量实际分压比 2. 空载/带载状态下对比ADC读数真实电压 3. 充放电循环测试电量估算精度 4. 低功耗模式验证电流消耗(目标<10μA) > ⚠️ **安全提示**: > - 分压电阻功率需满足 $ P \geq \frac{V_{bat}^2}{R1+R2} $ > - ADC输入口加TVS管防过压 > - 临界电量(如<10%)触发硬件保护电路 --- ### 六、扩展应用 1. **充放电管理** - 结合充电IC实现智能充停控制 - 库仑计集成(如MAX17048)提升精度[^1] 2. **无线传输方案** ```mermaid graph LR A[SWM34SRET6] --> B[ADC采集] B --> C[电量计算] C --> D[蓝牙/WiFi模块] D --> E[手机APP] ``` ---
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值