3、CAN应用层协议设计:ID分配策略、数据长度定义、传输周期规划、心跳机制设计

各位同行,今天我们来聊聊CAN应用层协议设计。说实话,这部分是PCS和BMS通信的「灵魂」。物理层搞定了,报文能发了,但怎么发、谁先发、发什么内容,全靠应用层协议来定。我见过不少项目,硬件选型没问题,最后死在了协议混乱上——ID冲突、数据长度对不上、周期乱跳,调试起来简直想砸示波器。

好,我们一个一个拆开讲。

3.1 ID分配策略:别让报文「撞车」

CAN总线是广播式的,所有节点都能收到报文。那怎么区分谁是谁?靠ID。ID不仅是标识,还决定了优先级——ID越小,优先级越高。

我个人习惯,把ID分成几个功能区段。比如:

  • 0x000 - 0x0FF:紧急报文,比如故障、保护动作。优先级最高。
  • 0x100 - 0x2FF:控制报文,比如PCS的功率指令、BMS的充放电允许。
  • 0x300 - 0x4FF:状态报文,比如SOC、SOH、电压、电流。
  • 0x500 - 0x6FF:参数配置报文,比如写参数、读参数。
  • 0x700 - 0x7FF:诊断与心跳报文。

你想想看,如果故障报文和状态报文抢总线,谁先走?当然是故障。所以故障报文ID要最小。

我的经验:ID分配一定要留余量。别把0x000到0x7FF全排满,留20%的空闲ID,方便后期扩展。我曾经在一个项目里,ID排得太紧,后来加一个新功能,发现没有可用ID了,只能重构协议——那叫一个痛苦。

另外,标准CAN是11位ID,扩展CAN是29位。储能系统里,我建议用标准CAN就够了。29位ID虽然灵活,但报文头更长,有效数据率反而低。除非你的节点超过2048个,否则别折腾扩展帧。

3.2 数据长度定义:8字节的「黄金分割」

CAN一帧最多带8字节数据。这是硬件限制,改不了。但怎么用这8个字节,大有讲究。

我建议,每个报文的数据长度固定,不要变长。比如:

  • 状态报文:固定8字节。前2字节放SOC(精度0.1%),中间2字节放SOH,后4字节放总电压。
  • 控制报文:固定8字节。前4字节放功率指令(有符号整数,单位W),后4字节保留。
  • 故障报文:固定8字节。前2字节放故障码,后6字节放故障时间戳。

为什么要固定长度?因为解析简单。接收方不用猜「这帧到底带了几个字节」,直接按模板解析就行。

避坑指南:我曾经遇到一个供应商,他们的BMS报文长度不固定,有时候发4字节,有时候发8字节。我们的PCS解析逻辑写得很复杂,还要判断DLC(数据长度码)。后来有一次,BMS发了5字节,我们的解析器直接崩了。从那以后,我要求所有报文必须固定长度,哪怕后面字节全是0填充。

数据长度定义还有一个细节:字节序。是Intel格式(小端)还是Motorola格式(大端)?我建议统一用小端。因为大部分MCU都是小端处理器,省去转换开销。协议文档里一定要写清楚,不然两边对不上。

3.3 传输周期规划:别让总线「堵车」

CAN总线的带宽是有限的。500kbps的波特率,理论上每秒能传约5000帧标准帧(每帧约100位)。但实际应用中,总线负载率最好控制在30%以下,超过40%就容易丢帧。

所以,传输周期要规划好。我的做法是:

  • 紧急报文:事件触发,不固定周期。比如故障发生瞬间,立刻发,优先级最高。
  • 控制报文:100ms周期。PCS的功率指令、BMS的充放电允许,这些实时性要求高,但不需要太快。
  • 状态报文:500ms周期。SOC、电压、电流这些,变化不快,500ms刷新一次足够了。
  • 参数报文:1s周期或按需。配置参数一般不变,没必要频繁发。
  • 心跳报文:1s周期。后面细讲。

你想想看,如果所有报文都是10ms周期,总线瞬间就满了。我见过一个项目,他们把状态报文设成10ms周期,结果总线负载率飙到60%,控制报文经常发不出去,导致PCS响应延迟——说白了,就是自己把自己玩死了。

注意:周期规划要留余量。假设你算出来总线负载率是25%,实际跑起来可能到35%。因为报文有抖动,还有重传。我一般要求设计负载率不超过20%,留10%给异常情况。

另外,周期要错开。比如状态报文在t=0ms发,控制报文在t=50ms发,心跳在t=100ms发。别让所有报文都在同一时刻挤总线。这个可以用定时器偏移来实现。

3.4 心跳机制设计:知道对方「还活着」

心跳机制,说白了就是「你还活着吗?」。PCS和BMS之间,必须互相知道对方的状态。如果BMS挂了,PCS还在傻傻地充电,那就出大事了。

我建议,心跳报文设计如下:

  • 发送周期:1秒。太短浪费总线,太长反应迟钝。
  • 数据内容:2字节。一个递增的计数器(0-65535循环),一个状态标志位(正常/告警/故障)。
  • 超时判断:连续3个周期没收到心跳,判定对方离线。也就是3秒超时。

为什么用递增计数器?因为可以检测「丢帧」。如果收到的心跳计数器是1、2、4,说明第3帧丢了。虽然丢一帧不影响,但频繁丢帧说明总线有问题,需要告警。

我的经验:心跳报文ID要固定,且优先级中等。我一般用0x700作为PCS的心跳,0x701作为BMS的心跳。这样调试时一眼就能看出来。

还有一个细节:心跳超时后的动作。PCS检测到BMS心跳超时,应该怎么做?我建议:

  • 第一步:停止充放电,进入待机状态。
  • 第二步:记录故障日志,上报监控系统。
  • 第三步:尝试重连,比如每隔10秒发一次「你是谁」的查询报文。
  • 第四步:如果连续30秒没恢复,直接停机并报警。

嗯,这里要注意:心跳超时不能立刻停机。因为总线偶尔会有干扰,丢一两帧很正常。3秒超时是比较合理的折中。

3.5 知识体系总览

下面这张图,是我画的本章节知识结构。你可以看到,CAN应用层协议设计,核心就是这四个维度:ID分配、数据长度、传输周期、心跳机制。它们互相影响,缺一不可。

CAN应用层协议设计 ID分配策略 优先级 + 功能区段 数据长度定义 固定8字节 + 字节序 传输周期规划 负载率 + 错开调度 心跳机制设计 超时判断 + 动作 核心设计原则 • 预留20%空闲ID,方便扩展 • 固定数据长度,简化解析逻辑 • 总线负载率控制在20%以下 • 心跳超时3秒,先待机再停机

好了,这一章的内容就这些。ID分配、数据长度、传输周期、心跳机制,这四个点你吃透了,CAN应用层协议设计的基本功就算打牢了。下一章我们聊具体的报文格式和编码方式,到时候拿实际项目里的报文来拆解。

最后说一句:协议设计没有绝对的对错,但一定要「写清楚、定下来、不轻易改」。我见过太多项目,协议文档写得不详细,开发过程中两边各改各的,最后联调时发现对不上。所以,协议文档一定要版本管理,每次修改都要通知到所有相关方。

公众号:蓝海资料掘金营,微信deep3321