数码之家

 找回密码
 立即注册
搜索
查看: 18859|回复: 163

2024年封帖,简单的电池容量测试仪的简单分析及验证

    [复制链接]
发表于 2024-12-24 17:04:48 | 显示全部楼层 |阅读模式
最近的折腾,汇总到一个帖子里,算是给2024年的交的一个作业吧。
分三个内容:
一、星河电子的XH-M240锂电池容量测试仪的拆并分析
二、ZB2L3电池容量测试仪的拆并画电路原理图
三、用Arduino验证测试


由于手头好几个充电宝使用的是18650锂电池,几年下来,容量有所减少。想着都拆开测量一下单节容量,重组一下。于是买了两种:

一、星河电子的XH-M240

整体情况
放电电流500mA左右,测量一节电池时间较长。

电路功能

电路

各功能电路分析如下:

系统的电源有两种供电方式,一是使用左边的电池仓,放入18650电池供电;二是使用外接5V电源供电。
两路供电使用了隔离二极管,防止倒灌。之后通过开关控制系统供电。供电经过C1滤波后,由U1时行3.3V稳压,整个系统使用的是3.3V电源的。

核心是一块标有FMD的MCU,16脚封装,外围电路有:LED指示、按键采样、电流电压采样、LED数码显示部分。
MCU的1、16脚是电源和地。
MCU的5、6、7脚是数码管正在显示毫瓦时、毫安时和正在测量的LED指示灯。
MCU的10脚是接收按键按下信号的。
MCU的11是被测电池的电压采样输入。
MCU的9脚是控制MOS管开关的,以控制是否进行放电测量。
MCU的12脚是被测电池放电电流的采样输入。
MCU的2、3脚是数码管显示“数字”的串行输出信号。
MCU的4、13、14、15脚是控制数码管位显示的信号。
另外,MCU的9、11脚应该是串行端口。
从引脚功能推断应该是辉芒微的FT64F0A3。

按键部分使用了MCU的一个端口,采用的是R19与R20、R21、R22不同的分压值,从而表示不同的按键被按下。

显示部分使用了一块无字MCU,用以将串行的信号进行“译码”,从而8段的数码显示。


电池电压的采样是由电阻R6、R7分压(衰减了0.118倍)后进行采样的,当电池为4.2V时,采样电压为4.2*6.8/(6.8+51)=0.5V。ADC内部参考电压可以为0.5V、2V、3V。不知配置为哪一档。
上图中的放电开关及电流采样可以简化为下图

图中,NMOS管U2完成被测电池放电回路的通断控制,MOS管的S极接地,参考电压为0V。电流采样电阻R2一端接地,另一端接被测电池的负极,当U2接通放电回路后,电流经过R8、U2、R2形成放电回路。此时,R2下端的电位相对于参考地的电位是负电位,而采样是从R2下端采集电压的,所以电流采样电压为负电压。当电池放电电流为3A时,采样电压为-60mV,百毫伏以下级的负电压一般要经过进一步处理,才能与后续电路使用。

电流采样放大部分如下图:

简化如下图:

输入与输出的关系如图,

在电路中,输入Vin不会出现正值,所以,输出Vout的在66.79mV以上,并随着输入Vin越负输出越高。
如果放电电流为3A时,采样放大输出为:
Vout =30.3*3*20+66.79=1884.79(mV)=1.88V。
可能使用了内部2V参考。

运算放大器的输入输出关系如下

公式推导

相对误差

使用一节锂电池供电的这个电池仓,只能供电,不能外接充电,利用这个电池仓,加了一块4056的充电板,这样就可以电池充电、电池供电、USB供电了。

USB供电状态

整体电路图


二、另一种常见的是ZB2L3电池容量测试仪

外接负载放电,最大测试电流是3A,可测电1.2到12V的电池,也就是可以测试单块铅酸电池。配有两个7.5欧姆(5W)的放电电阻,如下图

我买到的是下面这样的,关键(运放周边的电阻)信息不一样的

为了看清楚,拆下部分元件

背面

标上元件代号

部分元件

电路如下

