三菱 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 都有,不需要额外编程,保证效率

相关推荐
却道天凉_好个秋11 小时前
音视频学习(九十六):PLC
学习·音视频·plc
謓泽12 小时前
【MODBUS】串口 RTU / Modbus TCP / 透明就绪
网络·串口·modbus
9稳14 小时前
基于plc的自动化立体仓库控制系统设计
开发语言·网络·数据库·嵌入式硬件·plc
9稳15 小时前
基于智能巡检机器人与PLC系统联动控制设计
开发语言·网络·数据库·嵌入式硬件·plc
疆鸿智能研发小助手3 天前
疆鸿智能ETHERNET IP转MODBUS,让施耐德变频器“对话”无界
modbus·工业自动化·变频器·ethernet ip·工业通讯·协议转换网关
控电PLC4 天前
32. 功能图的产生及基本概念
plc·plc功能图
cfqq19897 天前
西门子1200G2
plc
czhc114007566311 天前
Modbus wpf 35
modbus
Hello_Embed1 个月前
Modbus 传感器开发:从寄存器规划到点表设计
笔记·stm32·单片机·学习·modbus
Hello_Embed1 个月前
Modbus 传感器开发:STM32F030 libmodbus 移植
笔记·stm32·学习·freertos·modbus