锂电池改用 BCU-MBMS CAN 协议按节点解析 + bcu_node 解析数据落库 + 板卡对时脚本

- pCCU/CanBms:按《04KT38电池BCU-MBMS通信(CAN)定义》重写解码,
  BmsStatus 单状态模型改为 BcuNodeStatus 多节点模型(0x10XX00YY,
  节点地址 01~36h),支持 0x0000 电压/电流/SOC/告警码、0x0001 单体
  电压、0x0002 单体温度、0x0003 继电器、0x0006 绝缘/端口电压、
  0x0010 告警位(附录1 中文码表)六类报文
- 平均单体温度偏移修正:协议文档 BYTE5 写"偏移0"有误,实测固件与
  最高/最低一致均带 -40℃ 偏移(实车 0x10020002 原始 68/69 减 40 后
  为 28/29℃,落在最低28~最高30区间内,按文档直读则超出物理范围)
- pCCU/DbStore:新增 bcu_node 解析数据表,锂电池 BMS 报文每帧落一行
  节点合成状态(原 comm_log 仅原始帧,BMS/CAN 帧此前不落 pCCU 库),
  buildReport 增加 BMS 记录数
- 快照/网页:BCU 按节点分组展示(告警位解析中文含义、数据 age),
  PM 状态报文锂电池/应急电池卡片
- pPowerManger:新增 iport 本地输入端口配置(UDP bind 延迟到
  OnStartUp 读取配置后执行,保证 iport/ccuhost/ccuport 生效),
  各 mission 文件补充注释
- test:CAN BCU 解码用例按新协议/新偏移更新(实车抓包 + 文档示例值),
  133 项全部通过
- docs:删除旧 BMS 协议(xlsx/20230324docx),归档 04KT38 BCU-MBMS
  CAN 定义、配电控制器通信协议 20260824、配电系统 CAN 通讯协议
- scripts:新增 sync-board-time.sh,223 板卡(RK3588)时间校正
  (本机为基准 + RTT 折半补偿对时,尽力写 RTC,支持 status/--time)
