3. 通信协议设计:物理层与数据链路层、应用层协议定义、报文格式与CRC校验

好,咱们进入通信协议设计这一节。说实话,在电池化成设备这个领域,通信协议搞不好,后面所有控制策略都是空中楼阁。我见过太多项目,算法写得天花乱坠,结果一跑起来,数据传丢了、时序对不上,整个产线直接瘫痪。所以这块,咱们得扎扎实实讲清楚。

3.1 物理层与数据链路层选型

物理层选什么?我个人习惯用RS-485。为什么?化成现场环境太恶劣了——电机启停、大功率充放电、变频器干扰,到处都是电磁噪声。RS-485是差分传输,抗共模干扰能力强,传输距离能到1200米,足够覆盖一个化成车间。

数据链路层呢?我建议用Modbus RTU。这玩意儿虽然老,但胜在稳定、简单。你想想看,一个化成柜少说几十个通道,多则上百个,每个通道都要实时上报电压、电流、温度。Modbus RTU的主从架构刚好合适——BMS做主站,化成设备做从站。

关键参数建议:

  • 波特率:115200 bps(别用9600,太慢了,一个轮询周期跑不完)
  • 数据位:8位
  • 停止位:1位
  • 校验位:无(我们把校验交给应用层的CRC)

我的经验:波特率别一味求快。我曾经在一个项目里试过460800 bps,结果线缆稍微长一点就丢包。后来老老实实降到115200,稳如老狗。记住,工业现场不是实验室,稳定压倒一切。

3.2 应用层协议定义

应用层协议,说白了就是咱们自己定义的一套“行话”。Modbus RTU只负责把数据包从A点搬到B点,但搬的是什么内容,得咱们自己定。

我习惯把报文分成三类:

  • 读命令:BMS读取化成设备的实时数据
  • 写命令:BMS下发控制参数(电流、电压、截止条件)
  • 应答命令:化成设备回复执行结果

举个例子,BMS要读第5通道的电压,报文长这样:

从站地址:0x01
功能码:0x03(读保持寄存器)
起始地址:0x0005(第5通道电压寄存器)
寄存器数量:0x0002(电压占2个寄存器,32位浮点数)
CRC校验:0xXXXX

化成设备回复:

从站地址:0x01
功能码:0x03
字节数:0x04
数据:0x4120 0x0000(浮点数10.0,表示10.0V)
CRC校验:0xXXXX

注意:浮点数传输一定要约定字节序!我遇到过合作方用大端,我们用小端,结果读出来的电压值完全不对。后来统一规定:所有多字节数据采用小端模式(低字节在前)。

3.3 报文格式详解

报文格式这块,我把它拆成四个部分:帧头、地址域、数据域、校验域。咱们一个一个说。

字段 长度(字节) 说明
帧头 2 固定为0xAA 0x55,用于帧同步
地址域 1 从站地址,范围1-247
数据域 N 功能码+数据内容,长度可变
校验域 2 CRC-16/MODBUS校验

为什么要加帧头?嗯,这里要注意。Modbus RTU本身是靠帧间隔来区分报文的,但化成设备里多个通道同时上报数据时,帧间隔很容易被干扰。加个固定帧头,接收端就能快速定位报文起始位置,容错性大大提升。

3.4 CRC校验实现

CRC校验,说白了就是给报文算个“指纹”。发送方算一遍,接收方算一遍,对得上就说明数据没被篡改。

我推荐用CRC-16/MODBUS算法,查表法实现,速度快、占用资源少。嵌入式MCU上跑起来毫无压力。

直接上代码:

// CRC-16/MODBUS 查表法实现
uint16_t crc16_modbus(uint8_t *data, uint16_t len)
{
    uint16_t crc = 0xFFFF;
    uint16_t i, j;
    
    for (i = 0; i < len; i++)
    {
        crc ^= data[i];
        for (j = 0; j < 8; j++)
        {
            if (crc & 0x0001)
                crc = (crc >> 1) ^ 0xA001;
            else
                crc >>= 1;
        }
    }
    return crc;
}

避坑指南:我曾经在一个项目里发现,CRC算出来总是对不上。查了半天,原来是发送时把CRC字节的顺序搞反了。记住:CRC低字节在前,高字节在后。这是Modbus协议的规定,别搞混了。

3.5 实际应用中的坑与对策

讲几个我踩过的坑,你们以后遇到了能少走弯路。

第一个坑:广播风暴。化成设备上百个通道同时回复数据,总线瞬间被占满。我的解决办法是——分时回复。BMS下发查询命令时,指定一个时间偏移量,每个通道按偏移量延时回复,避免冲突。

第二个坑:超时重传。工业现场偶尔丢包很正常。我习惯设一个超时定时器,100ms没收到回复就重发。连续重发3次都失败,就上报通信故障。别无限重发,否则总线会被堵死。

第三个坑:数据粘包。有时候两个报文挨得太近,接收端分不清边界。我的做法是:接收完一帧后,强制等待3.5个字符时间再处理下一帧。这是Modbus RTU的标准做法,能有效避免粘包。

总结一下:通信协议设计,说白了就是定规矩。物理层选RS-485,数据链路层用Modbus RTU,应用层自己定义报文格式,CRC校验保平安。规矩定好了,后面的事情就顺了。

下一节咱们讲控制策略的具体实现,到时候会用到今天讲的这些通信协议。嗯,先把基础打牢,后面才能盖高楼。