历史上的今天
今天是:2024年10月12日(星期六)
2020年10月12日 | 自制BMS监控仪通过485通讯读BMS信息并显示在LCD2004上
2020-10-12 来源:51hei
公司是做锂电池管理系统的,主要是通信用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即为数值
史海拾趣
|
[摘要〕在对锁相环频率捕捉过程进行数学分析的基础之上,给出了用计算机模拟锁相环频率捕捉过程的基本方法和计算程序。文中对一个具体例子做了模拟,模拟结果与理论分析完全吻合。… 查看全部问答> |
|
最简单的4-20mA输入/5V输出的I/V转换电路 在与电流输出的传感器接口的时候,为了把传感器(变送器)输出的1-10mA或者4-20mA电流信号转换成为电压信号,往往都会在后级电路的最前端配置一个I/V转换电路,图1就是这种电路最简单的应用示意图。 ...… 查看全部问答> |
|
利用SOPC Builder生成系统时,需要添加一个SSRAM模块进去,但是QUARTUS II 7.2版本的SSRAM型号是CY7C1380C,而我的板子上面用到的是IS61LPS12836A_200TQLI,我查看了二者的datasheet,发现二者引脚完全一致,以及真值表也一样,请问能否用CY7C1380C ...… 查看全部问答> |
|
这是串口打印的信息 RomBOOT>By www.mcuzone.com ...Master Clock is ???????? Hz ?FMD_DirectRead lasted 0 ms for 0x46 bytes (timer granularity is 400) Press [ENTER] to launch image stored in flash or [SPACE] to cancel. Initiating ...… 查看全部问答> |
|
用CCS打开TivaWare工程,变异出错(error #10234-D: unresolved symbols remain)... 用CCS打开官方提供的TivaWare里面的工程,添加头文件添加了#include “utils/uartstdio.h\",而且编译器也能找到Uartprintf()函数,但是主程序中调用它时,就会报错 undefined f ...… 查看全部问答> |
|
Cortex-M0开发板种类多,但从目前看STM32Nucleo的开发平台是最好的。 有些厂家的开发平台是自己独立开发的,有些不支持开源GCC.但是STM32Nucleo的平台丰富得难以选择。 1. 支持第三方开发平台。如IAR,Keil.而且是得到开发厂家的支持的,最新版的 ...… 查看全部问答> |




