三菱 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 Word[Signed] 寄存器数量(用户输入值)
VAR_INPUT data Word[Unsigned]/Bit STRING[16-bit] (0...127) 要计算的数据(INT数组)
VAR_INPUT rs_mode Bit RS指令设定的寄存器模式:0/1=16位/8位
VAR_OUTPUT output Word[Unsigned]/Bit STRING[16-bit] 计算结果
VAR reg_len_OK Word[Signed] 寄存器数量(校验OK值)
VAR crc_tmp Word[Unsigned]/Bit STRING[16-bit] CRC输出(初始值0xFFFF)
VAR reg_idx Word[Signed] 寄存器索引(循环变量1)
VAR index_j Word[Signed] 循环变量2
VAR index_i Word[Signed] 循环变量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 Word[Unsigned]/Bit STRING[16-bit]
VAR crc_result2 Word[Unsigned]/Bit STRING[16-bit]
VAR crc_result3 Word[Unsigned]/Bit STRING[16-bit]
VAR crc_result4 Word[Unsigned]/Bit STRING[16-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 Word[Signed]
VAR test_data_in Word[Unsigned]/Bit STRING[16-bit] (0...127)
VAR crc_result5 Word[Unsigned]/Bit STRING[16-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 都有,不需要额外编程,保证效率

相关推荐
控电PLC2 小时前
21-2. PLC的基本逻辑指令(触点的串并联指令)
硬件工程·plc
张人玉2 天前
技术开发文档:MES 系统与西门子 PLC 通信软件集成方案
https·c#·plc·mes系统·西门子s7
张人玉2 天前
MES 系统、上位机(西门子 PLC 对接)数据交互技术与使用文档
plc·mes系统
御控工业物联网2 天前
二供泵房物联网控制解决方案
物联网·数据采集·plc·远程控制·工业自动化
张人玉3 天前
C#编写西门子S7PLC通信的相关知识点
microsoft·c#·wpf·plc·西门子s7通信
御控工业物联网3 天前
工业网关新玩法:手机变“移动触摸屏”,局域网内远程操控PLC
物联网·智能手机·自动化·数据采集·plc·远程控制·远程操控plc
控电PLC4 天前
21-1. PLC的基本逻辑指令(逻辑取及线圈驱动指令)
硬件工程·plc
御控工业物联网5 天前
智慧水务新突破:基于工业网关的二次供水泵房物联网采集解决方案
物联网·自动化·数据采集·plc·远程监控·远程控制·多品牌plc数据采集
小徐敲java5 天前
plc通过opcua订阅或s7读取数据慢的原因
plc·博途