找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 36|回复: 2
打印 上一主题 下一主题
收起左侧

用STC89C52制作的“锂电池容量检测器”

[复制链接]
跳转到指定楼层
楼主
本帖最后由 ztzp 于 2025-7-6 12:07 编辑

初步设想,使用STC89C52单片机控制,利用该单片机T2的16位自动装填初值功能,得到精确的1秒时钟,每隔1秒累计所放电量(Q=It),放电负载使用自制的2A电子负载仪,放电电流由负载仪设定(先以100mA为准),则每1秒所放的电量为:

每隔1秒就以上述公式计算电量进行累计。
放电电流的设定可以用跳线(或拨码开关)将某个I/O口的各位分别对地短路来实现。
在P1口外接5位拨码开关(也可以在PCB板上焊两排排针,用短路帽来短接),设置放电电流(图中电流为50mA):

由于有5档开关,所以能够预置的放电电流种类为:

开关1~5分别设置电流强度为:50mA、100mA、200mA、400mA、800mA。
各档位组合设置的充电电流(以50mA递增)如下表:

当电池电压达到3V时终止放电,这个可以用继电器来控制负载仪,并接通蜂鸣器报警。
3V电压的检测可以用一个电压比较器(LM339)构成的“施密特触发器”来完成,基准电压源由一个TL431构成。
部分元件参数及封装:

电路原理:模拟部分(放电进行时):

电路原理:模拟部分(放电停止):

TL431分压原理:

其中R1是1、3脚间电阻;R2是1、2脚间电阻。
电路原理:数字部分:

单片机部分供电由78L05三端稳压器提供,实测单片机部分工作电流不大于20mA,所以78L05在不加散热片的情况下也能正常工作。

前面的电路(主要是模拟部分),由于电池有负载时的端电压小于空载时的电压,所以如果用继电器的开关控制电池放电的话,则在电池电压低于临界值时,继电器触点断开电池的瞬间,负载消失,导致电池电压又会高于临界值,这时继电器触点又会重新闭合,如此重复,继电器的触点就会不停的抖动,这个过程会长达几个小时不停,我第一个继电器就是这样损坏的。
所以在电路中取消了用继电器触点控制电池,在电路中只让触点控制单片机“中断”、蜂鸣器。
控制“中断”,在中断函数中停止累加器工作,LED显示最后的电量,这就是电池的容量
控制蜂鸣器报警,提醒人,放电结束了。
在关闭电路以前,电池继续通过负载仪放电也无所谓,因为电池都有保护芯片,放到一定的时候它会自动断电的。

目前采用如下电路结构:
模拟部分:

数字部分:

电路最大的改变是放电截止时不再用继电器断开电池,以免造成继电器触点频繁抖动。
继电器采用双刀双掷,当放电截止时,一个触点控制单片机的INT0,使累计停止;另一个触点接通蜂鸣器,用声音提醒放电已经结束了。
用面包板搭建的测试电路:



电路调试成功后,用洞洞板焊接而成的最终作品:





工作状态:

这是自制的“2A负载仪”在放电过程中的工作状态:
开始放电时(100mA放电电流):

放电结束时(100mA放电电流):

经过对手中几片锂电池进行测量,数据如下:



