Files
jiacun-20s-200A/USER/global.c
T
2026-08-25 17:30:40 +08:00

1494 lines
34 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
******************************************************************************
* @file tim.c
* @author Jerry
* @version V2.1
* @date 19-April-2022
* @brief tim program body.
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "global.h"
#include "string.h"
#include "stdio.h"
ONLINE_MEMORY onlineMem;
VERSION_MEMORY VersionMem;
PARA_MEMORY paraMem;
BMS_MEMORY bmsMem;
BMS_MEMORY bmsMem_slave;
PROTOCOL_SRNE_MEMORY SRNEMem;
PROTOCOL_VOLTRONIC_MEMORY VoltronicMem;
PROTOCOL_SMK_MEMORY SMKMem;
PROTOCOL_Growatt_MEMORY GrowattMem;
UNION_VOLTAGE_STATUS staVol;
UNION_CURRENT_STATUS staCur;
UNION_TEMPERA_STATUS1 staTemp1;
UNION_TEMPERA_STATUS2 staTemp2;
UNION_MOSFET_STATUS staMos;
UNION_PACK_STATUS staPack;
uint16_t alarm_occ_old;
uint16_t alarm_ocd1_old;
uint16_t alarm_puv_old;
uint8_t chgCurLimit_changeFlg;
uint8_t dsgCurLimit_changeFlg;
uint8_t dsgVolLimit_changeFlg;
const uint8_t CRC8Table[]=
{ //120424-1 CRC Table
0x00,0x07,0x0E,0x09,0x1C,0x1B,0x12,0x15,0x38,0x3F,0x36,0x31,0x24,0x23,0x2A,0x2D,
0x70,0x77,0x7E,0x79,0x6C,0x6B,0x62,0x65,0x48,0x4F,0x46,0x41,0x54,0x53,0x5A,0x5D,
0xE0,0xE7,0xEE,0xE9,0xFC,0xFB,0xF2,0xF5,0xD8,0xDF,0xD6,0xD1,0xC4,0xC3,0xCA,0xCD,
0x90,0x97,0x9E,0x99,0x8C,0x8B,0x82,0x85,0xA8,0xAF,0xA6,0xA1,0xB4,0xB3,0xBA,0xBD,
0xC7,0xC0,0xC9,0xCE,0xDB,0xDC,0xD5,0xD2,0xFF,0xF8,0xF1,0xF6,0xE3,0xE4,0xED,0xEA,
0xB7,0xB0,0xB9,0xBE,0xAB,0xAC,0xA5,0xA2,0x8F,0x88,0x81,0x86,0x93,0x94,0x9D,0x9A,
0x27,0x20,0x29,0x2E,0x3B,0x3C,0x35,0x32,0x1F,0x18,0x11,0x16,0x03,0x04,0x0D,0x0A,
0x57,0x50,0x59,0x5E,0x4B,0x4C,0x45,0x42,0x6F,0x68,0x61,0x66,0x73,0x74,0x7D,0x7A,
0x89,0x8E,0x87,0x80,0x95,0x92,0x9B,0x9C,0xB1,0xB6,0xBF,0xB8,0xAD,0xAA,0xA3,0xA4,
0xF9,0xFE,0xF7,0xF0,0xE5,0xE2,0xEB,0xEC,0xC1,0xC6,0xCF,0xC8,0xDD,0xDA,0xD3,0xD4,
0x69,0x6E,0x67,0x60,0x75,0x72,0x7B,0x7C,0x51,0x56,0x5F,0x58,0x4D,0x4A,0x43,0x44,
0x19,0x1E,0x17,0x10,0x05,0x02,0x0B,0x0C,0x21,0x26,0x2F,0x28,0x3D,0x3A,0x33,0x34,
0x4E,0x49,0x40,0x47,0x52,0x55,0x5C,0x5B,0x76,0x71,0x78,0x7F,0x6A,0x6D,0x64,0x63,
0x3E,0x39,0x30,0x37,0x22,0x25,0x2C,0x2B,0x06,0x01,0x08,0x0F,0x1A,0x1D,0x14,0x13,
0xAE,0xA9,0xA0,0xA7,0xB2,0xB5,0xBC,0xBB,0x96,0x91,0x98,0x9F,0x8A,0x8D,0x84,0x83,
0xDE,0xD9,0xD0,0xD7,0xC2,0xC5,0xCC,0xCB,0xE6,0xE1,0xE8,0xEF,0xFA,0xFD,0xF4,0xF3
};
//look-up table calculte CRC
uint8_t CRC8_Cal(uint8_t *pdata, uint8_t len)
{
uint8_t crc8 = 0;
for(; len > 0; len--)
{
crc8 = CRC8Table[crc8^*pdata];
pdata++;
}
return(crc8);
}
//和Flash_CRC校验同理的值
uint16_t CRC16_FirmtoEE(uint8_t *data, uint16_t len)
{
uint16_t i,j;
uint16_t mCrc = 0;
for(i=0; i<len; i++)
{
mCrc ^= ((uint16_t)data[i] << 8);
for(j=8; j!=0; j--)
{
if(mCrc & 0x8000)
mCrc = mCrc << 1 ^ 0x1021;
else
mCrc = mCrc << 1;
}
}
return mCrc;
}
//转换为ASCII字符传输
uint8_t toASCII(uint8_t data)
{
uint8_t result;
if(data<10) //0~9 -> 0x30~0x39
{
result = 0x30 + data;
}
else //A~F -> 0x41~0x46
{
result = 0x37 + data;
}
return result;
}
//根据当前时间取伪随机数,范围AddrMax+1~65534
#define MODULUS 2147483647
#define MULTIPLIER 1103515245
#define ADDEND 12345
uint16_t get_random(void)
{
uint32_t time;
uint32_t rand;
uint16_t index;
time=RTC_GetCounter();
rand=(time * MULTIPLIER + ADDEND) % MODULUS; //取值范围是0到MODULUS-1
index=rand%(65535-(AddrMax+1)-1+1)+(AddrMax+1); //取值范围是AddrMax+1~65534
return index;
}
//功能函数:获取字符串里的参数值
uint8_t GetStr(const char* dataKey, char End1, char End2, char* InStr, char* OutStr)
{
uint8_t datalen = 0; //字符串长度
char* dataStart = strstr(InStr, dataKey); //找dataKey
if(dataStart == NULL)
{
return 0; //找不到dataKey,返回0
}
dataStart += strlen(dataKey); //跳过dataKey
char* dataEnd = strchr(dataStart, End1); //先找End1
if(dataEnd == NULL)
{
dataEnd = strchr(dataStart, End2); //再找End2
}
if(dataEnd == NULL)
{
return 0; //找不到End1和End2,返回0
}
datalen = dataEnd - dataStart;
strncpy(OutStr, dataStart, datalen);
OutStr[datalen] = '\0';
return datalen; //返回内容长度
}
//功能函数:获取request_id
uint8_t GetID(char* InStr, char* OutStr)
{
//{"request_id":"123124sdf",…}
const char* dataKey = "\"request_id\":\"";
char End = '\"';
uint8_t datalen = 0; //字符串长度
char* dataStart = strstr(InStr, dataKey); //找dataKey
if(dataStart == NULL)
{
return 0; //找不到dataKey,返回0
}
dataStart += strlen(dataKey);
char* dataEnd = strchr(dataStart, End); //找End
if(dataEnd == NULL)
{
return 0; //找不到End,返回0
}
datalen = dataEnd - dataStart;
strncpy(OutStr, dataStart, datalen);
OutStr[datalen] = '\0';
return datalen; //返回内容长度
}
//功能函数:获取字符串里的参数值
//增加:只输入名称,会寻找之后的第一个':'
//注意:只适合"Name":Value这种格式,结束符固定','或'}'
uint8_t GetStrFromJson(const char* dataKey, char* InStr, char* OutStr)
{
uint8_t datalen = 0; //字符串长度
char* dataStart = strstr(InStr, dataKey); //找dataKey
if(dataStart == NULL)
{
return 0; //找不到dataKey,返回0
}
dataStart += strlen(dataKey); //跳过dataKey
dataStart = strchr(dataStart, ':');//找dataKey之后的第一个':'
if(dataStart == NULL)
{
return 0; //找不到':',返回0
}
dataStart += 1; //跳过冒号
while (*dataStart == ' ') dataStart++; //跳过空白
char* dataEnd = strchr(dataStart, ','); //先找End1
if(dataEnd == NULL)
{
dataEnd = strchr(dataStart, '}'); //再找End2
}
if(dataEnd == NULL)
{
return 0; //找不到End1和End2,返回0
}
datalen = dataEnd - dataStart;
strncpy(OutStr, dataStart, datalen);
OutStr[datalen] = '\0';
return datalen; //返回内容长度
}
//计算无符号数在十进制转字符串后,要多少字节
//输入最大5位数,保险起见再加1位
uint8_t uint_str_len(uint32_t value)
{
if(value < 10) return 1;
else if((value < 100)) return 2;
else if((value < 1000)) return 3;
else if((value < 10000)) return 4;
else if((value < 100000)) return 5;
else if((value < 1000000)) return 6;
else if((value < 10000000)) return 7;
else if((value < 100000000)) return 8;
else if((value < 1000000000)) return 9;
return 10;
}
//计算有符号数在十进制转字符串后,要多少字节
uint8_t int_str_len(int32_t value)
{
uint32_t abs_vlaue;
uint8_t abs_len;
if(value == INT32_MIN) return 11; //"-2147483648"
abs_vlaue = (value<0) ? (uint32_t)(-value):(uint32_t)value;
abs_len = uint_str_len(abs_vlaue);
return (value<0) ? abs_len+1:abs_len;
}
//实时更新并机总数据和数据判断,1s执行1次
uint8_t OnlineNum; //BMS正常工作数量
uint8_t OnlineFirstAddr; //第一个是正常工作的并机地址
uint8_t chg_curLimitNum; //充电限流置0的个数,SOC大于100%的时候启用
uint8_t dsg_curLimitNum; //放电限流置0的个数,SOC小于10%的时候启用
uint8_t chg_cur0Num; //因报警触发充电限流40A时,出现保护限流0A的个数
void canMem_refresh(void)
{
uint8_t i;
int32_t oldcur = canMem[0].cur; //保留上一次的总电流值,在计算值异常时放入
/*并机汇总信息的计算*/
//1.485采集数据初始化
canMem[0].status_byte1 = 0;
canMem[0].status_byte2 = 0;
canMem[0].status_byte3 = 0;
canMem[0].status_byte4 = 0;
canMem[0].soc = 0;
canMem[0].soh = 0;
canMem[0].cur = 0;
canMem[0].temp = 0;
canMem[0].VolMax = 0;
canMem[0].VolMin = 0;
canMem[0].VolMaxIndex = 0;
canMem[0].VolMinIndex = 0;
canMem[0].TempMax = 0;
canMem[0].TempMin = 0;
canMem[0].TempMaxIndex = 0;
canMem[0].TempMinIndex = 0;
//Wh版屏幕
canMem[0].cumuliCap = 0;
canMem[0].cycleCnt = 0;
//负值矫正
canMem[0].cellVoltageMax = 0;
canMem[0].cellVoltageMin = 0;
canMem[1].status_byte1 = bmsMem.can_status_byte1;
canMem[1].status_byte2 = bmsMem.can_status_byte2;
canMem[1].status_byte3 = bmsMem.can_status_byte3;
canMem[1].status_byte4 = bmsMem.can_status_byte4;
canMem[1].soc = bmsMem.can_soc;
canMem[1].soh = bmsMem.can_soh;
canMem[1].cur = (int16_t)bmsMem.can_cur;
canMem[1].temp = (int16_t)bmsMem.can_temp;
canMem[1].VolMax = bmsMem.can_VolMax;
canMem[1].VolMin = bmsMem.can_VolMin;
canMem[1].VolMaxIndex = bmsMem.can_VolMaxIndex;
canMem[1].VolMinIndex = bmsMem.can_VolMinIndex;
canMem[1].TempMax = bmsMem.can_TempMax;
canMem[1].TempMin = bmsMem.can_TempMin;
canMem[1].TempMaxIndex = bmsMem.can_TempMaxIndex;
canMem[1].TempMinIndex = bmsMem.can_TempMinIndex;
//Wh版屏幕
canMem[1].cumuliCap = bmsMem.can_cumuliCap;
canMem[1].cycleCnt = bmsMem.can_cycleCnt;
//负值矫正
canMem[1].cellVoltageMax = (int16_t)(canMem[1].VolMax*32/5)*5/32;
canMem[1].cellVoltageMin = (int16_t)(canMem[1].VolMin*32/5)*5/32;
#if Addr_SetAuto
if((paraMem.addr_FREE_Flg == 0) && (assignAddr_random != 0))
{
canMem[1].com = assignAddr_random; //赋值主机自身生成的队列标志位
}
else
#endif
{
canMem[1].com = 1; //就算此时没有生成好的队列标志位,也要置1不能是0(按逻辑其实这里一定有队列标志位)
}
//2.统计当前通讯正常从机数量
bmsMem.E2_485Snum = 1;
for(i=2;i<=paraMem.PACK_NUM;i++)
{
if(canMem[i].com != 0) //引入队列标志位后的改动
{
bmsMem.E2_485Snum++;
}
}
//3.计算并机总值
//报警记录的并机值
for(i=1;i<=paraMem.PACK_NUM;i++)
{
if(canMem[i].com != 0)
{
canMem[0].status_byte1 |= canMem[i].status_byte1;
canMem[0].status_byte2 |= canMem[i].status_byte2;
canMem[0].status_byte3 |= canMem[i].status_byte3;
canMem[0].status_byte4 |= canMem[i].status_byte4;
}
}
//参数的并机值
//当处于并机模式,只纳入在线正常工作的BMS的数据
if(bmsMem.E2_485Snum >= 2)
{
/*统计工作正常的数量,addr=1也要检测*/
OnlineNum = 0;
OnlineFirstAddr = 0;
for(i=1;i<=paraMem.PACK_NUM;i++)
{
if(canMem[i].com != 0) //引入队列标志位后的改动
{
//没有任何报警的BMS才认为是在线的(不考虑总体/单体过压)
if( ((canMem[i].status_byte1 & 0x067e) == 0) && ((canMem[i].status_byte2 & 0x00ff) == 0) && ((canMem[i].status_byte3 & 0x0008) == 0) && ((canMem[i].status_byte4 & 0x0f7f) == 0) )
{
canMem[0].soc += canMem[i].soc;
canMem[0].soh += canMem[i].soh;
canMem[0].cur += canMem[i].cur;
canMem[0].temp += canMem[i].temp;
//Wh版屏幕
canMem[0].cumuliCap += canMem[i].cumuliCap;
canMem[0].cycleCnt += canMem[i].cycleCnt;
if(OnlineFirstAddr == 0) OnlineFirstAddr = i;
OnlineNum++;
}
}
}
if(OnlineNum != 0) //存在才能进行除计算
{
//计算平均值
canMem[0].soc = canMem[0].soc / OnlineNum;
canMem[0].soh = canMem[0].soh / OnlineNum;
canMem[0].temp = canMem[0].temp / OnlineNum;
//计算最大最小值
canMem[0].VolMax = canMem[OnlineFirstAddr].VolMax;
canMem[0].VolMin = canMem[OnlineFirstAddr].VolMin;
canMem[0].cellVoltageMax = canMem[OnlineFirstAddr].cellVoltageMax; //负值矫正
canMem[0].cellVoltageMin = canMem[OnlineFirstAddr].cellVoltageMin; //负值矫正
canMem[0].VolMaxIndex = canMem[OnlineFirstAddr].VolMaxIndex;
canMem[0].VolMinIndex = canMem[OnlineFirstAddr].VolMinIndex;
canMem[0].TempMax = canMem[OnlineFirstAddr].TempMax;
canMem[0].TempMin = canMem[OnlineFirstAddr].TempMin;
canMem[0].TempMaxIndex = canMem[OnlineFirstAddr].TempMaxIndex;
canMem[0].TempMinIndex = canMem[OnlineFirstAddr].TempMinIndex;
for(i=OnlineFirstAddr+1;i<=paraMem.PACK_NUM;i++)
{
if(canMem[i].com != 0) //只比较在线的
{
//没有执行任何保护的BMS才认为是在线的(不考虑总体/单体过压)
if( ((canMem[i].status_byte1 & 0x067e) == 0) && ((canMem[i].status_byte2 & 0x00ff) == 0) && ((canMem[i].status_byte3 & 0x0008) == 0) && ((canMem[i].status_byte4 & 0x0f7f) == 0) )
{
//if(canMem[i].VolMax > canMem[0].VolMax)
if(canMem[i].cellVoltageMax > canMem[0].cellVoltageMax)
{
canMem[0].cellVoltageMax = canMem[i].cellVoltageMax; //负值矫正
canMem[0].VolMax = canMem[i].VolMax;
canMem[0].VolMaxIndex = canMem[i].VolMaxIndex+bmsMem.ucCellNum*(i-1);
}
//if(canMem[i].VolMin < canMem[0].VolMin)
if(canMem[i].cellVoltageMin < canMem[0].cellVoltageMin)
{
canMem[0].cellVoltageMin = canMem[i].cellVoltageMin; //负值矫正
canMem[0].VolMin = canMem[i].VolMin;
canMem[0].VolMinIndex = canMem[i].VolMinIndex+bmsMem.ucCellNum*(i-1);
}
if(canMem[i].TempMax > canMem[0].TempMax)
{
canMem[0].TempMax = canMem[i].TempMax;
canMem[0].TempMaxIndex = canMem[i].TempMaxIndex+4*(i-1);
}
if(canMem[i].TempMin < canMem[0].TempMin)
{
canMem[0].TempMin = canMem[i].TempMin;
canMem[0].TempMinIndex = canMem[i].TempMinIndex+4*(i-1);
}
}
}
}
}
}
//只连一台,只显示主机自身
else
{
//赋值1,不影响协议显示
OnlineNum = 1;
//单板直接等于主机值
canMem[0].soc = canMem[1].soc;
canMem[0].soh = canMem[1].soh;
canMem[0].cur = canMem[1].cur;
canMem[0].temp = canMem[1].temp;
canMem[0].VolMax = canMem[1].VolMax;
canMem[0].VolMin = canMem[1].VolMin;
canMem[0].VolMaxIndex = canMem[1].VolMaxIndex;
canMem[0].VolMinIndex = canMem[1].VolMinIndex;
canMem[0].TempMax = canMem[1].TempMax;
canMem[0].TempMin = canMem[1].TempMin;
canMem[0].TempMaxIndex = canMem[1].TempMaxIndex;
canMem[0].TempMinIndex = canMem[1].TempMinIndex;
//Wh版屏幕
canMem[0].cumuliCap = canMem[1].cumuliCap;
canMem[0].cycleCnt = canMem[1].cycleCnt;
//负值矫正
canMem[0].cellVoltageMax = (int16_t)(canMem[0].VolMax*32/5)*5/32;
canMem[0].cellVoltageMin = (int16_t)(canMem[0].VolMin*32/5)*5/32;
}
//额外补丁:超过600A的异常电流值不显示
if( (canMem[0].cur >= 60000) || (canMem[0].cur <= (-60000)) ) //600A = 60000*0.01A
{
canMem[0].cur = oldcur;
}
//4.数据分析判断
//逆变器通信:充到100%,启用禁充标志,低于99%释放
if((paraMem.requestFlg_enable & 0x0080) != 0)
{
if(canMem[0].soc >= paraMem.chg_forbid_Soc)
{
chg_forbidFlg = 1;
}
else if(canMem[0].soc <= paraMem.chg_forbid_rSoc)
{
chg_forbidFlg = 0;
}
}
else
{
chg_forbidFlg = 0;
}
//逆变器通信:小于10%,启用禁放标志;超过20%后释放
if((paraMem.requestFlg_enable & 0x0040) != 0)
{
if(canMem[0].soc <= paraMem.dsg_forbid_Soc)
{
dsg_forbidFlg = 1;
}
else if(canMem[0].soc >= paraMem.dsg_forbid_rSoc)
{
dsg_forbidFlg = 0;
}
//当PACK电压=放电请求电压时,BMS发送禁放指令
if(bmsMem.packVoltage <= bmsMem.inverter_dsgVolLimit*100)
{
dsg_forbidFlg = 1;
}
}
else
{
dsg_forbidFlg = 0;
}
//逆变器通信:小于10%,启用强充标志;超过20%后释放
if((paraMem.requestFlg_enable & 0x0020) != 0)
{
if(canMem[0].soc <= paraMem.chg_force_Soc)
{
chg_forceFlg = 1;
}
else if(canMem[0].soc >= paraMem.chg_force_rSoc)
{
chg_forceFlg = 0;
}
}
else
{
chg_forceFlg = 0;
}
//逆变器通信:出现充电单体/总体过压告警后,逆变器充电限流固定40A
if((canMem[0].status_byte3 & 0x0500) != 0)
{
chg_curlimitFlg = 1;
}
else
{
chg_curlimitFlg = 0;
}
//对于正常在线的PACK,若满足条件则计数,以在未完全禁充禁放时,更改限压限流值
chg_curLimitNum = 0;
dsg_curLimitNum = 0;
chg_cur0Num = 0;
for(i=1;i<=paraMem.PACK_NUM;i++)
{
if(canMem[i].com != 0) //引入队列标志位后的改动
{
//没有任何报警的BMS才认为是在线的(不考虑总体/单体过压)
if( ((canMem[i].status_byte1 & 0x067e) == 0) && ((canMem[i].status_byte2 & 0x00ff) == 0) && ((canMem[i].status_byte3 & 0x0008) == 0) && ((canMem[i].status_byte4 & 0x0f7f) == 0) )
{
if((canMem[i].soc >= paraMem.chg_forbid_Soc) && ((paraMem.requestFlg_enable & 0x0080) != 0)) //禁充个数
{
chg_curLimitNum++;
}
if((canMem[i].soc < paraMem.dsg_forbid_Soc) && ((paraMem.requestFlg_enable & 0x0040) != 0)) //禁放个数
{
dsg_curLimitNum++;
}
if((canMem[i].status_byte1 & 0x0101) != 0) //出现过压保护的个数
{
chg_cur0Num++;
}
}
}
}
}
//更新回复上位机对并机信息的请求所需的数据,1s执行1次
void onlineMem_refresh(void)
{
uint8_t i;
/**上位机读信息的处理**/
//上位机询问并机情况的回复
onlineMem.Online[0] = 0; //3.11 表示工作正常的并机个数
for(i=1; i<=paraMem.PACK_NUM; i++)
{
if(canMem[i].com != 0)
{
if( ((canMem[i].status_byte1 & 0x067e) ==0) && ((canMem[i].status_byte2 & 0x00ff) ==0) && ((canMem[i].status_byte3 & 0x0008) ==0) && ((canMem[i].status_byte4 & 0x0f7f) ==0) )
{
onlineMem.Online[0]++; //该从机的(不管总体/单体过压报警的其他)报警都没有,就认为在线数量+1
if((canMem[i].status_byte1 & 0x01) ==0) //若不存在过压报警,确认正常
{
onlineMem.Online[i] = 0xAA; //正常
}
else
{
onlineMem.Online[i] = 0xBB; //存在报警
}
}
else
{
onlineMem.Online[i] = 0xBB; //存在报警
}
}
else
{
onlineMem.Online[i] = 0x00;
}
}
//若实际不在并机,则固定1个
if(bmsMem.E2_485Snum == 1)
{
onlineMem.Online[0] = 1;
}
}
//参数改动函数,1s执行1次
uint8_t Addr_SetCount;//改虚地址后等待10s
void ParaChange(void)
{
uint8_t tempWr[2];
#if Addr_SetAuto
uint8_t tmpRd;
//分配地址-从机:虚地址10s自恢复
if((bmsMem.E2_485Addr == 0) || (bmsMem.E2_485Addr > AddrMax)) //若地址长时间保持为虚地址,自动恢复原地址
{
Addr_SetCount++;
if(Addr_SetCount > 10) //10s
{
Addr_SetCount=0;
EEPROM_RdMulByte(EE_ADDR,&tmpRd);
if((tmpRd>=1) && (tmpRd<=AddrMax))
{
bmsMem.E2_485Addr = tmpRd;
}
else
{
bmsMem.E2_485Addr = 2;
}
scr_RdData_Index = bmsMem.E2_485Addr;
}
}
else
{
Addr_SetCount=0;
}
#endif
#if LTE_Conn
if((sleep_flag == 0) && (LTE_sleep_flag == 2))
{
LTE_sleep_flag = 0xAA; //4G执行退出休眠
}
#endif
/**上位机写参数的处理**/
//上位机修改容量
if(bmsMem.write_Capacity !=0)
{
if((bmsMem.write_Capacity>0) && (bmsMem.write_Capacity<=1000)) //范围1~1000Ah
{
tempWr[0] = (bmsMem.write_Capacity >> 8) & 0xff;
tempWr[1] = (bmsMem.write_Capacity >> 0) & 0xff;
EEPROM_WrMulByte(EE_NCC,tempWr);
delay_ms(5);
//赋值额定容量
bmsMem.ncc = 3600 * 1000 * bmsMem.write_Capacity;
ncc_Ah = bmsMem.write_Capacity;
//赋值满充容量=额定容量
fcc = bmsMem.ncc;
fcc_Ah = ncc_Ah;
tmpWrFCC[0] = (fcc>>24) & 0xff;
tmpWrFCC[1] = (fcc>>16) & 0xff;
tmpWrFCC[2] = (fcc>> 8) & 0xff;
tmpWrFCC[3] = (fcc>> 0) & 0xff;
tmpWrFCC[4] = tmpWrFCC[0] ^ 0xff;
tmpWrFCC[5] = tmpWrFCC[1] ^ 0xff;
tmpWrFCC[6] = tmpWrFCC[2] ^ 0xff;
tmpWrFCC[7] = tmpWrFCC[3] ^ 0xff;
EEPROM_WrMulByte(EE_FCC,tmpWrFCC);
delay_ms(20);
//赋值剩余容量=新的满充容量*SOC
bmsMem.rcc = fcc/100 * bmsMem.soc;
rcc_Ah = fcc_Ah * bmsMem.soc / 100;
oldrcc_Ah = rcc_Ah;
//改额定容量不影响此前的满放满充过程
bmsMem.write_Capacity = 0;
}
else
{
bmsMem.write_Capacity = 0;
}
}
//上位机修改SOC
if(bmsMem.write_Soc !=0)
{
if(bmsMem.write_Soc <= 100) //范围判断
{
EEPROM_WrMulByte(EE_SOC,&bmsMem.write_Soc);
delay_ms(5);
//赋值SOC
bmsMem.soc = bmsMem.write_Soc;
//赋值剩余容量=满充容量*新的SOC
bmsMem.rcc = fcc/100 * bmsMem.soc;
rcc_Ah = fcc_Ah * bmsMem.soc / 100;
oldrcc_Ah = rcc_Ah;
//在满放满充过程中,修改SOC会影响效果,不再执行
if(fcc_CaliStartFlag == 1)
{
fcc_CaliStartFlag = 0;
if(LSEErrFlag == 0)
{
EEPROM_WrMulByte(EE_FCC_TIME,ClearEE);
delay_ms(5);
}
}
bmsMem.write_Soc = 0;
}
else if(bmsMem.write_Soc == 0xAA)
{
bmsMem.write_Soc = 0;
EEPROM_WrMulByte(EE_SOC,&bmsMem.write_Soc);
delay_ms(5);
//赋值SOC
bmsMem.soc = bmsMem.write_Soc;
//赋值剩余容量=满充容量*新的SOC
bmsMem.rcc = fcc/100 * bmsMem.soc;
rcc_Ah = fcc_Ah * bmsMem.soc /100;
oldrcc_Ah = rcc_Ah;
//在满放满充过程中,修改SOC会影响效果,不再执行
if(fcc_CaliStartFlag == 1)
{
fcc_CaliStartFlag = 0;
if(LSEErrFlag == 0)
{
EEPROM_WrMulByte(EE_FCC_TIME,ClearEE);
delay_ms(5);
}
}
}
else
{
bmsMem.write_Soc = 0;
}
}
//上位机修改休眠是否启用的标志位
if((paraMem.sleep_min_disable & 0x8000) == 0) //现在启用休眠1
{
if((sleep_enableflag & 0x01) == 0) //之前不执行休眠1
{
//在第一次发现不同后更新sleep
sleep_enableflag |= 0x01;
SLEEP_Refresh();
}
}
else
{
//保持清零
sleep_enableflag &= 0xfe;
sleeptimecount=0;
//若休眠模式都已关闭,退出休眠
if(sleep_enableflag == 0) sleep_flag = 0;
}
if((paraMem.sleep2_min_disable & 0x8000) == 0) //现在启用休眠2
{
if((sleep_enableflag & 0x02) == 0) //之前不执行休眠2
{
//在第一次发现不同后更新sleep
sleep_enableflag |= 0x02;
SLEEP2_Refresh();
}
}
else
{
//保持清零
sleep_enableflag &= 0xfd;
sleep2timecount=0;
//若休眠模式都已关闭,退出休眠
if(sleep_enableflag == 0) sleep_flag = 0;
}
//上位机解锁电流保护次数超限锁定
if((CTRL_Order & 0x10) != 0)
{
CTRL_Order &= 0xef; //用完恢复
bmsMem.balanceStatus &= 0xf8ff; //~0x0700 可解除3种锁定,短路保护不可解除
}
//更新了充电过流告警电流
if(alarm_occ_old != paraMem.alarm_occ)
{
alarm_occ_old = paraMem.alarm_occ;
chgCurLimit_changeFlg = 1;
}
//更新了放电过流1告警电流
if(alarm_ocd1_old != paraMem.alarm_ocd1)
{
alarm_ocd1_old = paraMem.alarm_ocd1;
dsgCurLimit_changeFlg = 1;
}
//更新了总体欠压告警电压
if(alarm_puv_old != paraMem.alarm_puv)
{
alarm_puv_old = paraMem.alarm_puv;
dsgVolLimit_changeFlg = 1;
}
//充放电过流告警,影响逆变器充放电限流
if((chgCurLimit_changeFlg == 1) || (dsgCurLimit_changeFlg == 1) || (dsgVolLimit_changeFlg == 1))
{
//修改充电过流告警电流后,逆变器充电限流值=该值-10A
if(chgCurLimit_changeFlg == 1)
{
chgCurLimit_changeFlg = 0;
bmsMem.inverter_chgCurLimit = paraMem.alarm_occ * 10 - 100;
}
//修改放电过流1告警电流后,逆变器放电限流值=该值-10A
if(dsgCurLimit_changeFlg == 1)
{
dsgCurLimit_changeFlg = 0;
bmsMem.inverter_dsgCurLimit = paraMem.alarm_ocd1 * 10 - 100;
}
//修改总体欠压告警电压后,逆变器放电限压值=该值
if(dsgVolLimit_changeFlg == 1)
{
dsgVolLimit_changeFlg = 0;
bmsMem.inverter_dsgVolLimit = paraMem.alarm_puv;
}
//bmsMem中数据更新到FLASH A区和B区和AFE EEPORM
if((MEMORY_UpdateFlash(FLASH_DATA_A_BASE) == 0) && (MEMORY_UpdateFlash(FLASH_DATA_B_BASE) == 0))
{
staPack.bits.flashUpdate = 0;
}
else
{
staPack.bits.flashUpdate = 1;
}
bmsMem.packStatus = staPack.byte;
}
}
//分配地址过程中,地址变动很快,故单独拎出直接放while循环里
void Addr_Set(void)
{
if(bmsMem.write_Addr !=0)
{
if(bmsMem.write_Addr <= AddrMax) //范围判断
{
EEPROM_WrMulByte(EE_ADDR,&bmsMem.write_Addr);
delay_ms(5);
bmsMem.E2_485Addr = bmsMem.write_Addr;
bmsMem.write_Addr = 0;
#if Addr_SetAuto
if(paraMem.addr_FREE_Flg == 0)
{
uint8_t tmp[2]={0,0};
//分配地址的状态(此时可接收队列标志位)
assignAddr_State = 2;
bmsMem.can_ArrayIndex = 0;
EEPROM_WrMulByte(EE_ASSIGN,tmp);
delay_ms(5);
}
#endif
//改地址后,屏幕显示也对应修改
scr_RdData_Index = bmsMem.E2_485Addr;
}
else
{
bmsMem.write_Addr = 0;
}
}
//因IO3变动而清空队列标志
if(ClearArray_Flag == 1)
{
uint8_t tmp[2]={0,0};
ClearArray_Flag = 0;
EEPROM_WrMulByte(EE_ASSIGN,tmp);
delay_ms(5);
}
#if Addr_SetAuto
if(paraMem.addr_FREE_Flg == 0)
{
//从机根据自身地址,决定IO2输出给下一从机的电平
if((bmsMem.E2_485Addr >= 2) && (bmsMem.E2_485Addr <= AddrMax))
{
IO2_OUTReset(); //正常地址的OUT引脚置低
}
else if(bmsMem.E2_485Addr > AddrMax)
{
IO2_OUTSet(); //OUT引脚置高,让下一个从机进入等待
}
}
else
{
IO2_OUTReset(); //所有地址保持置低
}
#endif
}
//休眠1的计时起点更新
void SLEEP_Refresh(void)
{
if(LSEErrFlag!=1)
sleeptimecount=RTC_GetCounter();
else
sleep_Moni_Count=SLEEP_MON_CNT;
}
//休眠2的计时起点更新
void SLEEP2_Refresh(void)
{
//满足休眠电压,无均衡,无电流
if((cellVoltageMin <= sleep2Vol) && ((bmsMem.balanceStatus & 0x0001) == 0) && (bmsMem.packCurrent > (-200)) && (bmsMem.packCurrent < 200))
{
//无除过压以外的保护
if(((bmsMem.bStatus1 & 0x067e) == 0) && ((bmsMem.bStatus2 & 0x00ff) == 0) && ((bmsMem.bStatus3 & 0x0008) == 0) && ((bmsMem.temperaStatus & 0x0f7f) == 0))
{
if(LSEErrFlag!=1)
sleep2timecount=RTC_GetCounter();
else
sleep2_Moni_Count=SLEEP2_MON_CNT;
}
}
}
//启动休眠1的定时
#define SLEEP_MON_CNT 6000*(paraMem.sleep_min_disable&0x7FFF) //60*100个10ms=1min
uint32_t sleep_Moni_Count;
void SLEEP_TIM_Moni(void)
{
//当没触发休眠
if(sleep_flag == 0)
{
if((paraMem.sleep_min_disable & 0x8000) == 0) //0代表启用休眠
{
sleep_Moni_Count--;
if(sleep_Moni_Count == 0)
{
sleep_Moni_Count = SLEEP_MON_CNT;
sleep_flag = 1;
#if LTE_Conn
pre_sleep_flag = 2;
pre_sleep_waitCnt = 0;
sleepOn_time=timecount;
#endif
}
}
}
}
//启动休眠2的定时
#define SLEEP2_MON_CNT 6000*(paraMem.sleep2_min_disable&0x7FFF) //60*100个10ms=1min
uint32_t sleep2_Moni_Count;
void SLEEP2_TIM_Moni(void)
{
//当没触发休眠
if(sleep_flag == 0)
{
if((paraMem.sleep2_min_disable & 0x8000) == 0) //0代表启用休眠
{
//满足休眠电压,无均衡,无电流
if((cellVoltageMin <= sleep2Vol) && ((bmsMem.balanceStatus & 0x0001) == 0) && (bmsMem.packCurrent > (-200)) && (bmsMem.packCurrent < 200))
{
//无除过压以外的保护
if(((bmsMem.bStatus1 & 0x067e) == 0) && ((bmsMem.bStatus2 & 0x00ff) == 0) && ((bmsMem.bStatus3 & 0x0008) == 0) && ((bmsMem.temperaStatus & 0x0f7f) == 0))
{
sleep2_Moni_Count--;
if(sleep2_Moni_Count == 0)
{
sleep2_Moni_Count = SLEEP2_MON_CNT;
sleep_flag = 1;
#if LTE_Conn
pre_sleep_flag = 2;
pre_sleep_waitCnt = 0;
sleepOn_time=timecount;
#endif
}
}
}
}
}
}
//欠压强制复位的定时
#define UVOff_MON_CNT 30000 //5*60*100个10ms=5分钟
uint16_t uvoff_Moni_Count;
void UVOff_TIM_Moni(void)
{
//屏幕手动关欠压启动了
if((bmsMem.balanceStatus & 0x20) != 0)
{
uvoff_Moni_Count--;
if(uvoff_Moni_Count == 0)
{
uvoff_Moni_Count = UVOff_MON_CNT;
bmsMem.balanceStatus &= 0xffdf;
}
}
else
{
uvoff_Moni_Count = UVOff_MON_CNT;
}
}
//校准满充容量的倒计时(中间开关机,会关闭。但这是重要功能,需在晶振故障时仍有计时)
#define FCCCALI_MON_CNT 6000*(paraMem.cali_min_disable&0x7FFF) //60*100个10ms=1min
uint32_t fcc_Cali_Moni_Count;
void FCCCali_TIM_Moni(void)
{
//倒计时12h
if(fcc_CaliStartFlag == 1)
{
fcc_Cali_Moni_Count--;
if(fcc_Cali_Moni_Count == 0)
{
fcc_Cali_Moni_Count = FCCCALI_MON_CNT;
fcc_CaliStartFlag = 0;
}
}
else
{
fcc_Cali_Moni_Count = FCCCALI_MON_CNT;
}
}
//更新软件版本
void Refresh_FirmwareVersion(void)
{
if(VersionMem.Software[2] < 0xA0)
{
sprintf(FirmwareVersion, "%u.%u.%u.%u", VersionMem.Software[0], VersionMem.Software[1], VersionMem.Software[2], VersionMem.Software[3]);
}
else
{
sprintf(FirmwareVersion, "%u.%u.%02X.%u",VersionMem.Software[0], VersionMem.Software[1],VersionMem.Software[2], VersionMem.Software[3]);
}
}
//更新硬件版本
void Refresh_HardwareVersion(void)
{
if(VersionMem.Hardware[2] < 'A')
{
sprintf(HardwareVersion, "%u.%u.%u", VersionMem.Hardware[0], VersionMem.Hardware[1], VersionMem.Hardware[2]); //最后1位是数字
}
else
{
sprintf(HardwareVersion, "%u.%u.%c", VersionMem.Hardware[0], VersionMem.Hardware[1], VersionMem.Hardware[2]); //最后1位是字符
}
}
//更新屏幕版本
void Refresh_ScreenVersion(void)
{
if(VersionMem.Screen == 0)
{
memset(ScreenVersion, 0, 6); //清空
}
else
{
uint8_t data = VersionMem.Screen;
uint8_t list1=0,list2=0;
//最高两位_00对应02801对应03510对应04211对应070
if((data>>6 == 0x00) && (data >= 0x04))
{
list1 = 28;
list2 = (data & 0x3f) +1-4;
}
else if(data>>6 == 0x01)
{
list1 = 35;
list2 = (data & 0x3f) +1;
}
else if(data>>6 == 0x02)
{
list1 = 43;
list2 = (data & 0x3f) +1;
}
else if(data>>6 == 0x03)
{
list1 = 70;
list2 = (data & 0x3f) +1;
}
if(list2 < 10)
{
sprintf(ScreenVersion, "0%u0%u", list1, list2);
}
else
{
sprintf(ScreenVersion, "0%u%u", list1, list2);
}
}
}
//更新BMS_SN
void Refresh_BMS_SN(void)
{
uint8_t i;
//固定9位数字
for(i=0;i<9;i++)
{
sprintf(&BMS_SN[i], "%c", VersionMem.BMS_SN[i]);
}
}
//更新PACK_SN
void Refresh_PACK_SN(void)
{
uint8_t i;
//最大共15字节,不足填充0xff
for(i=0;i<15;i++)
{
if(VersionMem.PACK_SN[i] == 0xff) break;
sprintf(&PACK_SN[i], "%c", VersionMem.PACK_SN[i]);
}
}
#if LTE_Conn
//默认值
#define DOMAIN domain_TianChu
#define PORT 1883
#define USERNAME username
#define PASSWORD password
char domain_TianChu[] = "tcp://mqtt.ricnsmart.com"; //天储平台
char username[] = "device";
char password[] = "E6Mk2Rj6uhFU4Zgy3kWCbghvaDYka8Dz";
//char clientId[] = "jdg0bcu4RMG!rmt0ubv"; //任意长度,所以改为和SN号一致
//4G通信凭证跟随SN号变动,更新到Flash
void LTE_4G_Domain_ChangeSN(void)
{
uint8_t i;
//清空原值
memset(&VersionMem.ClientID[0], 0, 23);
//改动ClientID=SN号
for(i=0;i<9;i++)
{
VersionMem.ClientID[i] = VersionMem.BMS_SN[i];
}
//计算CRC校验值
static uint8_t temp[HOSTMEM_LEN];
memcpy(temp, &VersionMem.host[0], HOSTMEM_LEN);
VersionMem.hostAll_crc = CRC8_Cal(&temp[0], 153);
//更新Flash
if((MEMORY_UpdateFlash(FLASH_DATA_A_BASE) == 0) && (MEMORY_UpdateFlash(FLASH_DATA_B_BASE) == 0))
{
staPack.bits.flashUpdate = 0;
}
else
{
staPack.bits.flashUpdate = 1;
}
bmsMem.packStatus = staPack.byte;
}
//4G通信凭证开机初始化
uint8_t Host_Def_Flag; //用默认值填充Host的标志
uint8_t Port_Def_Flag; //用默认值填充Port的标志
uint8_t UserName_Def_Flag; //用默认值填充UserName的标志
uint8_t Password_Def_Flag; //用默认值填充Password的标志
uint8_t ClientID_Def_Flag; //用SN号填充ClientID的标志
void LTE_4G_Domain_Init(void)
{
uint8_t i;
//若发现Host的内容为空,将默认值填充
Host_Def_Flag = 1;
for(i=0;i<40;i++)
{
if(VersionMem.host[i] != 0x00)
{
Host_Def_Flag = 0;
break;
}
}
if(Host_Def_Flag == 1)
{
uint8_t *data = (uint8_t *)DOMAIN;
i = 0;
while(data[i] != (uint8_t)'\0')
{
VersionMem.host[i] = data[i];
i++;
}
}
//若发现Port的内容为空,将默认值填充
if(VersionMem.port == 0x0000)
{
Port_Def_Flag = 1;
VersionMem.port = PORT;
}
//若发现UserName的内容为空,将默认值填充
UserName_Def_Flag = 1;
for(i=0;i<40;i++)
{
if(VersionMem.UserName[i] != 0x00)
{
UserName_Def_Flag = 0;
break;
}
}
if(UserName_Def_Flag == 1)
{
uint8_t *data = (uint8_t *)USERNAME;
i = 0;
while(data[i] != (uint8_t)'\0')
{
VersionMem.UserName[i] = data[i];
i++;
}
}
//若发现Password的内容为空,将默认值填充
Password_Def_Flag = 1;
for(i=0;i<40;i++)
{
if(VersionMem.PassWord[i] != 0x00)
{
Password_Def_Flag = 0;
break;
}
}
if(Password_Def_Flag == 1)
{
uint8_t *data = (uint8_t *)PASSWORD;
i = 0;
while(data[i] != (uint8_t)'\0')
{
VersionMem.PassWord[i] = data[i];
i++;
}
}
//若发现ClientID的内容为空,直接填充SN号
ClientID_Def_Flag = 1;
for(i=0;i<23;i++)
{
if(VersionMem.ClientID[i] != 0x00)
{
ClientID_Def_Flag = 0;
break;
}
}
if(ClientID_Def_Flag == 1)
{
for(i=0;i<9;i++)
{
VersionMem.ClientID[i] = VersionMem.BMS_SN[i];
}
}
//任意一个改动,写入Flash
if((Host_Def_Flag == 1) || (Port_Def_Flag == 1) || (UserName_Def_Flag == 1) || (Password_Def_Flag == 1) || (ClientID_Def_Flag == 1))
{
//计算CRC校验值
static uint8_t temp[HOSTMEM_LEN];
memcpy(temp, &VersionMem.host[0], HOSTMEM_LEN);
VersionMem.hostAll_crc = CRC8_Cal(&temp[0], 153);
//更新Flash
if((MEMORY_UpdateFlash(FLASH_DATA_A_BASE) == 0) && (MEMORY_UpdateFlash(FLASH_DATA_B_BASE) == 0))
{
staPack.bits.flashUpdate = 0;
}
else
{
staPack.bits.flashUpdate = 1;
}
bmsMem.packStatus = staPack.byte;
}
}
#endif
//各种初始默认值
void uf_GLOBAL_Init(void)
{
uint8_t i;
uint8_t tmpRd[16]; //用于读SN号
//初始值,之后会刷新
bmsMem.ucCellNum = 16;
bmsMem.cycleCount = 0;
bmsMem.soh = 99;
//根据配置读取
fcc = 10000;
bmsMem.ncc = 10000;
bmsMem.rcc = 5000;
bmsMem.soc = 50;
//bmsMem.E2uiDsgEndVol = 2500;//4.21修改
//for(i=0;i<10;i++)
//{
// bmsMem.E2uiVOC[i] = bmsMem.E2uiDsgEndVol+100*(i+1);
//}
bmsMem.temperaStatus = 0;
bmsMem.balanceStatus = 0;
bmsMem.packStatus = 0;
bmsMem.bStatus1 = 0;
bmsMem.bStatus2 = 0;
bmsMem.bStatus3 = 0;
//校准数据初始化
cali.current = 100000; //用于计算增益参数
cali.cmdZero = 0;
cali.cmdGain = 0;
cali.flagWrZeroToEE = 0;
cali.flagWrGainToEE = 0;
cali.cadcZero = 0;
cali.cadcGain = 0;
cali.cadcZero = EEPROM_CALI_RdZero();
delay_ms(20);
cali.cadcGain = EEPROM_CALI_RdGain();
delay_ms(20);
bmsMem.cadcZero = cali.cadcZero;
bmsMem.cadcGain = cali.cadcGain;
//[宁化]参数更新同步
alarm_occ_old = paraMem.alarm_occ;
alarm_ocd1_old = paraMem.alarm_ocd1;
alarm_puv_old = paraMem.alarm_puv;
#if Addr_SetAuto
//延时2s进行485通信
assignAddr_relay = 2;
#endif
//上位机求取数据的地址,只对主机有效默认是自身1
ConfigData_Index = 1;
//屏幕显示数据地址,每块BMS的屏幕都显示
scr_RdData_Index = bmsMem.E2_485Addr;
//开机初始化,屏幕记录要读数据并显示出来
scr_RdRecord_Flg = 1;
//该程序的软件版本号
VersionMem.Software[0] = 4; //大版本,有硬件不兼容项才变
VersionMem.Software[1] = 0; //客户对应出货单号
VersionMem.Software[2] = 0; //程序更新次数 A表示4G B表示蓝牙 C表示WIFI
VersionMem.Software[3] = 0; //程序后续小修改
//软件版本以当前程序的为准
EEPROM_RdMulByte(EE_Software,tmpRd);
if((tmpRd[0] != VersionMem.Software[0]) || (tmpRd[1] != VersionMem.Software[1]) || (tmpRd[2] != VersionMem.Software[2]) || (tmpRd[3] != VersionMem.Software[3])) //软件不是当前值,就进行写入
{
EEPROM_WrMulByte(EE_Software,VersionMem.Software);
delay_ms(5);
}
//填充FirmwareVersion
Refresh_FirmwareVersion();
//该程序的硬件版本号
VersionMem.Hardware[0] = 1;
VersionMem.Hardware[1] = 0;
VersionMem.Hardware[2] = 0;
//硬件版本号以EEPROM里面的为准,上面这个只做参考
EEPROM_RdMulByte(EE_Hardware,tmpRd);
if(tmpRd[0] == 0xff) //硬件的第1位不存在
{
EEPROM_WrMulByte(EE_Hardware,VersionMem.Hardware);
delay_ms(5);
}
else
{
VersionMem.Hardware[0] = tmpRd[0];
VersionMem.Hardware[1] = tmpRd[1];
VersionMem.Hardware[2] = tmpRd[2];
}
//填充HardwareVersion
Refresh_HardwareVersion();
//显示屏幕版本号
//最高两位表示型号,01对应03510对应04311对应070
//剩下6位表示序号Index,范围0~63,显示为1~64
EEPROM_RdMulByte(EE_Screen,tmpRd);
if(tmpRd[0] != 0xff)
{
VersionMem.Screen = tmpRd[0];
}
else
{
VersionMem.Screen = 0x00+37; //对应02834,也即2.8寸陶晶驰版
}
//填充ScreenVersion
Refresh_ScreenVersion();
//显示BMS_SN号
EEPROM_RdMulByte(EE_BMS_SN,tmpRd);
VersionMem.BMS_crc8 = CRC8_Cal(&tmpRd[0],9);
if(tmpRd[9] == VersionMem.BMS_crc8) //crc校验正确
{
for(i=0;i<9;i++)
{
VersionMem.BMS_SN[i] = tmpRd[i];
}
//填充BMS_SN
Refresh_BMS_SN();
}
else
{
//清零
memset(VersionMem.BMS_SN, 0, 9);
VersionMem.BMS_crc8 = 0;
memset(BMS_SN, 0, 9);
}
//显示PACK_SN号
EEPROM_RdMulByte(EE_PACK_SN,tmpRd);
VersionMem.PACK_crc8 = CRC8_Cal(&tmpRd[0],15);
if(tmpRd[15] == VersionMem.PACK_crc8) //crc校验正确
{
//最大共15字节,不足填充0xff
for(i=0;i<15;i++)
{
VersionMem.PACK_SN[i] = tmpRd[i];
}
//填充PACK_SN
Refresh_PACK_SN();
}
else
{
//清零
memset(VersionMem.PACK_SN, 0, 15);
VersionMem.PACK_crc8 = 0;
memset(PACK_SN, 0, 15);
}
#if LTE_Conn
//显示4G凭证
LTE_4G_Domain_Init();
#endif
}