与上面的锂电池容量测试仪的原理一样,不做分析。
注意电流采样放大器周边电阻阻值,与上面的锂电池容量测试仪有一点不一样,其实,这个ZB2L3电池容量测试仪与上面的应该是一样的,我这个可能是山(修)寨(改)的,所以运放的放大倍数不一样。
这个输入与输入关系如下图
不知MCU中的算法是什么样的

相对误差如下:


三、自己献个丑

先仿真一个看看,

搭一个实际电路试试。

用8欧姆电阻放电,其中的4欧姆电阻做电流采样,现场情况。

使用Arduino板载5V做为参考电位,

用的是U盾上拆下来的ST7565的屏,放电开关使用NMOS管A09T。

电路如下:

改用0.5欧姆电阻进行采样,放电为(5.6+0.5=)6.1欧姆电阻。与NMOS管搭有一块洞洞板上

测量结果的查对误差如下

换了一个参考表,测量结果

两块参考表一起用,在500mAh时的情况

在1000mAH时的情况

看来两个参考表也是有误差的,而且很明显。自己做的肯定是有误差,想做修正,又没有标准源,只好找这两个参考表的中间值了。在算法中做个小修正

最后的结果如下

两个参考表在前后的位置也表现不同。
先不管了。误差产生:

一是参考电压的选择,是否准确;可用板载的电压,不同USB设备供电电压是不同的,所以可以选用外部参考电压。当然要求不高也可使用Arduino nano板的内部1.1V参考电压。

二是所用的电阻(采样及分压)精度是否高;电池电压测量使用的分压电阻是为了使用低参考电压而设置的,有误差只会影响放电终止电压。放电电阻中的电流采样电阻不精确,就会影响电池容量的测量。

三是系统布线是否合理,大电流线要粗且单独走线。这个就不用说了。其实这个测量精度对于本人而言没有多大意义,就是对18650电池衰减情况的一个判定,不做分容,足够了,打板就不做了,几块电池测完也是吃灰。


最后,贴出丑陋的原代码。

#include <Arduino.h>
#include <U8g2lib.h>
/*
* 实际使用的是ST7565的12864屏,仿真时使用的是ST7920的12864屏。
* 所有串行输出均为调试时使用,若不用串行监视器,可以删除。
* 引脚A0采集电池电压值。
* 引脚A1采集取样电阻上的电压,并根据欧姆定律换算为电流值。
* 引脚D2为按键引脚,对地接一个按键开关。在测量结束后长按可复位。
* 引脚D4为MOSFET控制,测量结束可停止放电。
* 引脚D6为蜂鸣器引脚,对地接蜂鸣器,测试结后提示用。
* 引脚D7、D8、D9、D11、D13按照U8G2库要求接LCD屏。
*
*/

//实物使用
U8G2_ST7567_JLX12864_1_4W_SW_SPI u8g2(U8G2_R2, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 7, /* dc=*/ 9, /* reset=*/ 8);  // 实际是ST7565的屏
//仿真使用
//U8G2_ST7920_128X64_1_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* CS=*/ 7, /* reset=*/ 8);  // 仿真用
// 构造函数列表结束

const float BAT_LOW = 3.0;    //定义锂离子电池的最低电压
const float BAT_HIGH = 5.3;   //定义电池的最高电压,现为测试,可根据实际修改
const int Vref = 5.0;       // 定义芯片的参考电压,用于电压采样的C/A转换!根据实际修改
const int BEE_Pin = 6;   // 蜂鸣输出引脚 (pin5 pin6 频率为976.5625Hz)
const int BUTTON = 2;   // 按键引脚
const int SW = 4;    // MOSFET控制引脚
unsigned long previousMillis = 0; // 上次时间(毫秒)
unsigned long millisPassed = 0;  // 经过时间(毫秒)
unsigned long millisNow = 0;  // 当前时间(毫秒)
float Capacity = 0;   // 定义电池容量的变量
const float Resistor = 0.48; // 定义采样电阻值为4欧姆,根据实际修改
float mA;
float voltage;  // 电压值
float current;  // 电流值
int hours;
int minutes;
int seconds;