分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:97023 发表于 2025-7-6 13:24 | 只看该作者
  1. /**********************************************
  2. * 功能:LED显示锂电池容量检测
  3. * 编程:ztzp
  4. * 日期:2020-04-14
  5. * P0        段码控制
  6. * P2        位码控制
  7. * P1        5位拨码开关,决定放电电流。
  8. **********************************************/
  9. #include <reg52.h>

  10. #define uchar unsigned char
  11. #define uint unsigned int
  12. #define ulong unsigned long

  13. sfr T2MOD = 0xC9;        //使用定时器2必须定义,T2MOD的地址。
  14. float Iout;                //放电电流,与预设值相关。
  15. float Sum;                //放电量累加值
  16. ulong S;                //放电量四舍五入后转换为整数
  17. uchar S1;                //第一位
  18. uchar S2;                //第二位
  19. uchar S3;                //第三位
  20. uint i;                        //1秒钟计时变量
  21. uint State = 0;        //计数状态,0开始计数;1停止计数。此值由外部中断0控制。

  22. sbit LED_R = P3^6;        //红色LED,表示正在放电,低电平有效。
  23. sbit LED_G = P3^7;        //绿色LED,表示停止放电,低电平有效。

  24. void init();                //系统初始化
  25. void init_int0();        //外部中断初始化
  26. void init_Time2();        //定时器2初始化
  27. void Show();                //LED数码管显示放电量

  28. //共阳LED数码管位码(从左到右:1、2、3、4、5、6、7、8)
  29. //用PNP管驱动
  30. code uchar WEI_CODE [] =
  31. {
  32.         0xFE,                //1          11111110
  33.         0xFD,                //2          11111101
  34.         0xFB,                //3          11111011
  35.         0xF7,                //4          11110111
  36.         0xEF,                //5          11101111
  37.         0xDF,                //6          11011111
  38.         0xBF,                //7          10111111
  39.         0x7F                //8   01111111
  40. };

  41. //共阳LED数码管段码(带小数点)
  42. code uchar DSY_CODE [] =
  43. {
  44.         0x40,                //0
  45.         0x79,                //1
  46.         0x24,                //2
  47.         0x30,                //3
  48.         0x19,                //4
  49.         0x12,                //5
  50.         0x02,                //6
  51.         0x78,                //7
  52.         0x00,                //8
  53.         0x10                //9
  54. };

  55. // 延时 xms(晶振:11.0592MHz,)
  56. void DelayMS(uint x)
  57. {
  58.         uchar i;
  59.         while( x-- )
  60.         {
  61.                 for( i=0; i<111; i++ );
  62.         }
  63. }

  64. //主程序
  65. void main()
  66. {
  67.         init();                        //系统初始化
  68.         init_int0();        //外部中断初始化
  69.         init_Time2();        //定时器2初始化
  70.         while(1)
  71.         {
  72.                 Show();                //显示
  73.         }
  74. }

  75. //LED数码管显示放电量(从左到右)
  76. void Show()
  77. {
  78.         //第1位
  79.         S = Sum + 0.5;        //四舍五入
  80.         S1 = S/10000000;
  81.         if (S1 != 0)
  82.         {
  83.                 P2 = WEI_CODE[0];
  84.                 P0 = DSY_CODE[S1] + 0x80;        //无小数点
  85.                 DelayMS(1);
  86.                 P2 = 0xFF;
  87.                 P0 = 0xFF;
  88.                 DelayMS(1);
  89.         }

  90.         //第2位
  91.         S = Sum + 0.5;
  92.         S2 = S%10000000/1000000;
  93.         if (S2 != 0 || S1 != 0)                //当第一位不等于0时,第二位必须显示
  94.         {
  95.                 P2 = WEI_CODE[1];
  96.                 P0 = DSY_CODE[S2] + 0x80;
  97.                 DelayMS(1);
  98.                 P2 = 0xFF;
  99.                 P0 = 0xFF;
  100.                 DelayMS(1);
  101.         }

  102.         //第3位
  103.         S = Sum + 0.5;
  104.         S3 = S%1000000/100000;
  105.         if (S3 != 0 || S2 != 0 || S1 != 0)                //当第二位(或者第一位)不等于0时,第三位必须显示。
  106.         {
  107.                 P2 = WEI_CODE[2];
  108.                 P0 = DSY_CODE[S3] + 0x80;
  109.                 DelayMS(1);
  110.                 P2 = 0xFF;
  111.                 P0 = 0xFF;
  112.                 DelayMS(1);
  113.         }

  114.         //第4位(个位)
  115.         S = Sum + 0.5;
  116.         S = S%100000/10000;
  117.         P2 = WEI_CODE[3];
  118.         P0 = DSY_CODE[S];        //有小数点
  119.         DelayMS(1);
  120.         P2 = 0xFF;
  121.         P0 = 0xFF;
  122.         DelayMS(1);

  123.         //第5位
  124.         S = Sum + 0.5;
  125.         S = S%10000/1000;
  126.         P2 = WEI_CODE[4];
  127.         P0 = DSY_CODE[S]+0x80;
  128.         DelayMS(1);
  129.         P2 = 0xFF;
  130.         P0 = 0xFF;
  131.         DelayMS(1);

  132.         //第6位
  133.         S = Sum + 0.5;
  134.         S = S % 1000 /100;
  135.         P2 = WEI_CODE[5];
  136.         P0 = DSY_CODE[S]+0x80;
  137.         DelayMS(1);
  138.         P2 = 0xFF;
  139.         P0 = 0xFF;
  140.         DelayMS(1);

  141.         //第7位
  142.         S = Sum + 0.5;
  143.         S = S % 100/10;
  144.         P2 = WEI_CODE[6];
  145.         P0 = DSY_CODE[S]+0x80;
  146.         DelayMS(1);
  147.         P2 = 0xFF;
  148.         P0 = 0xFF;
  149.         DelayMS(1);

  150.         //第8位
  151.         S = Sum + 0.5;
  152.         S = S % 10;
  153.         P2 = WEI_CODE[7];
  154.         P0 = DSY_CODE[S]+0x80;
  155.         DelayMS(1);
  156.         P2 = 0xFF;
  157.         P0 = 0xFF;
  158.         DelayMS(1);
  159. }

  160. //系统初始化
  161. //根据读入P1的拨码开关状态值,决定放电电流。
  162. void init()
  163. {
  164.         LED_R = 0;        //红灯亮
  165.         LED_G = 1;        //绿灯灭

  166.         switch( P1 )
  167.         {
  168.                 case 0XFE:        //50mA
  169.                         Iout = 500.0/3.6;
  170.                         break;
  171.                 case 0XFD:        //100mA
  172.                         Iout = 1000.0/3.6;
  173.                         break;
  174.                 case 0XFC:        //150mA
  175.                         Iout = 1.5*1000.0/3.6;
  176.                         break;
  177.                 case 0XFB:        //200mA
  178.                         Iout = 2.0*1000.0/3.6;
  179.                         break;
  180.                 case 0XFA:        //250mA
  181.                         Iout = 2.5*1000.0/3.6;
  182.                         break;
  183.                 case 0XF9:        //300mA
  184.                         Iout = 3.0*1000.0/3.6;
  185.                         break;
  186.                 case 0XF8:        //350mA
  187.                         Iout = 3.5*1000.0/3.6;
  188.                         break;
  189.                 case 0XF7:        //400mA
  190.                         Iout = 4.0*1000.0/3.6;
  191.                         break;
  192.                 case 0XF6:        //450mA
  193.                         Iout = 4.5*1000.0/3.6;
  194.                         break;
  195.                 case 0XF5:        //500mA
  196.                         Iout = 5.0*1000.0/3.6;
  197.                         break;
  198.                 case 0XF4:        //550mA
  199.                         Iout = 5.5*1000.0/3.6;
  200.                         break;
  201.                 case 0XF3:        //600mA
  202.                         Iout = 6.0*1000.0/3.6;
  203.                         break;
  204.                 case 0XF2:        //650mA
  205.                         Iout = 6.5*1000.0/3.6;
  206.                         break;
  207.                 case 0XF1:        //700mA
  208.                         Iout = 7.0*1000.0/3.6;
  209.                         break;
  210.                 case 0XF0:        //750mA
  211.                         Iout = 7.5*1000.0/3.6;
  212.                         break;
  213.                 case 0XEF:        //800mA
  214.                         Iout = 8.0*1000.0/3.6;
  215.                         break;
  216.                 case 0XEE:        //850mA
  217.                         Iout = 8.5*1000.0/3.6;
  218.                         break;
  219.                 case 0XED:        //900mA
  220.                         Iout = 9.0*1000.0/3.6;
  221.                         break;
  222.                 case 0XEC:        //950mA
  223.                         Iout = 9.5*1000.0/3.6;
  224.                         break;
  225.                 case 0XEB:        //1000mA
  226.                         Iout = 10.0*1000.0/3.6;
  227.                         break;
  228.                 case 0XEA:        //1050mA
  229.                         Iout = 10.5*1000.0/3.6;
  230.                         break;
  231.                 case 0XE9:        //1100mA
  232.                         Iout = 11.0*1000.0/3.6;
  233.                         break;
  234.                 case 0XE8:        //1150mA
  235.                         Iout = 11.5*1000.0/3.6;
  236.                         break;
  237.                 case 0XE7:        //1200mA
  238.                         Iout = 12.0*1000.0/3.6;
  239.                         break;
  240.                 case 0XE6:        //1250mA
  241.                         Iout = 12.5*1000.0/3.6;
  242.                         break;
  243.                 case 0XE5:        //1300mA
  244.                         Iout = 13.0*1000.0/3.6;
  245.                         break;
  246.                 case 0XE4:        //1350mA
  247.                         Iout = 13.5*1000.0/3.6;
  248.                         break;
  249.                 case 0XE3:        //1400mA
  250.                         Iout = 14.0*1000.0/3.6;
  251.                         break;
  252.                 case 0XE2:        //1450mA
  253.                         Iout = 14.5*1000.0/3.6;
  254.                         break;
  255.                 case 0XE1:        //1500mA
  256.                         Iout = 15.0*1000.0/3.6;
  257.                         break;
  258.                 case 0XE0:        //1550mA
  259.                         Iout = 15.5*1000.0/3.6;
  260.                         break;
  261.         }
  262. }

  263. //外部中断初始化
  264. void init_int0()
  265. {
  266.         PX0 = 1;        //设置外部中断0为最高优先级
  267.         IT0 = 1;        //1:设置下降沿触发方式;0:低电平触发方式
  268.         EA = 1;                //开总中断
  269.         EX0 = 1;        //开外部中断0
  270. }

  271. //定时器2初始化
  272. void init_Time2()
  273. {
  274.         T2MOD = 0;
  275.         T2CON = 0;
  276.         RCAP2H = (65536 - 50000)/256;                //12M晶振,50ms产生一次中断
  277.         RCAP2L = (65536 - 50000)%256;
  278.         ET2 = 1;        //允许T2中断
  279.         TR2 = 1;        //启动T2中断
  280. }

  281. //外部中断0,用于停止放电计数。
  282. void Int0() interrupt 0
  283. {
  284.         LED_R = 1;        //红灯灭
  285.         LED_G = 0;        //绿灯亮
  286.         State = 1;        //停止放电计数
  287. //        EX0 = 0;        //停止外部中断
  288. }

  289. //定时器2中断服务程序
  290. void Time2() interrupt 5
  291. {
  292.         TF2 = 0;        //定时器2必须由软件对溢出标志位清零!
  293.         if (i > 20 && State == 0)        //每1秒累加一次放电量
  294.         {
  295.                 Sum += Iout;                //以100mA放电,1秒钟的放电量。
  296.                 i = 0;
  297.         }
  298.         i++;
  299. }
复制代码


回复

使用道具 举报

板凳
ID:1136941 发表于 2025-7-6 15:09 | 只看该作者
请问拨码开关是如何通过单片机控制放电电流的?
回复

使用道具 举报

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

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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