三菱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 CACRC校验码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.htmlPython验证脚本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计算未考虑设备特定的报文前缀。这提醒我们任何协议实现都要以设备文档为准标准算法可能需要根据设备特性调整。