#include "CanBms.h" namespace ccu { namespace { // 大端 u16 inline uint16_t rdU16BE(const uint8_t* p) { return static_cast((static_cast(p[0]) << 8) | p[1]); } // 大端 16 位有符号整数(补码) inline int16_t rdS16BE(const uint8_t* p) { return static_cast(rdU16BE(p)); } // 有符号 8 位(温度) inline int8_t rdS8(uint8_t b) { return static_cast(b); } // 设置功能码已收位 inline void markRx(BcuNodeStatus& n, uint8_t func) { n.rxMask |= (1u << (func & 31)); } } // namespace //============================================================================ // 附录1:0x10XX0010 告警位定义(04KT38电池BCU-MBMS通信(CAN)定义.docx) //============================================================================ const BcuAlarmDesc kBcuAlarmTable[] = { // byte0:2 级告警 {0, 0, "BCU绝缘2级"}, {0, 1, "BCU最高温度过高2级"}, {0, 2, "BCU最低温度过低2级"}, {0, 3, "BCU单体电压不均衡2级"}, {0, 4, "BCU单体温度不均衡2级"}, {0, 5, "BCU放电过流2级"}, {0, 6, "BCU单体电压过高2级"}, {0, 7, "BCU单体电压过低2级"}, // byte1 {1, 0, "BCU总电压过高2级"}, {1, 1, "BCU总电压过低2级"}, {1, 2, "BCU烟雾报警1级"}, {1, 3, "烟雾报警器不在线"}, {1, 4, "烟雾报警器上报自身故障"}, {1, 5, "BCU单包绝缘3级"}, {1, 6, "BCU最高温度过高3级"}, {1, 7, "BCU最低温度过低3级"}, // byte2 {2, 0, "单体电压不均衡3级"}, {2, 1, "单体温度不均衡3级"}, {2, 2, "单包放电过流3级"}, {2, 3, "BCU单体电压过高3级"}, {2, 4, "BCU单体电压过低3级"}, {2, 5, "BCU总电压过高3级"}, {2, 6, "BCU总电压过低3级"}, {2, 7, "BCU单体温度传感器检测故障"}, // byte3 {3, 0, "BCU正极继电器失效"}, {3, 1, "BCU负极继电器失效"}, {3, 3, "单包BMS内部通讯故障"}, {3, 4, "BCU电流传感器故障"}, {3, 5, "BCU电压传感器故障"}, {3, 7, "BCU高压回路连接异常"}, // byte4 {4, 1, "烟雾报警2级"}, {4, 4, "烟雾报警3级"}, {4, 6, "烟雾报警4级"}, {4, 7, "回馈电流过大2级"}, // byte5 {5, 0, "回馈电流过大3级"}, {5, 1, "充电电流过大3级"}, {0xFF, 0, nullptr}, // 表尾 }; int bcuAlarmTexts(const BcuNodeStatus& node, const char* texts[], int cap) { if (!texts || cap <= 0) return 0; int count = 0; for (int i = 0; kBcuAlarmTable[i].text; ++i) { const BcuAlarmDesc& d = kBcuAlarmTable[i]; if (d.byteIdx < sizeof(node.alarmBits) && (node.alarmBits[d.byteIdx] >> d.bitIdx) & 0x01) { if (count >= cap) break; texts[count++] = d.text; } } return count; } bool decodeCanBcuFrame(uint32_t canId, const uint8_t* data, int dlc, uint8_t& nodeAddr, BcuNodeStatus& node) { if (!data || dlc <= 0) return false; uint8_t addr = bcuAddrOf(canId); if (!addr || !isCanBcuId(canId)) return false; nodeAddr = addr; // 缩放用除法(而非乘 0.1/0.001),保证结果与十进制字面量严格一致。 switch (bcuFuncOf(canId)) { case BCU_FUNC_BASE: // 0x10XX0000:电压 / 电流 / SOC / 告警码 / 自检 if (dlc >= 8) { node.totalVoltage = rdU16BE(data + 0) / 10.0; // 0.1V node.current = rdS16BE(data + 2) / 10.0 - 600.0; // 0.1A,偏移-600A node.soc = rdU16BE(data + 4) / 10.0; // 0.1% node.alarmCode = data[6]; node.selfCheck = data[7]; markRx(node, BCU_FUNC_BASE); } break; case BCU_FUNC_CELL_V: // 0x10XX0001:最高/最低/平均单体电压 if (dlc >= 2) node.maxCellVoltage = rdU16BE(data + 0) / 1000.0; // 1mV if (dlc >= 3) node.maxCellVoltageNo = data[2]; if (dlc >= 5) node.minCellVoltage = rdU16BE(data + 3) / 1000.0; // 1mV if (dlc >= 6) node.minCellVoltageNo = data[5]; if (dlc >= 8) node.avgCellVoltage = rdU16BE(data + 6) / 1000.0; // 1mV markRx(node, BCU_FUNC_CELL_V); break; case BCU_FUNC_CELL_T: // 0x10XX0002:最高/最低/平均单体温度 if (dlc >= 1) node.maxCellTemp = rdS8(data[0]) - 40.0; // 偏移-40℃ if (dlc >= 2) node.maxCellTempNo = data[1]; if (dlc >= 3) node.minCellTemp = rdS8(data[2]) - 40.0; // 偏移-40℃ if (dlc >= 4) node.minCellTempNo = data[3]; if (dlc >= 5) node.avgCellTemp = rdS8(data[4]) - 40.0; // 偏移-40℃(文档写偏移0, // 但实测固件与最高/最低一致均带-40偏移,见0x10020002: // 原始68/69减40后为28/29℃,落在最低28~最高30区间内) markRx(node, BCU_FUNC_CELL_T); break; case BCU_FUNC_RELAY: // 0x10XX0003:正/负极继电器状态 if (dlc >= 1) node.posRelay = data[0]; if (dlc >= 2) node.negRelay = data[1]; markRx(node, BCU_FUNC_RELAY); break; case BCU_FUNC_ISU: // 0x10XX0006:绝缘阻值 / 端口电压 if (dlc >= 2) node.posInsulationKohm = rdU16BE(data + 0); // 1kΩ if (dlc >= 4) node.negInsulationKohm = rdU16BE(data + 2); // 1kΩ if (dlc >= 6) node.portVoltage = rdU16BE(data + 4) / 10.0; // 0.1V if (dlc >= 8) node.posRelayOuterVoltage = rdU16BE(data + 6) / 10.0; // 0.1V markRx(node, BCU_FUNC_ISU); break; case BCU_FUNC_ALARM: // 0x10XX0010:告警位(附录1) for (int i = 0; i < 6 && i < dlc; ++i) node.alarmBits[i] = data[i]; if (dlc >= 7) node.alarmExtraByte6 = data[6]; if (dlc >= 8) node.alarmExtraByte7 = data[7]; markRx(node, BCU_FUNC_ALARM); break; default: // Reserve(0x10XX0004/0005 等)不解析 return false; } return true; } } // namespace ccu