问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

ESP32C3 ADC 测量锂电池电压

创作时间:
作者:
@小白创作中心

ESP32C3 ADC 测量锂电池电压

引用
51CTO
1.
https://blog.51cto.com/u_16099287/13174675

在低功耗设备中,准确测量锂电池电压是一个常见的需求。本文将介绍如何使用ESP32C3微控制器通过ADC(模数转换器)来测量锂电池电压,重点讲解了使用分压电路进行电压采集的方案,并提供了详细的配置步骤和代码示例。

电量采集有两种方法:

  1. 加电量采集芯片(库仑计)
  2. 分压电路进行ADC采集,进而计算出电压

对于很多低功耗产品来说,外加一个库仑计的成本是不能接受的,所以一般都是使用分压电路来进行电池电压的采集,而一般低功耗产品都把电阻加大至1M-2M,入下图就是经典的电池电压采集电路。

这样看来貌似很简单的样子,把ADC值取出来,然后再转换为电压即可,实则不然!因为低功耗设备是靠电池供电的,而一般的锂电池的电压是随着电量的降低,电压也随之降低,虽然咱们的电路上一般会加上LDO电源芯片,来让3.6V的电池降压至3.3V给MCU供电。(注:特殊的锂亚电池可以长久保持稳压状态即使电量几乎耗尽)

一般ADC的电压转换方法为:电池电压=3.3*ADC采集值/分压比例

可是咱们为什么用3.3V来乘呢?因为我们已经默认了MCU的电压为3.3V,如果电池电压一直稳定或者是常供电设备,这一点无需关注。但是可充电的锂电池一般随着电量降低电压也会降低,那么当电池的电压降到3.3V以下,经过LDO以后,MCU的电压也会在3.3V以下,再使用3.3带入公式计算,显然是不合理的!

其实ST芯片已经有了一个算法来计算MCU
VDDA的电压值,这个值是MCU内部自动实时监控的,其中它连接在ADC_IN17通道,通过这个方法咱们就可以确定VDDA的真实电压,同时VREFINT_CAL(校准值)存在MCU内部的寄存器内,而且是只读的。 STM32L051 datasheet如图:

STM32L051 使用手册如图:

那么新的计算公式应该是这样的:

电池电压=真实的VDDA电压ADC采集值/分压比例

其中真实的VDDA电压=3V基准值/VDDA采集值

电池电压=(3V*基准值/VDDA值)*ADC采集值/分压比例

这样就可以得到非常准确的电池电压了,即使电池降到3.3V以下也是非常准确的。 ADC采集多路可以使用DMA也可以开启连续采集,然后我们把ADC值都取出来带入公式即可。

实战举例:

  1. 配置电池ADC引脚
  2. 勾选内部参考电压 其实是ADC_IN17
  3. 使能连续转换模式
  4. 配置低功率等待
  5. 配置auto off
  6. 提高采样率提高准确度

下方代码就实现了初始化,校准,然后启动ADC采集,便开始了连续转换(可以配置为读取之后开始下次循环)打印5次最终电池电量BAT_val。分压比例为0.5,所以是4096/2=2028.即:BAT_val = VDDA_val*BAT_DATA/2048;

ADC1->CR |= ADC_CR_ADCAL; //校准ADC
    while((ADC1->ISR&ADC_ISR_EOCAL)!=ADC_ISR_EOCAL);//等待ADC校准完成
    ADC1->ISR |= ADC_ISR_EOCAL;//校准ADC完成
    VREFINT_CAL = *(uint16_t*)0x1FF80078; //获取校准值
    ADC1->CR |= ADC_CR_ADSTART;
    //HAL_ADC_Start(&hadc); //启动ADC
    EnableUsart_IT();
    printf("123456\r\n");
    for(uint8_t i=5; i>0; i--)
    {
      HAL_Delay(1000);
            while((ADC1->ISR&ADC_ISR_EOC)!=ADC_ISR_EOC);
            BAT_DATA = ADC1->DR;
            while((ADC1->ISR&ADC_ISR_EOC)!=ADC_ISR_EOC);
            VREFINT_DATA = ADC1->DR;
            VDDA_val = 3.0*VREFINT_CAL/VREFINT_DATA;
        printf("VDDA_val=%.2f\r\n",VDDA_val);
            BAT_val = VDDA_val*BAT_DATA/2048;
            printf("BAT_val=%.2f\r\n",BAT_val);
      printf("wait %dS to sleep\r\n",i);
    }

根据我的测试可以满足项目需求!

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号