找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 4872|回復(fù): 7
打印 上一主題 下一主題
收起左側(cè)

自制BMS監(jiān)控儀通過(guò)485通訊讀BMS信息并顯示在LCD2004上 單片機(jī)源程序電路

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:702386 發(fā)表于 2020-10-10 17:15 | 只看該作者 |只看大圖 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
公司是做鋰電池管理系統(tǒng)的,主要是通信用16串鋰電池用的保護(hù)板(BMS),產(chǎn)品有485接口,可以上傳各種測(cè)量和告警信息。最近開(kāi)始學(xué)習(xí)單片機(jī),自己動(dòng)手做了一個(gè)BMS監(jiān)控儀,通過(guò)485與自家的BMS通訊獲取狀態(tài)信息并顯示在LCD2004上。
程序也是自己弄了好久才排除各種bug,現(xiàn)在具備了基本狀態(tài)信息顯示、菜單、16串單體電壓顯示、各溫度顯示、BMS狀態(tài)顯示、基本告警信息顯示。
這個(gè)小產(chǎn)品使用STC89C54RD+,MAX485芯,使用5V升壓鋰電池供電,在外殼上留出了USB充電接口。
電路原圖是自己搞了一個(gè),然后同事幫我畫(huà)的PCB。制作比較廢勁,尤其是殼子不好切割,還把手割傷了。。。
因?yàn)楣ぷ鞅容^忙,從頭到尾斷斷續(xù)續(xù)搞了一個(gè)來(lái)月終于算是完成了,和大家分享一下。

已附上原理圖和單片機(jī)程序。因自己初學(xué),程序有很多不足,比如沒(méi)有按模塊化編寫(xiě)、邏輯較亂等,希望大家?guī)兔χ更c(diǎn)。

制作出來(lái)的實(shí)物圖如下:


電路原理圖如下:


