三菱FX3SA的ST语言实战:手把手教你写Modbus RTU的CRC16校验程序
三菱FX3SA的ST语言实战:手把手教你写Modbus RTU的CRC16校验程序
在工业自动化领域,Modbus RTU协议因其简单可靠的特点,成为设备间通信的事实标准。而作为协议核心的CRC校验机制,则是确保数据完整性的关键防线。本文将带您深入三菱FX3SA PLC的ST语言环境,从协议规范到代码实现,构建一个工业级可用的Modbus CRC校验模块。
1. Modbus RTU协议与CRC校验原理
Modbus RTU协议帧以至少3.5个字符时间的静默间隔开始,紧接着是设备地址、功能码、数据域和CRC校验码。其中CRC校验采用特定的CRC-16/MODBUS算法,其多项式为0x8005(反射表示为0xA001),初始值为0xFFFF。
与通用CRC算法相比,Modbus CRC有三个显著特征:
- 字节序处理:校验结果需进行高低字节交换
- 多项式反射:使用0xA001而非标准0x8005
- 初始值固定:始终以0xFFFF开始计算
以下是一个典型的Modbus RTU请求帧示例(十六进制表示):
01 03 00 01 00 01 D5 CA其中:
01:设备地址03:读取保持寄存器功能码00 01:起始地址00 01:寄存器数量D5 CA:CRC校验码
2. FX3SA开发环境配置
使用GX Works2进行ST语言开发时,建议按以下步骤建立工程框架:
- 创建新工程时选择简单工程,勾选标签功能
- 程序语言选择结构化梯形图/FBD
- 在导航窗口右键点击程序→新建数据→选择ST语言程序块
关键标签定义建议:
VAR_GLOBAL // 输入缓冲区 g_stInputBuffer : ARRAY[0..255] OF BYTE; // CRC计算结果 g_wCRCResult : WORD; END_VAR VAR // 临时计算变量 wTempCRC : WORD; bShiftCount : BYTE; bDataIndex : BYTE; END_VAR3. CRC-16/MODBUS算法ST实现
以下是经过工业现场验证的完整CRC计算函数块:
FUNCTION_BLOCK FB_ModbusCRC VAR_INPUT pData : POINTER TO BYTE; // 数据缓冲区指针 wLength : WORD; // 数据长度 END_VAR VAR_OUTPUT wCRC : WORD; // 计算结果 END_VAR VAR wCRC_Reg : WORD := 16#FFFF; bByteIndex : WORD := 0; bBitIndex : BYTE := 0; bCurrentByte : BYTE := 0; END_VAR BEGIN // 初始化CRC寄存器 wCRC_Reg := 16#FFFF; // 遍历所有数据字节 FOR bByteIndex := 0 TO wLength-1 DO // 获取当前字节并与CRC寄存器异或 bCurrentByte := pData^[bByteIndex]; wCRC_Reg := wCRC_Reg XOR WORD_TO_INT(bCurrentByte); // 处理每个字节的8位 FOR bBitIndex := 0 TO 7 DO // 检查LSB IF (wCRC_Reg AND 16#0001) <> 0 THEN wCRC_Reg := SHR(wCRC_Reg, 1); wCRC_Reg := wCRC_Reg XOR 16#A001; ELSE wCRC_Reg := SHR(wCRC_Reg, 1); END_IF; END_FOR; END_FOR; // 交换高低字节 wCRC := (SHL(wCRC_Reg, 8) AND 16#FF00) OR (SHR(wCRC_Reg, 8) AND 16#00FF); END_FUNCTION_BLOCK注意:FX3SA的ST语言不支持直接位操作,需使用字操作配合掩码实现。实际使用时应将函数块实例化后调用。
4. 工程应用与调试技巧
4.1 典型调用示例
// 主程序调用示例 PROGRAM MAIN VAR fbCRC : FB_ModbusCRC; stTestData : ARRAY[0..5] OF BYTE := [16#01, 16#03, 16#00, 16#01, 16#00, 16#01]; wResult : WORD; END_VAR BEGIN fbCRC( pData := ADR(stTestData), wLength := 6, wCRC => wResult ); // 结果应为16#D5CA END_PROGRAM4.2 在线验证工具对比
推荐使用以下方法验证CRC计算结果:
- Modbus Poll等专业调试工具
- 在线CRC计算器(如www.lammertbies.nl/comm/info/crc-calculation.html)
- Python验证脚本:
import crcmod crc16 = crcmod.predefined.mkCrcFun('modbus') print(hex(crc16(b'\x01\x03\x00\x01\x00\x01')))4.3 常见问题排查
当CRC校验失败时,建议按以下顺序检查:
- 字节顺序:确认最终结果是否进行了高低字节交换
- 多项式选择:必须使用0xA001而非标准CRC-16的0x8005
- 初始值:确保计算前CRC寄存器初始化为0xFFFF
- 数据范围:检查是否包含了从设备地址到数据域的全部字节
5. 性能优化与高级应用
对于需要高频计算CRC的场景,可采用以下优化策略:
- 查表法优化:预先计算256种字节值的CRC结果
CONST aCRCTable : ARRAY[0..255] OF WORD := [ 16#0000, 16#C0C1, 16#C181, 16#0140, ... // 完整256项表格 ]; END_CONST- 批量处理:对大块数据采用分段计算策略
- 异步计算:在后台任务中执行CRC校验
实际项目中,我们曾遇到变频器通信异常问题,最终发现是CRC计算未考虑设备特定的报文前缀。这提醒我们:任何协议实现都要以设备文档为准,标准算法可能需要根据设备特性调整。