This commit is contained in:
zjk
2026-09-01 12:58:33 +08:00
parent 88bed850ee
commit 5b8f0b1421
27 changed files with 894 additions and 170 deletions
+124 -41
View File
@@ -6,58 +6,141 @@
namespace ccu {
//============================================================================
// CAN BMS 协议(锂电池 BMS 报文,经 USBCAN-8E-U/CANET 上 CAN 总线)
// 依据:docs/BMS_协议字段定义.xlsx
// 锂电池 BCU-MBMS CAN 协议(docs/04KT38电池BCU-MBMS通信(CAN)定义.docx)
//
// 数据来源:pCanBridge 透传的 MOOS 消息
// 变量名 CAN_0x%08X = CAN ID(扩展帧),m_sVal = 二进制数据域,
// m_dfVal2 = 原始帧信息字节。本模块只负责按报文 ID 解析数据域字段。
//
// 报文(均为扩展帧,字段大端,起始字节相对 8 字节数据域):
// 0x10010000 总电压 u16@0 ÷10 V
// SOC u16@4 ÷10 %
// 故障码 u8 @6 ×1
// 状态位 u8 @7 ×1
// 0x10010005 最大电流限制 u16@0 ÷10 A
// 总电流 s16@2 ÷10 A(补码,放电为正;xlsx 表标注
// 3 字节与其示例/实测冲突,按 2 字节实现,
// 详见 CanBms.cpp 注释)
// 最高单体电压 u16@4 ÷1000 V
// 0x10010006 总电压(校验) u16@4 ÷10 V
// 【节点地址】一个 MBMS 下最多挂 54 个 BCU,CAN ID 中间的 XX(01h~36h)
// 为 BCU 节点地址编号,其余位固定:
// 0x10XX00YY BCU 主动上报消息(YY=功能码 00/01/02/03/06/10)
// 0x10XX81FF MBMS 下发指令(继电器控制,pCCU 只收不发,不解析)
//
// 【上报报文】均为扩展帧,字段大端,BYTE1~8 对应数据域 byte0~7:
// 0x10XX0000 (200ms) 累加电压 u16@0 ÷10 V
// 回路电流 s16@2 ÷10-600 A(偏移 -600A,放电为正)
// SOC u16@4 ÷10 %
// 告警码 u8 @6(循环上报,协议未附码表,原样显示)
// 自检状态 u8 @7
// 0x10XX0001 (200ms) 最高单体电压 u16@0 mV,编号 u8@2
// 最低单体电压 u16@3 mV,编号 u8@5
// 单体平均电压 u16@6 mV
// 0x10XX0002 (1000ms) 最高单体温度 s8@0(偏移 -40℃),编号 u8@1
// 最低单体温度 s8@2(偏移 -40℃),编号 u8@3
// 单体平均温度 s8@4(偏移 -40℃,协议文档写"偏移0"
// 有误:实测原始值减 40 后才落在最低~最高区间内)
// 0x10XX0003 (200ms) 正极继电器状态 u8@0(1 闭合 / 0 断开)
// 负极继电器状态 u8@1(1 闭合 / 0 断开)
// 0x10XX0006 (200ms) 正极绝缘阻抗 u16@0 kΩ
// 负极绝缘阻抗 u16@2 kΩ
// 正负极端口电压 u16@4 ÷10 V
// 正极继电器外侧电压 u16@6 ÷10 V
// 0x10XX0010 (200ms) byte0~5 告警位(bit 定义见附录1,
// kBcuAlarmTable,可经 bcuAlarmTexts() 取告警描述)
// 0x10XX0004/0005 Reserve(调试预留,不解析)
//============================================================================
// BMS 报文 ID(扩展帧 CAN ID)
enum CanBmsId : uint32_t {
CAN_BMS_ID_STATUS = 0x10010000, // 总电压 / SOC / 故障码 / 状态位
CAN_BMS_ID_CURRENT = 0x10010005, // 最大电流限制 / 总电流 / 最高单体电压
CAN_BMS_ID_VOLT_CHK = 0x10010006, // 总电压(校验)
// BCU 节点地址范围:01h~36h(1~54)
static const int kBcuAddrMin = 0x01;
static const int kBcuAddrMax = 0x36;
static const int kBcuNodeCount = kBcuAddrMax - kBcuAddrMin + 1;
// BCU 上报报文功能码(CAN ID 低字节 YY)
enum BcuFunc : uint8_t {
BCU_FUNC_BASE = 0x00, // 0x10XX0000 电压 / 电流 / SOC / 告警码 / 自检
BCU_FUNC_CELL_V = 0x01, // 0x10XX0001 单体电压
BCU_FUNC_CELL_T = 0x02, // 0x10XX0002 单体温度
BCU_FUNC_RELAY = 0x03, // 0x10XX0003 继电器状态
BCU_FUNC_ISU = 0x06, // 0x10XX0006 绝缘阻值 / 端口电压
BCU_FUNC_ALARM = 0x10, // 0x10XX0010 告警位(附录1)
};
// BMS 最新状态(各报文字段按 ID 部分更新,最终合成完整快照)
struct BmsStatusValue {
double totalVoltage = 0; // 总电压 V(0x10010000)
double soc = 0; // SOC %(0x10010000)
int faultCode = 0; // 故障码(0x10010000 @6)
int statusBits = 0; // 状态位(0x10010000 @7)
double maxCurrentLimit = 0; // 最大电流限制 A(0x10010005)
double totalCurrent = 0; // 总电流 A,放电为正(0x10010005,s24 补码)
double maxCellVoltage = 0; // 最高单体电压 V(0x10010005)
double totalVoltageChk = 0; // 总电压校验 V(0x10010006)
double lastRxTime = 0; // 最近收到任一 BMS 报文的时刻(MOOSTime)
bool valid = false; // 是否收到过 BMS 报文
};
// 解析一帧 CAN 数据域;命中 BMS 报文 ID 返回 true 并把字段合并进 v
// (部分更新语义:每个 ID 只更新自己的字段)。
// data 为数据域首指针(有效长度 dlc 字节),data/dlc 非法或非 BMS ID 返回 false。
bool decodeCanBmsFrame(uint32_t canId, const uint8_t* data, int dlc, BmsStatusValue& v);
// 是否为 BMS 报文 ID
inline bool isCanBmsId(uint32_t canId) {
return canId == CAN_BMS_ID_STATUS || canId == CAN_BMS_ID_CURRENT ||
canId == CAN_BMS_ID_VOLT_CHK;
// 从 CAN ID 提取 BCU 节点地址(0x10XX00YY -> XX);非 BCU 报文格式返回 0
inline uint8_t bcuAddrOf(uint32_t canId) {
if ((canId & 0xFF00FF00u) != 0x10000000u) return 0;
uint8_t addr = static_cast<uint8_t>((canId >> 16) & 0xFF);
return (addr >= kBcuAddrMin && addr <= kBcuAddrMax) ? addr : 0;
}
// 从 CAN ID 提取功能码(0x10XX00YY -> YY)
inline uint8_t bcuFuncOf(uint32_t canId) {
return static_cast<uint8_t>(canId & 0xFF);
}
// 是否为 BCU 主动上报报文 ID(0x10XX00YY,YY 为已定义功能码)
inline bool isCanBcuId(uint32_t canId) {
uint8_t addr = bcuAddrOf(canId);
if (!addr) return false;
switch (bcuFuncOf(canId)) {
case BCU_FUNC_BASE:
case BCU_FUNC_CELL_V:
case BCU_FUNC_CELL_T:
case BCU_FUNC_RELAY:
case BCU_FUNC_ISU:
case BCU_FUNC_ALARM:
return true;
default:
return false;
}
}
// 单个 BCU 节点最新状态(各报文字段按功能码部分更新,最终合成完整快照)
struct BcuNodeStatus {
// 0x10XX0000 基本信息
double totalVoltage = 0; // 累加电压 V
double current = 0; // 回路电流 A(放电为正,偏移 -600A 已折算)
double soc = 0; // SOC %
int alarmCode = 0; // 告警码(原始值,协议未附码表)
int selfCheck = 0; // 自检状态(原始值)
// 0x10XX0001 单体电压
double maxCellVoltage = 0; // 最高单体电压 V
int maxCellVoltageNo = 0; // 最高单体电压编号
double minCellVoltage = 0; // 最低单体电压 V
int minCellVoltageNo = 0; // 最低单体电压编号
double avgCellVoltage = 0; // 单体平均电压 V
// 0x10XX0002 单体温度
double maxCellTemp = 0; // 最高单体温度 ℃
int maxCellTempNo = 0; // 最高单体温度编号
double minCellTemp = 0; // 最低单体温度 ℃
int minCellTempNo = 0; // 最低单体温度编号
double avgCellTemp = 0; // 单体平均温度 ℃
// 0x10XX0003 继电器(1 闭合 / 0 断开)
int posRelay = 0;
int negRelay = 0;
// 0x10XX0006 绝缘 / 端口电压
double posInsulationKohm = 0; // 正极绝缘阻抗 kΩ
double negInsulationKohm = 0; // 负极绝缘阻抗 kΩ
double portVoltage = 0; // 正负极端口电压 V
double posRelayOuterVoltage = 0; // 正极继电器外侧电压 V
// 0x10XX0010 告警位(附录1,byte0~5)
uint8_t alarmBits[6] = {0, 0, 0, 0, 0, 0};
// 0x10XX0010 附加字节(协议未详列,原样保留)
int alarmExtraByte6 = 0;
int alarmExtraByte7 = 0;
// 元数据
uint32_t rxMask = 0; // 收到过的功能码位图(bit = BcuFunc)
double lastRxTime = 0; // 最近收到该节点任一报文的时刻(MOOSTime)
bool valid = false; // 是否收到过该节点报文
};
// 解析一帧 CAN 数据域;命中 BCU 上报报文返回 true,nodeAddr 为节点地址(1~54),
// 字段按功能码部分更新合并进 node。data/dlc 非法或非 BCU 报文返回 false。
bool decodeCanBcuFrame(uint32_t canId, const uint8_t* data, int dlc,
uint8_t& nodeAddr, BcuNodeStatus& node);
// 附录1:0x10XX0010 告警位定义表(byteIdx/bitIdx -> 告警描述),表尾以 byteIdx=0xFF 结束
struct BcuAlarmDesc {
uint8_t byteIdx;
uint8_t bitIdx;
const char* text;
};
extern const BcuAlarmDesc kBcuAlarmTable[];
// 收集节点当前置位的告警描述;返回条数(不超过 cap,texts 填描述指针)
int bcuAlarmTexts(const BcuNodeStatus& node, const char* texts[], int cap);
} // namespace ccu
#endif // PCCU_CAN_BMS_H