|
公司是做鋰電池管理系統(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í)物圖如下:
1571849934.jpg (2.08 MB, 下載次數(shù): 69)
下載附件
2020-10-10 17:05 上傳
1941352020.jpg (2.31 MB, 下載次數(shù): 67)
下載附件
2020-10-10 17:05 上傳
233656511.jpg (1.73 MB, 下載次數(shù): 86)
下載附件
2020-10-10 17:05 上傳
1514261199.jpg (1.63 MB, 下載次數(shù): 62)
下載附件
2020-10-10 17:05 上傳
1633315626.jpg (1.45 MB, 下載次數(shù): 85)
下載附件
2020-10-10 17:05 上傳
1934588858.jpg (1.55 MB, 下載次數(shù): 78)
下載附件
2020-10-10 17:17 上傳
電路原理圖如下:
51hei.png (43.53 KB, 下載次數(shù): 73)
下載附件
2020-10-10 18:50 上傳
單片機(jī)源程序如下:
- #include "STC89C54.h"
- #define uint unsigned int
- #define uchar unsigned char
- uchar code welcome1[]="BMS Monitor";
- uchar code welcome2[]="RichPower";
- uchar code waiting[]="CONNECTING...";
- uchar code menu_table1[]="CELL VOLTAGE"; //12個(gè)字符
- uchar code menu_table2[]="TEMPERATURE"; //11個(gè)字符
- uchar code menu_table3[]="BMS STATUS"; //10個(gè)字符
- uchar code menu_table4[]="ALARM INFO"; //10個(gè)字符
- 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)文
- 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)文
- uchar buffer[145]={0}; //用于緩存遙測(cè)報(bào)文
- sbit lcdrs=P2^5; //指令和數(shù)據(jù)寄存器選擇,高電平時(shí)為數(shù)據(jù),低電平選擇命令
- sbit lcdrw=P2^6; //讀寫(xiě)選擇,高電平為讀,低電平為寫(xiě)
- sbit lcden=P2^7; //使能
- sbit lcdbg=P2^4; //背光,0為開(kāi)
- sbit beep=P2^0; //蜂鳴器,0為開(kāi)
- sbit key1=P1^0; //菜單或確認(rèn)
- sbit key2=P1^1; //上一項(xiàng)
- sbit key3=P1^2; //下一項(xiàng)
- sbit key4=P1^3; //返回或背光開(kāi)關(guān)
- bit data datareceived_flag=0,displayclear=1; //datareceived_flag為遙測(cè)報(bào)文是否接收完的標(biāo)志位,current_bit為電流值符號(hào)標(biāo)志位
- bit data current_bit,celltemp_unit,envtemp_unit,mostemp_unit;
- uchar data num;
- uchar data i=0,watchdog=0,end_position;
- uint single_max,single_min,totalvoltage,current,totalcap,remaincap;
- uint data remaincap2;
- uchar digit0,digit1,digit2,digit3,digit4; //LCD顯示數(shù)字的萬(wàn)千百十個(gè)位
- uchar cellnumber,cellnumber_offset,address_offset; //串?dāng)?shù),串?dāng)?shù)差,地址偏移量
- uchar cappercentage; //SOC
- uint singlevoltage[15]; //單體電芯電壓
- uint temperature[5]; //4個(gè)電芯溫度和環(huán)境溫度及功率溫度
- uchar display_mode=0,menu_position=1,singlevoltage_page=1; //顯示模式
- void delayms(uint z) //延時(shí)子程序
- {
- uint x,y;
- for(x=z;x>0;x--)
- for(y=110;y>0;y--);
- }
- void write_com(uchar com) //LCD1602寫(xiě)命令函數(shù)
- {
- lcdrs=0; //rs低電平為寫(xiě)命令
- P0=com;
- delayms(4);
- lcden=1; //EN先置高電平
- delayms(4);
- lcden=0; //短暫延時(shí)后EN置低電平
- }
- void write_dat(uchar dat) //LCD1602寫(xiě)數(shù)據(jù)函數(shù)
- {
- lcdrs=1; //rs高電平為寫(xiě)數(shù)據(jù)
- P0=dat;
- delayms(4);
- lcden=1;
- delayms(4);
- lcden=0;
- }
- void UsartInit() //串口初始化
- {
- SCON=0X50; //設(shè)置為工作方式1
- TMOD=0X20; //設(shè)置計(jì)數(shù)器工作方式2
- PCON=0X80; //波特率加倍
- TH1=0XFA; //計(jì)數(shù)器初始值設(shè)置,注意11.0592Mhz波特率是9600的
- TL1=0XFA;
- ES=1; //打開(kāi)接收中斷
- EA=1; //打開(kāi)總中斷
- TR1=1; //打開(kāi)計(jì)數(shù)器
- }
- void init() //LCD初始化及開(kāi)機(jī)界面
- {
- lcdrw=0;
- lcden=0;
- P0=0;
- write_com(0x38);
- write_com(0x0f); //初始化,開(kāi)顯示,開(kāi)光標(biāo),開(kāi)光標(biāo)閃爍
- write_com(0x06); //初始化,讀寫(xiě)一個(gè)字符后地址指針自動(dòng)加1
- write_com(0x01); //清屏
- // write_com(0x80); //數(shù)據(jù)地址指針從0開(kāi)始
- lcdbg=0; //開(kāi)背光
- write_com(0x0C); //關(guān)光標(biāo)
- /**********************歡迎界面**************************/
- write_com(0x80+0x44); //第2行第5個(gè)字符
- for(num=0;num<11;num++)
- write_dat(welcome1[num]);
- write_com(0x94+0x05); //第3行第6個(gè)字符
- for(num=0;num<9;num++)
- write_dat(welcome2[num]);
- delayms(1000);
- beep=0;
- delayms(60);
- beep=1;
- /********************************************************/
- }
- /********************************************************************************
- 計(jì)算報(bào)文緩存中的一個(gè)字節(jié)
- ********************************************************************************/
- uchar buffer_byte_process(uchar buffer_address)
- {
- uchar byte_value;
- if(buffer[buffer_address]<=0x39)
- buffer[buffer_address]=buffer[buffer_address]-0x30; //若為0~9的字符,減0x30即為數(shù)值
- else
- buffer[buffer_address]=buffer[buffer_address]-0x37; //若為大于9即為A~F的字符,減0x37即為數(shù)值
- if(buffer[buffer_address+1]<=0x39)
- buffer[buffer_address+1]=buffer[buffer_address+1]-0x30;
- else
- buffer[buffer_address+1]=buffer[buffer_address+1]-0x37;
- byte_value=(buffer[buffer_address]<<4)|buffer[buffer_address+1];
- return byte_value;
- }
- /********************************************************************************
- 計(jì)算報(bào)文緩存中的一個(gè)字
- ********************************************************************************/
- uint buffer_word_process(uchar buffer_address)
- {
- uint word_value;
- if(buffer[buffer_address]<=0x39)
- buffer[buffer_address]=buffer[buffer_address]-0x30; //若為0~9的字符,減0x30即為數(shù)值
- else
- buffer[buffer_address]=buffer[buffer_address]-0x37; //若為大于9即為A~F的字符,減0x37即為數(shù)值
- if(buffer[buffer_address+1]<=0x39)
- buffer[buffer_address+1]=buffer[buffer_address+1]-0x30;
- else
- buffer[buffer_address+1]=buffer[buffer_address+1]-0x37;
- if(buffer[buffer_address+2]<=0x39)
- buffer[buffer_address+2]=buffer[buffer_address+2]-0x30; //若為0~9的字符,減0x30即為數(shù)值
- else
- buffer[buffer_address+2]=buffer[buffer_address+2]-0x37; //若為大于9即為A~F的字符,減0x37即為數(shù)值
- if(buffer[buffer_address+3]<=0x39)
- buffer[buffer_address+3]=buffer[buffer_address+3]-0x30;
- else
- buffer[buffer_address+3]=buffer[buffer_address+3]-0x37;
- word_value=(buffer[buffer_address]<<12)|(buffer[buffer_address+1]<<8)|(buffer[buffer_address+2]<<4)|buffer[buffer_address+3];
- return word_value;
- }
- void buffer_value_process()
- {
- cellnumber=buffer_byte_process(17); //串?dāng)?shù)計(jì)算
- cellnumber_offset=16-cellnumber; //串?dāng)?shù)與16串相比的差值
- address_offset=cellnumber_offset*4; //報(bào)文地址偏移量
- current=buffer_word_process(109-address_offset); //電流計(jì)算
- current_bit=current>>15; //電流正負(fù)判斷
- if(current_bit==1)
- current=~current+1; //電流值為補(bǔ)碼,負(fù)數(shù)取反加1得到原碼(符號(hào)位1取反變成了0);
- totalvoltage=buffer_word_process(113-address_offset); //總壓計(jì)算
- for(num=0;num<=15-cellnumber_offset;num++) //單體電壓和最高最低電壓計(jì)算
- {
- singlevoltage[num]=buffer_word_process(19+4*num); //各串單體電壓
- if(num==0)
- {
- single_max=singlevoltage[0];
- single_min=single_max;
- }
- if(singlevoltage[num]>single_max)
- single_max=singlevoltage[num]; //最高單體電壓
- if(singlevoltage[num]<single_min)
- single_min=singlevoltage[num]; //最低單體電壓
- }
- remaincap=buffer_word_process(117-address_offset); //剩余容量
- remaincap2=remaincap;
- totalcap=buffer_word_process(123-address_offset)/100; //總?cè)萘?br />
- cappercentage=(remaincap/totalcap)&0xFF; //SOC計(jì)算
- for(num=0;num<=5;num++) //6個(gè)溫度計(jì)算
- temperature[num]=buffer_word_process(85+4*num-address_offset); //此溫度需減2731并判斷符號(hào)
- }
- void main_display()
- {
- digit4=totalvoltage%10; //總壓個(gè)位數(shù)字
- digit3=totalvoltage/10%10; //總壓十位數(shù)字
- digit2=totalvoltage/100%10; //總壓百位數(shù)字
- digit1=totalvoltage/1000%10; //總壓千位數(shù)字
- write_com(0x80+0x02); //數(shù)據(jù)地址指針從第1行第9位開(kāi)始寫(xiě)總壓值
- write_dat(digit1+0x30); //顯示千位數(shù)字的ASCII碼
- write_dat(digit2+0x30);
- write_dat('.');
- write_dat(digit3+0x30);
- write_dat(digit4+0x30);
- write_dat('V');
- digit4=current%10; //電流個(gè)位數(shù)字
- digit3=current/10%10;
- digit2=current/100%10;
- digit1=current/1000%10;
- if(current_bit==0&&digit1==0) //如果為正電流,且千位為0
- {
- write_com(0xc0);
- write_dat(' ');write_dat(' ');
- write_dat(' ');
- }
- if(current_bit==0&&digit1>0) //如果為正電流,且千位不為0
- {
- write_com(0xc0);
- write_dat(' ');write_dat(' ');
- write_dat(digit1+0x30);
- }
- if(current_bit==1&&digit1==0) //如果為負(fù)電流,且千位為0
- {
- write_com(0xc0);
- write_dat(' ');write_dat(' ');
- write_dat('-');
- }
- if(current_bit==1&&digit1>0) //如果為負(fù)電流,且千位不為0
- {
- write_com(0xc0);write_dat(' ');
- write_dat('-');
- write_dat(digit1+0x30);
- }
- write_dat(digit2+0x30);
- write_dat('.');
- write_dat(digit3+0x30);
- write_dat(digit4+0x30);
- write_dat('A');
- digit4=remaincap2%10; //剩余容量個(gè)位數(shù)字
- digit3=remaincap2/10%10; //十位數(shù)字
- digit2=remaincap2/100%10; //百位數(shù)字
- digit1=remaincap2/1000%10; //千位數(shù)字
- digit0=remaincap2/10000%10; //萬(wàn)位數(shù)字
- write_com(0x80+0x0A); //數(shù)據(jù)地址指針從第1行第10位開(kāi)始寫(xiě)剩余容量
- if(digit0>0)
- write_dat(digit0+0x30); //顯示萬(wàn)位數(shù)字的ASCII碼
- else
- write_dat(' ');
- if(digit0==0&&digit1==0)
- write_dat(' '); //顯示千位數(shù)字的ASCII碼
- else
- write_dat(digit1+0x30);
- write_dat(digit2+0x30);
- write_dat('A');
- write_dat('h');
- digit4=cappercentage%10; //SOC個(gè)位數(shù)字
- digit3=cappercentage/10%10; //十位數(shù)字
- digit2=cappercentage/100%10; //百位數(shù)字
- write_com(0x80+0x10); //數(shù)據(jù)地址指針從第1行第16位開(kāi)始寫(xiě)剩余容量
- if(digit2>0)
- write_dat(digit2+0x30);
- else
- write_dat(' ');
- if(digit2==0&&digit3==0)
- write_dat(' ');
- else
- write_dat(digit3+0x30);
- write_dat(digit4+0x30);
- write_dat('%');
- if(temperature[0]>=2731) //第1節(jié)電芯溫度
- {
- celltemp_unit=1; //單位為正
-
- ……………………
- …………限于本文篇幅 余下代碼請(qǐng)從51黑下載附件…………
復(fù)制代碼
所有資料51hei提供下載:
BMS監(jiān)控儀原理和程序.rar
(19.71 KB, 下載次數(shù): 79)
2020-10-10 18:18 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
|
|