公司是做锂电池管理系统的,主要是通信用16串锂电池用的保护板(BMS),产品有485接口,可以上传各种测量和告警信息。最近开始学习单片机,自己动手做了一个BMS监控仪,通过485与自家的BMS通讯获取状态信息并显示在LCD2004上。
程序也是自己弄了好久才排除各种bug,现在具备了基本状态信息显示、菜单、16串单体电压显示、各温度显示、BMS状态显示、基本告警信息显示。
这个小产品使用STC89C54RD+,MAX485芯,使用5V升压锂电池供电,在外壳上留出了USB充电接口。
电路原图是自己搞了一个,然后同事帮我画的PCB。制作比较废劲,尤其是壳子不好切割,还把手割伤了。。。
因为工作比较忙,从头到尾断断续续搞了一个来月终于算是完成了,和大家分享一下。
已附上原理图和单片机程序。因自己初学,程序有很多不足,比如没有按模块化编写、逻辑较乱等,希望大家帮忙指点。
制作出来的实物图如下:
电路原理图如下:
单片机源程序如下:
- #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个字符
- uchar code menu_table2[]="TEMPERATURE"; //11个字符
- uchar code menu_table3[]="BMS STATUS"; //10个字符
- uchar code menu_table4[]="ALARM INFO"; //10个字符
- uchar code BMSINFO1[20]={0x7E,0x32,0x36,0x30,0x30,0x34,0x36,0x46,0x32,0x45,0x30,0x30,0x32,0x30,0x31,0x46,0x44,0x31,0x45,0x0D}; //询遥测的命令报文
- uchar code BMSINFO2[20]={0x7E,0x32,0x36,0x30,0x30,0x34,0x36,0x46,0x34,0x45,0x30,0x30,0x32,0x30,0x31,0x46,0x44,0x31,0x43,0x0D}; //询遥信命令报文
- uchar buffer[145]={0}; //用于缓存遥测报文
- sbit lcdrs=P2^5; //指令和数据寄存器选择,高电平时为数据,低电平选择命令
- sbit lcdrw=P2^6; //读写选择,高电平为读,低电平为写
- sbit lcden=P2^7; //使能
- sbit lcdbg=P2^4; //背光,0为开
- sbit beep=P2^0; //蜂鸣器,0为开
- sbit key1=P1^0; //菜单或确认
- sbit key2=P1^1; //上一项
- sbit key3=P1^2; //下一项
- sbit key4=P1^3; //返回或背光开关
- bit data datareceived_flag=0,displayclear=1; //datareceived_flag为遥测报文是否接收完的标志位,current_bit为电流值符号标志位
- 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显示数字的万千百十个位
- uchar cellnumber,cellnumber_offset,address_offset; //串数,串数差,地址偏移量
- uchar cappercentage; //SOC
- uint singlevoltage[15]; //单体电芯电压
- uint temperature[5]; //4个电芯温度和环境温度及功率温度
- uchar display_mode=0,menu_position=1,singlevoltage_page=1; //显示模式
- void delayms(uint z) //延时子程序
- {
- uint x,y;
- for(x=z;x>0;x--)
- for(y=110;y>0;y--);
- }
- void write_com(uchar com) //LCD1602写命令函数
- {
- lcdrs=0; //rs低电平为写命令
- P0=com;
- delayms(4);
- lcden=1; //EN先置高电平
- delayms(4);
- lcden=0; //短暂延时后EN置低电平
- }
- void write_dat(uchar dat) //LCD1602写数据函数
- {
- lcdrs=1; //rs高电平为写数据
- P0=dat;
- delayms(4);
- lcden=1;
- delayms(4);
- lcden=0;
- }
- void UsartInit() //串口初始化
- {
- SCON=0X50; //设置为工作方式1
- TMOD=0X20; //设置计数器工作方式2
- PCON=0X80; //波特率加倍
- TH1=0XFA; //计数器初始值设置,注意11.0592Mhz波特率是9600的
- TL1=0XFA;
- ES=1; //打开接收中断
- EA=1; //打开总中断
- TR1=1; //打开计数器
- }
- void init() //LCD初始化及开机界面
- {
- lcdrw=0;
- lcden=0;
- P0=0;
- write_com(0x38);
- write_com(0x0f); //初始化,开显示,开光标,开光标闪烁
- write_com(0x06); //初始化,读写一个字符后地址指针自动加1
- write_com(0x01); //清屏
- // write_com(0x80); //数据地址指针从0开始
- lcdbg=0; //开背光
- write_com(0x0C); //关光标
- /**********************欢迎界面**************************/
- write_com(0x80+0x44); //第2行第5个字符
- for(num=0;num<11;num++)
- write_dat(welcome1[num]);
- write_com(0x94+0x05); //第3行第6个字符
- for(num=0;num<9;num++)
- write_dat(welcome2[num]);
- delayms(1000);
- beep=0;
- delayms(60);
- beep=1;
- /********************************************************/
- }
- /********************************************************************************
- 计算报文缓存中的一个字节
- ********************************************************************************/
- 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即为数值
- else
- buffer[buffer_address]=buffer[buffer_address]-0x37; //若为大于9即为A~F的字符,减0x37即为数值
- 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;
- }
- /********************************************************************************
- 计算报文缓存中的一个字
- ********************************************************************************/
- 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即为数值
- else
- buffer[buffer_address]=buffer[buffer_address]-0x37; //若为大于9即为A~F的字符,减0x37即为数值
- 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即为数值
- else
- buffer[buffer_address+2]=buffer[buffer_address+2]-0x37; //若为大于9即为A~F的字符,减0x37即为数值
- 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); //串数计算
- cellnumber_offset=16-cellnumber; //串数与16串相比的差值
- address_offset=cellnumber_offset*4; //报文地址偏移量
- current=buffer_word_process(109-address_offset); //电流计算
- current_bit=current>>15; //电流正负判断
- if(current_bit==1)
- current=~current+1; //电流值为补码,负数取反加1得到原码(符号位1取反变成了0);
- totalvoltage=buffer_word_process(113-address_offset); //总压计算
- for(num=0;num<=15-cellnumber_offset;num++) //单体电压和最高最低电压计算
- {
- 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; //总容量
- cappercentage=(remaincap/totalcap)&0xFF; //SOC计算
- for(num=0;num<=5;num++) //6个温度计算
- temperature[num]=buffer_word_process(85+4*num-address_offset); //此温度需减2731并判断符号
- }
- void main_display()
- {
- digit4=totalvoltage%10; //总压个位数字
- digit3=totalvoltage/10%10; //总压十位数字
- digit2=totalvoltage/100%10; //总压百位数字
- digit1=totalvoltage/1000%10; //总压千位数字
- write_com(0x80+0x02); //数据地址指针从第1行第9位开始写总压值
- write_dat(digit1+0x30); //显示千位数字的ASCII码
- write_dat(digit2+0x30);
- write_dat('.');
- write_dat(digit3+0x30);
- write_dat(digit4+0x30);
- write_dat('V');
- digit4=current%10; //电流个位数字
- 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) //如果为负电流,且千位为0
- {
- write_com(0xc0);
- write_dat(' ');write_dat(' ');
- write_dat('-');
- }
- if(current_bit==1&&digit1>0) //如果为负电流,且千位不为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; //剩余容量个位数字
- digit3=remaincap2/10%10; //十位数字
- digit2=remaincap2/100%10; //百位数字
- digit1=remaincap2/1000%10; //千位数字
- digit0=remaincap2/10000%10; //万位数字
- write_com(0x80+0x0A); //数据地址指针从第1行第10位开始写剩余容量
- if(digit0>0)
- write_dat(digit0+0x30); //显示万位数字的ASCII码
- else
- write_dat(' ');
- if(digit0==0&&digit1==0)
- write_dat(' '); //显示千位数字的ASCII码
- else
- write_dat(digit1+0x30);
- write_dat(digit2+0x30);
- write_dat('A');
- write_dat('h');
- digit4=cappercentage%10; //SOC个位数字
- digit3=cappercentage/10%10; //十位数字
- digit2=cappercentage/100%10; //百位数字
- write_com(0x80+0x10); //数据地址指针从第1行第16位开始写剩余容量
- 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节电芯温度
- {
- celltemp_unit=1; //单位为正
-
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
所有资料51hei提供下载:
BMS监控仪原理和程序.rar
(19.71 KB, 下载次数: 69)
|