void setup(void) {
  pinMode(BUTTON, INPUT_PULLUP);  //按键设为输入模式,内部上拉
  analogReference(DEFAULT) ;   // 参考电压使用默认参考值5v
  // 以9600bit的速度初始化串行通信:
  Serial.begin(9600);    // 在串行监视器上输出相关信息,调试用
  pinMode(SW, OUTPUT);
  digitalWrite(SW, LOW);    // 关闭MOSFET,无放电
  digitalWrite(BEE_Pin, LOW);

  u8g2.begin();
  u8g2.setContrast(100); // 设置对比度
  u8g2.clearDisplay();
  u8g2.setFont(u8g2_font_ncenB08_tr);  // 选择字体 // 47 63
  Serial.println( "  BAT capacity tester >_< ");
  u8g2.firstPage();
  do {
    u8g2.drawFrame(0, 0, 128, 64);
    u8g2.drawStr(10, 15, "BAT capacity test"); // 在坐标(10,15)处开始写入文本
    u8g2.drawStr(10, 35, "Bat termination");
    u8g2.drawStr(10, 50, "voltage: 3.0V");
  } while ( u8g2.nextPage() );

  delay(3000);
}

void loop(void) {
  //u8g2.clearDisplay();
  //u8g2.setFontDirection(0);
  while (digitalRead(BUTTON) ==  HIGH) {
    ReadV();  // 读取电压值
    u8g2.firstPage();
    do {
      u8g2.drawFrame(0, 0, 128, 64);
      u8g2.drawStr(10, 15, "BAT voltage :        V"); // 在坐标(10,15)处开始写入文本
      u8g2.setCursor(85, 15);
      u8g2.print(voltage);
      u8g2.drawStr(10, 35, "Press the BUTTON");
      u8g2.drawStr(10, 50, "To start ! ");
    } while ( u8g2.nextPage() );
    delay(200);
  } // 等待按下按键
  delay(80);
  ReadV();
  if ( voltage > BAT_HIGH)
  {
    digitalWrite(SW, LOW);    // 关闭MOSFET,无放电
    Serial.println( "  Warning High-V! ");  // Voltage alarm !
    u8g2.firstPage();
    do {
      Dframe_B();
      u8g2.drawStr(10, 55, "HIGH Voltage!!"); // 在坐标(0,15)处开始写入文本
    } while ( u8g2.nextPage() );
    delay(2000);
  }
  else if (voltage < BAT_LOW)
  {
    digitalWrite(SW, LOW);      // 关闭MOSFET,无放电
    Serial.println( "  Warning Low-V! ");
    u8g2.firstPage();
    do {
      Dframe_B();
      u8g2.drawStr(10, 55, "Low Voltage!!");
    } while ( u8g2.nextPage() );
    delay(2000);
  }
  else if (voltage >= BAT_LOW && voltage < BAT_HIGH  )
  { // 检查电池电压如果在安全范围内
    previousMillis = millis();
    while (1) {
      GaugeC();  // 测量放电容量
      Serial.println( "  During testing ... ");
      //u8g2.clearDisplay();
      u8g2.firstPage();
      do {
        Dframe();
        Dframe_T();
        u8g2.drawStr(10, 13, "During testing . . .");
      } while ( u8g2.nextPage() );
      millisToTime();
      delay(1000);
      if (voltage <= BAT_LOW )
      {
        EndGauge();  // 结束
      }
    }
  }
}
//**********主程序结束*********

void Dframe_B() { // 电压告警输出画面
  u8g2.drawFrame(0, 0, 128, 64);
  u8g2.drawHLine(0, 20, 128);
  u8g2.drawHLine(0, 42, 128);  // Replace the BAT
  u8g2.drawStr(15, 15, "Voltage alarm !!");
  u8g2.drawStr(5, 35, "The BAT voltage :");
  u8g2.setCursor(105, 35);
  u8g2.print(voltage);
  //u8g2.drawStr(10, 55, "Replace the BAT");
}

