文章目录

PLC程序中的数据校验与CRC计算

发布于 2026-03-25 16:24:55 · 浏览 6 次 · 评论 0 条

PLC程序中的数据校验与CRC计算

在工业现场,PLC与变频器、触摸屏或上位机进行通讯时,信号干扰是不可避免的。电缆铺设在强电旁,电磁场会在传输线上产生噪声,导致原本发送的数据0x03变成了0x02。如果这是启动电机的指令,后果不堪设想。为了确保数据接收方收到的信息与发送方完全一致,必须在数据包末尾附加一串“指纹”,这就是数据校验。在众多校验方式中,CRC(循环冗余校验)因检错能力强、计算速度快,成为了Modbus等工业协议的标准配置。


理解CRC的核心逻辑

不要被复杂的数学术语吓倒。CRC的计算原理本质上就是“除法取余”,只不过是在二进制世界里进行的除法。

想象一下,我们把要发送的一串数据看作一个巨大的二进制数,然后除以一个固定的小整数(这个固定的数叫“生成多项式”)。计算过程中得到的“余数”,就是CRC校验码。

当接收方收到数据后,它用同样的算法把数据部分除以那个固定的小整数。如果算出来的余数和发送过来的CRC码不一样,就说明数据在传输中被修改了。

在Modbus RTU协议中,最常用的是CRC-16模型,其生成多项式为:

$$ P(x) = x^{16} + x^{15} + x^2 + 1 $$

对应的十六进制数通常记为 0xA001


CRC计算的硬件流程

在编写代码之前,先理清手工计算的逻辑流程。虽然PLC内部有指令可以一步完成,但理解流程有助于排查错误。以下是计算单个字节串的CRC-16标准流程。

graph TD A["Start: Load Register 0xFFFF"] --> B["Loop Start: Get Next Byte"] B --> C["XOR Byte with Low Byte of Register"] C --> D["Loop: 8 Bit Shifts"] D --> E{Is LSB 1?} E -- "Yes" --> F["XOR Register with Polynomial: 0xA001"] E -- "No" --> G["Shift Right 1 Bit"] F --> G G --> H{Bit Count < 8?} H -- "Yes" --> D H -- "No" --> I{More Bytes?} I -- "Yes" --> B I -- "No" --> J["Finish: Swap High/Low Byte"]

实战:手动计算CRC(验证工具)

在PLC里写程序前,先用Windows自带的计算器验证一下算法是否正确。假设我们要计算的数据帧(十六进制)为:01 03 00 00 00 01

  1. 打开 Windows计算器,选择 “程序员”模式。
  2. 输入 初始值 FFFF
  3. 第一个数据字节 01,将其与当前值进行 异或(XOR)运算。
    • FFFF XOR 0101 (注意字节位置,通常是低8位运算) = FEFE
  4. 判断 最低位是否为1。如果是,异或 多项式 A001;如果不是,直接 右移 一位。
  5. 重复 右移和异或操作,共进行8次循环(处理完一个字节的所有位)。
  6. 下一个数据字节 03,与当前寄存器值 异或,再次重复8次移位循环。
  7. 处理 完所有字节(00 00 00 01)后,寄存器中得到的值通常是 340E(注意:高低字节可能需要根据具体规则交换)。

编写PLC程序:三菱FX系列实现

不同品牌的PLC实现方式不同,有的内置CRC指令,有的需要用梯形图逻辑手搓。这里以三菱FX3U为例,展示“手搓”算法的过程。假设数据存储在 D0 开始的连续寄存器中,数据长度为 K6

步骤1:初始化寄存器

首先需要一个存放CRC结果的寄存器,初始化为 FFFF

写入 以下梯形图逻辑:
当M0接通时,传送 FFFFD10(CRC寄存器),D10 存放高八位,D11 存放低八位。

步骤2:构建循环与移位逻辑

由于PLC指令集限制,我们通常使用“查表法”或“位操作法”。这里演示位操作法的核心思路。

  1. 建立 一个索引指针 Z0,初始值为0,指向 D0
  2. 建立 一个位计数器 D20,初始值为8。

步骤3:异或与移位循环

这是核心算法部分。

  1. 当前指针 Z0 指向的数据 D0Z0

  2. 执行 WOR 指令(逻辑异或):将 D11(CRC低字节)与 D0Z0 进行异或,结果存回 D11

  3. 创建 一个针对 D11 的循环,循环8次(对应8个Bit):

    • 检测 D11 的最低位(位0)。
    • 如果 为1,执行 WXOR 指令:将 D10D11 组成的32位数与 A001 进行异或。
    • 执行 ROR / RCR 指令:将 D10D11 视为一个16位整体,带进位右移 一位。
    • 如果 为0,仅 执行 带进位右移一位。
    • 递减 位计数器 D20
  4. 当8位处理完后,递增 指针 Z0

  5. 判断 Z0 是否小于数据长度6。如果小于,跳转 回步骤3处理下一个字节。

