三菱 FX3U Modbus CRC16 校验码生成程序(结构化工程,计算法)

1. 设备

  1. PLC型号:三菱 FX3U
  2. 使用仿真器验证程序。仿真器------凌一仿真器
  3. 通讯设备:电脑模拟串口(com0com)

2. 参考资料

  1. 三菱官方通讯手册:FX系列微型可编程控制器 用户手册 通信篇
  2. Modbus 协议规范文档(CRC 计算示例在附录 80-86 页):https://image.modbus.cn/wp-content/uploads/2022/03/2022031708564080.pdf
  3. CRC16-Modbus 校验代码实例(C语言实现,查表法、计算法均有):【CRC笔记】CRC-16 MODBUS C语言实现_modbus crc16 c语言代码-CSDN博客
  4. 计算法PLC程序(西门子博途ST语言实现,以下代码的参考源于此视频,还可参考视频下的高赞评论代码):MODBUS RTU CRC16校验算法-Bilibili

3. 代码

  1. FX3U工程项目使用 "结构化工程" 类型

    此类型创建的项目,使用标签作变量,系统自动分配标签给寄存器,不需要纠结具体寄存器位置,可使用 IEC 标准的指令,使用起来有点欧系 PLC 的味道

  2. 使用计算法实现(占用内存少,但处理大量寄存器计算时速度较慢)

  3. 按照 RS 指令发送顺序优化(实现以下两种数据发送模式下的 CRC 校验计算)

    1. M8161 = 0 -- 设置为 16 位数据收发模式
      • 一个 WORD 寄存器,先发低字节,再发高字节
      • 实际发送字节数 = 寄存器数量 X 2 2. M8161 = 1 -- 设置为 8 位数据收发模式。
    • 一个 WORD 寄存器,只发低字节,高字节不管。
    • 实际发送数据字节数 = 传入的寄存器数量
    • 使用 PLC 中自带的 CRC 指令则应使用这种数据模式,否则算出的校验码和传入的数据不匹配)

计算法见上一篇文章

上一篇文章讲解了计算法相关程序:https://blog.csdn.net/weixin_44112083/article/details/155649333

全局标签(全局变量)

计算法不需要全局标签

3.1 功能块 -- MB_CRC16_Calc -- CRC校验码计算程序(计算法)

此功能块每次计算 CRC 时调用,可重复调用同一个实例进行不同数据的计算


局部标签

标签名 数据类型 注释
VAR_INPUT reg_len WordSigned 寄存器数量(用户输入值)
VAR_INPUT data WordUnsigned/Bit STRING16-bit (0...127) 要计算的数据(INT数组)
VAR_INPUT rs_mode Bit RS指令设定的寄存器模式:0/1=16位/8位
VAR_OUTPUT output WordUnsigned/Bit STRING16-bit 计算结果
VAR reg_len_OK WordSigned 寄存器数量(校验OK值)
VAR crc_tmp WordUnsigned/Bit STRING16-bit CRC输出(初始值0xFFFF)
VAR reg_idx WordSigned 寄存器索引(循环变量1)
VAR index_j WordSigned 循环变量2
VAR index_i WordSigned 循环变量3
  • 功能块输入(定义同上一篇文章中的计算法功能块):
    • 数据:data,无符号整数数组,最大长度128
    • 寄存器数量:reg_len,无符号整数,范围 0-125
    • 数据模式(前文说过的8/16位模式):rs_mode,0/1 = 8位模式 / 16位模式
  • 功能块输出
    • 计算出的CRC校验码结果:output,无符号整数

程序本体:ST 语言代码段

BASIC 复制代码
(* Modbus CRC16 校验程序-计算法 *)

(* 2. 边界检查:最多128个寄存器(256字节,功能码&站号(2字节) + 寄存器长度(2字节) + 传输数据最多125字) *)
IF reg_len > 125 THEN
	reg_len_OK := 125;
ELSIF reg_len <= 0 THEN
	reg_len_OK := 0;
	output := 16#FFFF;
	RETURN;
ELSE
	reg_len_OK := reg_len;
END_IF;

(* 设置输出初始值  *)
crc_tmp := 16#FFFF;