void Dframe() {  // 放电画面
  u8g2.drawFrame(0, 0, 128, 64);
  u8g2.drawHLine(0, 20, 128);
  u8g2.drawVLine(45, 20, 64);
  //u8g2.drawStr(10, 13, "During testing ……");
  u8g2.drawStr(5, 35, "V:");
  u8g2.setCursor(20, 35);
  u8g2.print(voltage);
  u8g2.drawStr(5, 55, "I:");
  u8g2.setCursor(20, 55);
  u8g2.print(current);
  u8g2.drawStr(52, 35, "C:");
  u8g2.setCursor(68, 35);
  u8g2.print(Capacity);
  //u8g2.drawStr(100, 35,"mAH");
  u8g2.drawStr(52, 55, "T:");
  u8g2.setCursor(68, 55);
}

void Dframe_T() { // LCD上显示放电时间
  u8g2.setCursor(68, 55);
  if (hours < 10) u8g2.print('0'); // 补零
  u8g2.print(hours);
  u8g2.setCursor(82, 55); u8g2.print(':');
  u8g2.setCursor(86, 55);
  if (minutes < 10) u8g2.print('0'); // 补零
  u8g2.print(minutes);
  u8g2.setCursor(100, 55); u8g2.print(':');
  u8g2.setCursor(105, 55);
  if (seconds < 10) u8g2.print('0'); // 补零
  u8g2.print(seconds);
}

void ReadV() {  // 模拟引脚A0:读入电池电压值
  int sensorValue_voltage_Bat = analogRead(A0);
  // 将模拟读数(从0到1023)转换为电压(0到Vref的值)
  voltage = sensorValue_voltage_Bat * (Vref / 1023.0) * 5.0;   // 根据分压比计算
  // voltage = sensorValue_voltage_Bat * (Vref / 1023.0) * 5.05; // 带修正值
  Serial.print("Voltage: ");  // 串行口输出
  Serial.print(voltage);      // 串行监视器上显示电压值
  delay(200);
}

void ReadI() {  // 模拟引脚A1:读入电阻上的电压值
  int sensorValue_Shunt_Resistor = analogRead(A1);
  float voltage1 = sensorValue_Shunt_Resistor * (Vref / 1023.0); // 转换为电压值
  //float voltage1 = sensorValue_Shunt_Resistor * (Vref / 1023.0) * 1.03; // 加入修正算法
  current = voltage1 / Resistor; // 用欧姆定律计算
  Serial.print("    Current: ");
  Serial.println(current);
  delay(200);
}

void GaugeC() {  //  通过电流值和时间计算一秒时间的放电容量
  // analogWrite(MOSFET_Pin, PWM_VALUE);  // D5输出PWM信号
  digitalWrite(SW, HIGH);
  delay(30);
  ReadV();
  ReadI();
  millisPassed = millis() - previousMillis;
  mA = current * 1000.0 ;
  Capacity = Capacity + (mA * (millisPassed / 3600000.0));   // 1 Hour = 3600000ms 将其转换为毫安时单位
  previousMillis = millis();
  millisNow = millisPassed + millisNow;
  float TimePass = millisNow / 1000;
  Serial.print("Capacity , Time, "); Serial.print(Capacity); Serial.print(" , "); Serial.println(TimePass); Serial.println( );
  delay(700);
}

void EndGauge() {  // 进入死循环,显示测量结束
  digitalWrite(SW, LOW);      // 关闭MOSFET,无放电
  while (1) {
    Serial.println();
    Serial.println( "  TEST FINISHED @-@ ");
    millisToTime();
    Serial.print("Capacity : "); Serial.print(Capacity); Serial.println(" mAH");
    u8g2.firstPage();
    do {
      Dframe();
      Dframe_T();
      u8g2.drawStr(10, 15, "FINISHED"); // 在坐标(10,15)处开始写入文本
    } while ( u8g2.nextPage() );
    analogWrite(BEE_Pin, 100);
    delay(900);
    if ( digitalRead(BUTTON) ==  LOW) {
      delay(2000);
      if ( digitalRead(BUTTON) ==  LOW) {
        restartArduino();
      }
    }
    ReadV();
    ReadI();
    u8g2.firstPage();
    do {
      Dframe();
      Dframe_T();
      u8g2.drawStr(10, 15, "       ");
    } while ( u8g2.nextPage() );
    digitalWrite(BEE_Pin, LOW);
    delay(600);
  }
}