單片機(jī)源程序如下:
  1. #include "STC89C54.h"
  2. #define uint unsigned int
  3. #define uchar unsigned char

  4. uchar code welcome1[]="BMS Monitor";
  5. uchar code welcome2[]="RichPower";
  6. uchar code waiting[]="CONNECTING...";
  7. uchar code menu_table1[]="CELL VOLTAGE"; //12個(gè)字符
  8. uchar code menu_table2[]="TEMPERATURE";  //11個(gè)字符
  9. uchar code menu_table3[]="BMS STATUS";        //10個(gè)字符
  10. uchar code menu_table4[]="ALARM INFO"; //10個(gè)字符
  11. uchar code BMSINFO1[20]={0x7E,0x32,0x36,0x30,0x30,0x34,0x36,0x46,0x32,0x45,0x30,0x30,0x32,0x30,0x31,0x46,0x44,0x31,0x45,0x0D};         //詢(xún)遙測(cè)的命令報(bào)文
  12. uchar code BMSINFO2[20]={0x7E,0x32,0x36,0x30,0x30,0x34,0x36,0x46,0x34,0x45,0x30,0x30,0x32,0x30,0x31,0x46,0x44,0x31,0x43,0x0D};  //詢(xún)遙信命令報(bào)文
  13. uchar buffer[145]={0};          //用于緩存遙測(cè)報(bào)文
  14. sbit lcdrs=P2^5;        //指令和數(shù)據(jù)寄存器選擇,高電平時(shí)為數(shù)據(jù),低電平選擇命令
  15. sbit lcdrw=P2^6;        //讀寫(xiě)選擇,高電平為讀,低電平為寫(xiě)
  16. sbit lcden=P2^7;    //使能
  17. sbit lcdbg=P2^4;        //背光,0為開(kāi)
  18. sbit beep=P2^0;                //蜂鳴器,0為開(kāi)
  19. sbit key1=P1^0;                //菜單或確認(rèn)
  20. sbit key2=P1^1;                //上一項(xiàng)
  21. sbit key3=P1^2;                //下一項(xiàng)
  22. sbit key4=P1^3;                //返回或背光開(kāi)關(guān)
  23. bit data datareceived_flag=0,displayclear=1;           //datareceived_flag為遙測(cè)報(bào)文是否接收完的標(biāo)志位,current_bit為電流值符號(hào)標(biāo)志位
  24. bit data current_bit,celltemp_unit,envtemp_unit,mostemp_unit;
  25. uchar data num;
  26. uchar data i=0,watchdog=0,end_position;
  27. uint single_max,single_min,totalvoltage,current,totalcap,remaincap;
  28. uint data remaincap2;               
  29. uchar digit0,digit1,digit2,digit3,digit4;                                   //LCD顯示數(shù)字的萬(wàn)千百十個(gè)位
  30. uchar cellnumber,cellnumber_offset,address_offset;                 //串?dāng)?shù),串?dāng)?shù)差,地址偏移量
  31. uchar cappercentage;                                                                         //SOC
  32. uint singlevoltage[15];                                                   //單體電芯電壓
  33. uint temperature[5];                                                           //4個(gè)電芯溫度和環(huán)境溫度及功率溫度
  34. uchar display_mode=0,menu_position=1,singlevoltage_page=1;                         //顯示模式


  35. void delayms(uint z)         //延時(shí)子程序
  36. {
  37.         uint x,y;
  38.         for(x=z;x>0;x--)
  39.                 for(y=110;y>0;y--);
  40. }


  41. void write_com(uchar com)           //LCD1602寫(xiě)命令函數(shù)
  42. {
  43.         lcdrs=0;                 //rs低電平為寫(xiě)命令
  44.         P0=com;                        
  45.         delayms(4);
  46.         lcden=1;                 //EN先置高電平
  47.         delayms(4);
  48.         lcden=0;                 //短暫延時(shí)后EN置低電平
  49. }
  50. void write_dat(uchar dat)           //LCD1602寫(xiě)數(shù)據(jù)函數(shù)
  51. {
  52.         lcdrs=1;                 //rs高電平為寫(xiě)數(shù)據(jù)
  53.         P0=dat;
  54.         delayms(4);
  55.         lcden=1;
  56.         delayms(4);
  57.         lcden=0;
  58. }

  59. void UsartInit()           //串口初始化
  60. {
  61.         SCON=0X50;                        //設(shè)置為工作方式1
  62.         TMOD=0X20;                        //設(shè)置計(jì)數(shù)器工作方式2
  63.         PCON=0X80;                        //波特率加倍
  64.         TH1=0XFA;                                //計(jì)數(shù)器初始值設(shè)置,注意11.0592Mhz波特率是9600的
  65.         TL1=0XFA;
  66.         ES=1;                                                //打開(kāi)接收中斷
  67.         EA=1;                                                //打開(kāi)總中斷
  68.         TR1=1;                                        //打開(kāi)計(jì)數(shù)器
  69. }

  70. void init()                                //LCD初始化及開(kāi)機(jī)界面
  71. {
  72.         lcdrw=0;
  73.         lcden=0;
  74.         P0=0;
  75.         write_com(0x38);
  76.         write_com(0x0f);   //初始化,開(kāi)顯示,開(kāi)光標(biāo),開(kāi)光標(biāo)閃爍
  77.         write_com(0x06);   //初始化,讀寫(xiě)一個(gè)字符后地址指針自動(dòng)加1
  78.         write_com(0x01);   //清屏
  79. //        write_com(0x80);   //數(shù)據(jù)地址指針從0開(kāi)始
  80.         lcdbg=0;                   //開(kāi)背光
  81.         write_com(0x0C);        //關(guān)光標(biāo)
  82. /**********************歡迎界面**************************/
  83.         write_com(0x80+0x44);  //第2行第5個(gè)字符
  84.         for(num=0;num<11;num++)
  85.         write_dat(welcome1[num]);
  86.         write_com(0x94+0x05);         //第3行第6個(gè)字符
  87.         for(num=0;num<9;num++)
  88.         write_dat(welcome2[num]);
  89.         delayms(1000);
  90.         beep=0;
  91.         delayms(60);
  92.         beep=1;
  93. /********************************************************/
  94. }

  95. /********************************************************************************
  96. 計(jì)算報(bào)文緩存中的一個(gè)字節(jié)
  97. ********************************************************************************/
  98. uchar buffer_byte_process(uchar buffer_address)
  99. {
  100.         uchar byte_value;
  101.         if(buffer[buffer_address]<=0x39)
  102.                 buffer[buffer_address]=buffer[buffer_address]-0x30;        //若為0~9的字符,減0x30即為數(shù)值
  103.         else
  104.                 buffer[buffer_address]=buffer[buffer_address]-0x37;        //若為大于9即為A~F的字符,減0x37即為數(shù)值
  105.         if(buffer[buffer_address+1]<=0x39)
  106.                 buffer[buffer_address+1]=buffer[buffer_address+1]-0x30;
  107.         else
  108.                 buffer[buffer_address+1]=buffer[buffer_address+1]-0x37;
  109.         byte_value=(buffer[buffer_address]<<4)|buffer[buffer_address+1];               
  110.         return byte_value;
  111. }

  112. /********************************************************************************
  113. 計(jì)算報(bào)文緩存中的一個(gè)字
  114. ********************************************************************************/
  115. uint buffer_word_process(uchar buffer_address)
  116. {
  117.         uint word_value;
  118.         if(buffer[buffer_address]<=0x39)
  119.                 buffer[buffer_address]=buffer[buffer_address]-0x30;        //若為0~9的字符,減0x30即為數(shù)值
  120.         else
  121.                 buffer[buffer_address]=buffer[buffer_address]-0x37;        //若為大于9即為A~F的字符,減0x37即為數(shù)值
  122.         if(buffer[buffer_address+1]<=0x39)
  123.                 buffer[buffer_address+1]=buffer[buffer_address+1]-0x30;
  124.         else
  125.                 buffer[buffer_address+1]=buffer[buffer_address+1]-0x37;
  126.         if(buffer[buffer_address+2]<=0x39)
  127.                 buffer[buffer_address+2]=buffer[buffer_address+2]-0x30;        //若為0~9的字符,減0x30即為數(shù)值
  128.         else
  129.                 buffer[buffer_address+2]=buffer[buffer_address+2]-0x37;        //若為大于9即為A~F的字符,減0x37即為數(shù)值
  130.         if(buffer[buffer_address+3]<=0x39)
  131.                 buffer[buffer_address+3]=buffer[buffer_address+3]-0x30;
  132.         else
  133.                 buffer[buffer_address+3]=buffer[buffer_address+3]-0x37;
  134.         word_value=(buffer[buffer_address]<<12)|(buffer[buffer_address+1]<<8)|(buffer[buffer_address+2]<<4)|buffer[buffer_address+3];               
  135.         return word_value;
  136. }

  137. void buffer_value_process()
  138. {
  139.         cellnumber=buffer_byte_process(17);                                                //串?dāng)?shù)計(jì)算
  140.         cellnumber_offset=16-cellnumber;                                                //串?dāng)?shù)與16串相比的差值
  141.         address_offset=cellnumber_offset*4;                                                //報(bào)文地址偏移量        
  142.         current=buffer_word_process(109-address_offset);                //電流計(jì)算
  143.         current_bit=current>>15;                                                                //電流正負(fù)判斷
  144.         if(current_bit==1)                                                                                
  145.                 current=~current+1;   //電流值為補(bǔ)碼,負(fù)數(shù)取反加1得到原碼(符號(hào)位1取反變成了0);        
  146.         totalvoltage=buffer_word_process(113-address_offset);  //總壓計(jì)算
  147.         for(num=0;num<=15-cellnumber_offset;num++)                           //單體電壓和最高最低電壓計(jì)算
  148.         {
  149.                 singlevoltage[num]=buffer_word_process(19+4*num);  //各串單體電壓
  150.                 if(num==0)
  151.                 {
  152.                         single_max=singlevoltage[0];
  153.                         single_min=single_max;
  154.                 }
  155.                 if(singlevoltage[num]>single_max)
  156.                         single_max=singlevoltage[num];                                   //最高單體電壓
  157.                 if(singlevoltage[num]<single_min)
  158.                         single_min=singlevoltage[num];                                   //最低單體電壓
  159.         }
  160.         remaincap=buffer_word_process(117-address_offset);           //剩余容量
  161.         remaincap2=remaincap;
  162.         totalcap=buffer_word_process(123-address_offset)/100;  //總?cè)萘?br />
  163.         cappercentage=(remaincap/totalcap)&0xFF;                           //SOC計(jì)算
  164.         for(num=0;num<=5;num++)                                                                   //6個(gè)溫度計(jì)算
  165.                 temperature[num]=buffer_word_process(85+4*num-address_offset);           //此溫度需減2731并判斷符號(hào)
  166. }

  167. void main_display()
  168. {
  169.         digit4=totalvoltage%10;                          //總壓個(gè)位數(shù)字
  170.         digit3=totalvoltage/10%10;                  //總壓十位數(shù)字
  171.         digit2=totalvoltage/100%10;                  //總壓百位數(shù)字
  172.         digit1=totalvoltage/1000%10;          //總壓千位數(shù)字
  173.         write_com(0x80+0x02);                   //數(shù)據(jù)地址指針從第1行第9位開(kāi)始寫(xiě)總壓值
  174.         write_dat(digit1+0x30);                           //顯示千位數(shù)字的ASCII碼
  175.         write_dat(digit2+0x30);
  176.         write_dat('.');
  177.         write_dat(digit3+0x30);
  178.         write_dat(digit4+0x30);
  179.         write_dat('V');        

  180.         digit4=current%10;                           //電流個(gè)位數(shù)字
  181.         digit3=current/10%10;
  182.         digit2=current/100%10;
  183.         digit1=current/1000%10;
  184.         if(current_bit==0&&digit1==0)          //如果為正電流,且千位為0
  185.         {
  186.                 write_com(0xc0);
  187.                 write_dat(' ');write_dat(' ');
  188.                 write_dat(' ');                                
  189.         }
  190.         if(current_bit==0&&digit1>0)          //如果為正電流,且千位不為0
  191.         {
  192.                 write_com(0xc0);
  193.                 write_dat(' ');write_dat(' ');
  194.                 write_dat(digit1+0x30);                                
  195.         }
  196.         if(current_bit==1&&digit1==0)          //如果為負(fù)電流,且千位為0
  197.         {
  198.                 write_com(0xc0);
  199.                 write_dat(' ');write_dat(' ');
  200.                 write_dat('-');                                
  201.         }
  202.         if(current_bit==1&&digit1>0)          //如果為負(fù)電流,且千位不為0
  203.         {
  204.                 write_com(0xc0);write_dat(' ');
  205.                 write_dat('-');
  206.                 write_dat(digit1+0x30);                                
  207.         }
  208.         write_dat(digit2+0x30);
  209.         write_dat('.');
  210.         write_dat(digit3+0x30);
  211.         write_dat(digit4+0x30);
  212.         write_dat('A');

  213.         digit4=remaincap2%10;                          //剩余容量個(gè)位數(shù)字
  214.         digit3=remaincap2/10%10;                          //十位數(shù)字
  215.         digit2=remaincap2/100%10;                  //百位數(shù)字
  216.         digit1=remaincap2/1000%10;                  //千位數(shù)字
  217.         digit0=remaincap2/10000%10;                //萬(wàn)位數(shù)字
  218.         write_com(0x80+0x0A);                   //數(shù)據(jù)地址指針從第1行第10位開(kāi)始寫(xiě)剩余容量
  219.         if(digit0>0)
  220.                 write_dat(digit0+0x30);                //顯示萬(wàn)位數(shù)字的ASCII碼
  221.         else
  222.                 write_dat(' ');
  223.         if(digit0==0&&digit1==0)
  224.                 write_dat(' ');                                   //顯示千位數(shù)字的ASCII碼
  225.         else
  226.                 write_dat(digit1+0x30);
  227.         write_dat(digit2+0x30);
  228.         write_dat('A');
  229.         write_dat('h');

  230.         digit4=cappercentage%10;                  //SOC個(gè)位數(shù)字
  231.         digit3=cappercentage/10%10;                  //十位數(shù)字
  232.         digit2=cappercentage/100%10;          //百位數(shù)字
  233.         write_com(0x80+0x10);                   //數(shù)據(jù)地址指針從第1行第16位開(kāi)始寫(xiě)剩余容量
  234.         if(digit2>0)
  235.                 write_dat(digit2+0x30);
  236.         else
  237.                 write_dat(' ');
  238.         if(digit2==0&&digit3==0)
  239.                 write_dat(' ');
  240.         else
  241.                 write_dat(digit3+0x30);
  242.         write_dat(digit4+0x30);
  243.         write_dat('%');

  244.         if(temperature[0]>=2731)                                         //第1節(jié)電芯溫度
  245.         {
  246.                 celltemp_unit=1;   //單位為正
  247.            
  248. ……………………

  249. …………限于本文篇幅 余下代碼請(qǐng)從51黑下載附件…………
復(fù)制代碼

所有資料51hei提供下載:
BMS監(jiān)控儀原理和程序.rar (19.71 KB, 下載次數(shù): 79)

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏10 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:328014 發(fā)表于 2020-10-10 17:55 | 只看該作者
好東東,能分享下電路和源碼嗎?
回復(fù)

使用道具 舉報(bào)

板凳
ID:702386 發(fā)表于 2020-10-10 20:14 | 只看該作者
51hei團(tuán)團(tuán) 發(fā)表于 2020-10-10 17:55
好東東,能分享下電路和源碼嗎?

已分享,歡迎給建議
回復(fù)

使用道具 舉報(bào)

地板
ID:828614 發(fā)表于 2020-10-12 10:18 | 只看該作者
厲害了,我們也是做電池,我最近準(zhǔn)備做BMS藍(lán)牙上位機(jī)
回復(fù)

使用道具 舉報(bào)

5#
ID:702386 發(fā)表于 2020-10-12 16:20 | 只看該作者
嗜血者123 發(fā)表于 2020-10-12 10:18
厲害了,我們也是做電池,我最近準(zhǔn)備做BMS藍(lán)牙上位機(jī)

那你們BMS帶藍(lán)牙模塊吧。你是要開(kāi)發(fā)手機(jī)app嗎?
回復(fù)

使用道具 舉報(bào)

6#
ID:828614 發(fā)表于 2020-10-14 12:43 | 只看該作者
zsw3721 發(fā)表于 2020-10-12 16:20
那你們BMS帶藍(lán)牙模塊吧。你是要開(kāi)發(fā)手機(jī)app嗎?

不帶藍(lán)牙  是帶uart串口,自己開(kāi)發(fā)個(gè)手機(jī)APP
回復(fù)

使用道具 舉報(bào)

7#
ID:876818 發(fā)表于 2021-1-12 16:37 | 只看該作者
厲害
謝謝,學(xué)習(xí)一下
回復(fù)

使用道具 舉報(bào)

8#
ID:373684 發(fā)表于 2021-1-14 12:48 | 只看該作者
嗜血者123 發(fā)表于 2020-10-12 10:18
厲害了,我們也是做電池,我最近準(zhǔn)備做BMS藍(lán)牙上位機(jī)

藍(lán)牙電池保護(hù)??
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表