(* 3-1. 16位模式 M8161=0  *)
IF NOT rs_mode THEN
	(* 外循环,对每个寄存器 *)
	FOR reg_idx := 0 TO reg_len_OK - 1 DO
		FOR index_i := 0 TO 1 DO  (* 高低字节循环,先低字节后高字节 *)
			IF index_i = 0 THEN
				crc_tmp := crc_tmp XOR (data[reg_idx] AND 16#00FF);
			END_IF;
			IF index_i = 1 THEN
				crc_tmp := crc_tmp XOR SHR(data[reg_idx], 8);
			END_IF; 
			(* 内循环,对每一位 *)
			FOR index_j := 1 TO 8 DO
				IF (crc_tmp AND 16#0001) = 1 THEN
					crc_tmp := SHR( crc_tmp , 1 );
					crc_tmp := crc_tmp XOR 16#A001;
				ELSE
					crc_tmp := SHR( crc_tmp , 1 );
				END_IF;
			END_FOR;
		END_FOR;
	END_FOR;
END_IF;

(* 3-2. 8位模式 M8161=1,只取低Byte作计算  *)
IF rs_mode THEN
	(* 外循环,对每个寄存器 *)
	FOR reg_idx := 0 TO reg_len_OK - 1 DO
		crc_tmp := crc_tmp XOR (data[reg_idx] AND 16#00FF);
		(* 内循环,对每一位 *)
		FOR index_j := 1 TO 8 DO
			IF (crc_tmp AND 16#0001) = 1 THEN
				crc_tmp := SHR( crc_tmp , 1 );
				crc_tmp := crc_tmp XOR 16#A001;
			ELSE
				crc_tmp := SHR( crc_tmp , 1 );
			END_IF;
		END_FOR;
	END_FOR;
END_IF;

(* 输出结果  *)
output := crc_tmp;

4. 功能块测试

声明 ST 代码段TEST_CRC,调用编写的功能块并验证输出是否OK

(在上一篇文章基础上,调用计算法功能块,并与查表法功能块比较数据是否一致)


代码段局部标签(局部变量)

标签名 数据类型 注释
VAR crc_result WordUnsigned/Bit STRING16-bit
VAR crc_result2 WordUnsigned/Bit STRING16-bit
VAR crc_result3 WordUnsigned/Bit STRING16-bit
VAR crc_result4 WordUnsigned/Bit STRING16-bit
VAR MB_CRC16_Calc_1 MB_CRC16_Calc
VAR MB_CRC16_CheckTable_1 MB_CRC16_CheckTable
VAR MB_CRC16_InitCheckTable_1 MB_CRC16_InitCheckTable
VAR regs_to_check WordSigned
VAR test_data_in WordUnsigned/Bit STRING16-bit (0...127)
VAR crc_result5 WordUnsigned/Bit STRING16-bit
BASIC 复制代码
(* 示例1:3个寄存器-16位数据模式(对应字节流:03 05 00 10 00 1E) *)
test_data_in[0] :=16#0305;
test_data_in[1] :=16#1000;
test_data_in[2] :=16#1E00;
regs_to_check := 3;

(* PLC运行第一个周期,初始化CRC表 *)
IF M8002 THEN
	MB_CRC16_InitCheckTable_1();
END_IF;


(* 示例1-1:计算3个寄存器的CRC,16位模式,rs_mode=0 *)
(* 结果: crc_result = 16#4409 *)
MB_CRC16_CheckTable_1(reg_len:= regs_to_check ,
 					data:= test_data_in,
 					rs_mode:= 0,
 					output:= crc_result);
 					
(* 示例1-2:计算3个寄存器的CRC,16位模式,rs_mode=0 *)
MB_CRC16_Calc_1(reg_len:= regs_to_check ,
 					data:= test_data_in,
 					rs_mode:= 0,
 					output:= crc_result5);	


(* 示例2:6个寄存器-8位数据模式(对应字节流:03 05 00 10 00 1E) *)
test_data_in[0] :=16#0003;
test_data_in[1] :=16#0005;
test_data_in[2] :=16#0000;
test_data_in[3] :=16#0010;
test_data_in[4] :=16#0000;
test_data_in[5] :=16#001E;
regs_to_check := 6;

(* 示例2:使用内置函数计算3个寄存器的CRC,对应RS中的8位数据模式(M8161=1) *)
CRC( TRUE , test_data_in[0], 6 , crc_result2 );

(* 示例2-2:调用查表功能块,对应RS中的8位数据模式(M8161=1) *)
MB_CRC16_CheckTable_1(reg_len:= regs_to_check ,
 					data:= test_data_in,
 					rs_mode:= 1,
 					output:= crc_result3);

regs_to_check := 6;
 (* 示例2-3:调用计算法功能块,8位数据模式(M8161=1) *)
MB_CRC16_Calc_1(reg_len:= regs_to_check ,
 					data:= test_data_in,
 					rs_mode:= 1,
 					output:= crc_result4);			

在线验证网站:https://www.23bei.com/tool/59.html

模式1(rs_mode = FALSE,16位数据模式)验证

模式2(rs_mode = TRUE,8位数据模式)验证

5. 最后

此程序仅用于测试、熟悉三菱的编程界面

实际计算 Modbus 的 CRC16 校验码时,建议采用CRC()函数

此函数在 FX3U 以上型号 PLC 都有,不需要额外编程,保证效率

相关推荐
Oflycomm1 天前
2026年PLC技术全景:标准修订、Matter互联、欧飞信全栈模组矩阵深度解读
物联网·智能家居·plc·通信技术·电力线载波
小金子会发光2 天前
C# WinForms 基于 Socket 手搓 Modbus TCP 调试助手(支持 01/02/03/04/05/06/0F/10)
c#·socket·plc·modbus tcp·工业通信
土族程序员4 天前
采集发那科(FANUC)数控机床(CNC)数据 网线直连无须网关
数据采集·plc·cnc·机床·fanuc·发那科·joyzl
Emily156853598706 天前
ICS TRIPLEX T8403 TMR Digital Input Module
plc·module·dcs·ics triplex·t8403
风口上的猪201510 天前
批量模拟modbus tcp设备
modbus
9稳11 天前
基于组态的双向人行道交通灯控制系统设计
开发语言·网络·数据库·labview·plc
家有娇妻张兔兔13 天前
Java 对接 PLC 主流型号最合适的方案:Apache PLC4X 实战指南
java·开发语言·plc·modbus·数据缓存·西门子s7·apache plc4x
AlanBruce14 天前
摩尔信使MThings EdgeWeb HTTP API接口
网络·网络协议·http·上位机·plc·modbus·摩尔信使
AlanBruce22 天前
从串口仪表到西门子PLC:摩尔信使MThings的多协议设备接入能力
自动化·plc·modbus·mthings·摩尔信使·西门子s7