/** ****************************************************************************** * @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 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对应028,01对应035,10对应042,11对应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对应035,10对应043,11对应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 }