void millisToTime() { // 将时间转换为时分秒形式
  hours = millisNow / 3600000; // 1小时 = 3600000毫秒
  minutes = (millisNow % 3600000) / 60000; // 取余数后,1分钟 = 60000毫秒
  seconds = (millisNow % 60000) / 1000; // 取余数后,1秒 = 1000毫秒
  Serial.print("Passing time: ");
  if (hours < 10) Serial.print('0'); // 补零
  Serial.print(hours);
  Serial.print(':');
  if (minutes < 10) Serial.print('0'); // 补零
  Serial.print(minutes);
  Serial.print(':');
  if (seconds < 10) Serial.print('0'); // 补零
  Serial.println(seconds);
}

void restartArduino() { // 复位Arduino
  delay(10);
  asm volatile("jmp 0");

}



以上2024年不论是好过还是难过,都即将过去。展现在面前的2025年是全新的,需要努力拼搏。

祝各位坛友们及数码之家的工作人员平安健康!2025快乐!!
谢谢观赏!



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

打赏

参与人数 34家元 +2946 收起 理由
ch104517745 + 30 原創內容
散淡 + 30 謝謝分享
caixinqiang + 30
snowrose2000 + 30 謝謝分享
mydigit + 2000 恭喜获得原创文章热度奖励2000家元.
10011110 + 6
xiaowei0304 + 30 優秀文章
girlstorm + 30 優秀文章
kyhwhb + 30 優秀文章
newnet1234 + 30 謝謝分享

查看全部打赏

发表于 2024-12-24 17:06:32 来自手机浏览器 | 显示全部楼层
好像还没编辑完,先占个位置

打赏

参与人数 2家元 +16 收起 理由
8139 + 10 就你动作快啊,哈哈
jf201006 + 6 占啥位置,就是你的

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 17:27:00 | 显示全部楼层
先点赞收藏,慢慢看,估计又是精华帖

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 謝謝分享

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 17:29:49 | 显示全部楼层
看不懂,只能说会玩...


二○二四年十二月二十四日

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 謝謝分享

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 17:32:30 | 显示全部楼层
先回帖占位,下来慢慢看。

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 謝謝分享

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 17:37:34 | 显示全部楼层
买了两个不过瘾,自己又造了一个

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 精彩回帖

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 18:50:43 | 显示全部楼层
楼主分析详细,洋洋洒洒,试验结果就是diy和成品模块都半斤八两
      
2025快乐!

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 我很贊同 同乐

查看全部打赏

回复 支持 1 反对 0

使用道具 举报

发表于 2024-12-24 18:53:37 | 显示全部楼层
厉害啊,软硬件通吃,大神级别了

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 謝謝分享

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 18:54:00 | 显示全部楼层
楼主乃真大神也!

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 謝謝分享

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 18:57:31 | 显示全部楼层
知识太渊博了

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 謝謝分享

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 18:58:03 | 显示全部楼层
一个电池供电

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 謝謝分享

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 19:02:39 | 显示全部楼层
前来观摩学习jf的大作,jf的充电板应该更新了,还是Micro口的,要换成Type-C与时俱进啊,也祝jf元旦快乐,新年新气象

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 我很贊同 陈年老板了。同乐哈

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 19:27:05 | 显示全部楼层
感谢论坛提供交流平台

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 謝謝分享

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 19:30:45 | 显示全部楼层
水泥电阻一定要上散热片了,不然真心太热了

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 我很贊同

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 19:34:32 | 显示全部楼层
厉害啊,软硬件通吃

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 謝謝分享

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 19:39:17 来自手机浏览器 | 显示全部楼层
不明觉厉啊

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 謝謝分享

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 19:41:20 | 显示全部楼层
厉害了, 这改动试验工程够大的了

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 謝謝分享

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 20:06:05 | 显示全部楼层
介绍的挺详细,高手啊

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 謝謝分享

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2024-12-24 20:09:46 | 显示全部楼层
谢谢分享,图文并茂,分析的太详细了。

打赏

参与人数 1家元 +6 收起 理由
jf201006 + 6 謝謝分享

查看全部打赏

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-5-2 15:07 , Processed in 0.280800 second(s), 14 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

快速回复 返回顶部 返回列表