步骤4:高低字节交换

Modbus协议要求先发低字节,再发高字节。而计算结果通常在寄存器中是高位在左。在发送前,交换 D10D11 的内容。


编写PLC程序:西门子S7-1200/1500实现

西门子SCL(结构化控制语言)更适合处理这种算法,代码简洁易读。

打开 TIA Portal软件,新建一个FC块,语言选择SCL。

  1. 定义 变量接口:

    • pData:Variant类型,指向数据区。
    • dwLength:DWord类型,数据长度。
    • dwCRC:DWord类型,输出校验码。
  2. 编写 程序代码:

FUNCTION_BLOCK "FB_CalcCRC"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1

VAR_INPUT
    pData : Variant;
    dwLength : DWord;
END_VAR

VAR_OUTPUT
    dwCRC : Word;
END_VAR

VAR_TEMP
    iCount : DInt;
    jCount : DInt;
    byData : Byte;
    wCRC : Word := 16#FFFF;
    wPoly : Word := 16#A001;
END_VAR

BEGIN
    // 将Variant转换为指针访问或直接使用AT覆盖(此处为简化逻辑描述)
    // 实际应用中建议使用 "AT" 结构覆盖字节块或使用Serialize指令读取

    FOR iCount := 0 TO (dwLength - 1) DO
        // 获取当前字节 (此处示意,具体取决于数据源类型)
        // 假设源是数组,实际需根据pData具体类型调整
        byData := 0; 

        // 异或操作
        wCRC := wCRC XOR byData;

        // 8位循环移位
        FOR jCount := 0 TO 7 DO
            IF (wCRC AND 16#0001) <> 0 THEN
                wCRC := SHR(IN:=wCRC, N:=1);
                wCRC := wCRC XOR wPoly;
            ELSE
                wCRC := SHR(IN:=wCRC, N:=1);
            END_IF;
        END_FOR;
    END_FOR;

    dwCRC := wCRC;
END_FUNCTION_BLOCK

注意:西门子编程中,直接处理Variant需要谨慎。如果是Modbus库调用,通常直接使用 MB_CLIENT 指令自动处理CRC。如果必须手动计算,建议将数据先 MOVE 到一个 Array of Byte 中,再传入上述逻辑。


常见CRC参数速查表

不同的协议使用的多项式、初始值可能不同。核对 下表,确保你的计算参数正确。

协议/名称 宽度 多项式 初始值 结果异或 输入反转 输出反转
Modbus RTU 16 0xA001 0xFFFF 0x0000 True True
CRC-16/CCITT 16 0x1021 0x0000 0x0000 False False
CRC-16/XMODEM 16 0x1021 0x0000 0x0000 False False
CRC-8 8 0x07 0x00 0x00 False False

表注:反转指的是字节内的Bit顺序,例如低位在前还是高位在前。


调试技巧与常见错误

在调试通讯程序时,CRC计算错误是最常见的问题。检查 以下几点可以节省大量时间。

  1. 高低字节反了

    • 现象:计算出的CRC值与标准值总是高低位颠倒。
    • 解决:Modbus协议规定先发低字节,再发高字节。如果你的PLC是大端存储(如某些旧式PLC),发送前必须 交换 高低字节。
  2. 数据包含了CRC本身

    • 现象:第一次计算正确,后续全错。
    • 解决:确保 CRC计算的输入数据长度只包含“有效数据”,不包含末尾的两个字节CRC。
  3. 多项式搞混了

    • 现象:计算结果完全对不上。
    • 解决:确认对方设备使用的是 0xA001 (Modbus) 还是 0x1021 (XMODEM) 或其他多项式。
  4. 使用在线计算器验证

    • 当程序跑不通时,复制 你的十六进制数据字符串。
    • 打开 浏览器,搜索 "CRC calculator online"。
    • 粘贴 数据,选择 Model 为 Modbus
    • 对比 在线结果与PLC寄存器内的结果。如果在线计算器是对的,那就是你的代码逻辑有漏洞。

最终步骤:
完成程序编写后,下载 到PLC。强制 发送一组固定的数据(如 01 03 00 00 00 01)。监控 CRC计算寄存器的值。如果寄存器显示 340E(假设低字节在前),连接 通讯线,观察接收设备的响应状态码是否为正常(如无异常校验错误)。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文