Skip to main content

 


 

引言

在工业现场通信中,Modbus、Profinet、自定义串口协议等数据传输场景里,CRC(循环冗余校验) 是最常用的数据完整性校验方式,相当于给数据包加了一个“唯一指纹”——只要传输过程中有 1 位数据被干扰出错,接收方计算出的 CRC 值就会和发送方不一致,从而直接丢弃错误数据,避免设备误动作。 很多工程师在使用 PLC 做通信开发时,要么依赖厂商自带的 CRC 库(灵活性差、无法适配自定义协议),要么自己写 scattered 的零散逻辑(容易出 bug、复用性差)。今天给大家介绍一款轻量、兼容性强、可灵活配置的 IEC 61131-3 标准 CRC 计算功能块FB_CRCCalculator,无需额外依赖库文件,几乎所有品牌的 PLC(西门子、汇川、信捷、台达等)都能直接导入使用。

1

   

核心程序代码及逻辑详解

1.1

   

完整功能块代码

下面是完整的 PLC 程序代码(代码基于 CodeSys 平台的 ST 语言):

FUNCTION_BLOCK FB_CRCCalculator
VAR_INPUT    
    dataIn : BYTE;          // 输入的单字节数据    
    dataValid : BOOL;       // 数据有效标志(上升沿触发处理)   
    polynomial : WORD;      // CRC多项式,可配置适配不同协议    
    reset : BOOL;           // 复位信号,高电平有效
END_VAR
VAR_OUTPUT    
    crcValue : WORD;        // 计算完成的CRC结果    
    dataReady : BOOL;       // 计算完成标志,单周期脉冲输出
END_VAR
VAR    
    i : INT;                // 移位循环计数    
    crcReg : WORD;          // CRC运算寄存器    
    bitCount : INT;         // 已接收数据位数计数    
    lastDataValid : BOOL;   // 上一次dataValid的状态,用于上升沿检测
END_VAR
// 复位逻辑:最高优先级,复位时清空所有状态
IF reset THEN    
    crcReg := 16#FFFF;     // CRC寄存器初始值,可根据协议修改    
    crcValue := 0;    
    dataReady := FALSE;    
    bitCount := 0;    
    RETURN;
END_IF;
// 数据处理:仅当dataValid出现上升沿(从无效变有效)时处理当前字节
IF dataValid AND NOT lastDataValid THEN    
    // 1. 当前字节与CRC寄存器异或    
    crcReg := crcReg XOR BYTE_TO_WORD(dataIn);    
    // 2. 核心CRC移位运算:对当前字节的8位逐位处理    
    FOR i := 1 TO 8 DO        
        // 判断CRC寄存器最低位是否为1        
        IF (crcReg AND 16#0001) <> 0 THEN            
            crcReg := SHR(crcReg, 1) XOR polynomial; // 最低位为1则右移后异或多项式        
        ELSE            
            crcReg := SHR(crcReg, 1); // 最低位为0仅右移        
        END_IF;    
    END_FOR;    
    // 3. 累计已处理的数据位数    
    bitCount := bitCount + 8;    
    // 4. 达到预设帧长后输出CRC结果(默认每8字节/64位输出一次)    
    IF bitCount >= 64 THEN  // 8字节,可根据实际帧长修改阈值        
        crcValue := crcReg;        
        dataReady := TRUE;  // 输出单周期脉冲,提示结果可用        
        bitCount := 0;      // 计数器清零,准备下一帧计算    
    ELSE        
        dataReady := FALSE;    
    END_IF;
ELSE    
    dataReady := FALSE;
END_IF;
// 更新上一次dataValid状态,供下次扫描做上升沿判断
lastDataValid := dataValid;

 

1.2

   

逻辑逐段解读

  1. 变量设计逻辑

    • 输入dataValid采用上升沿触发机制,配合内部变量lastDataValid,避免同一个字节被重复计算(比如串口接收一个字节需要 10ms,dataValid 拉高 10ms,仅第一个扫描周期会处理该字节,后续即使 dataValid 仍为高也不会重复运算,从根源避免 CRC 错误)。

    • polynomial和 CRC 寄存器初始值可配置,无需修改核心逻辑就能适配 Modbus RTU、CRC16-IBM、CRC16-CCITT 等几乎所有常用 CRC 协议。

    • bitCount默认按 8 字节(64 位)为一帧输出结果,如果是变长帧或者单字节校验的场景,可以自行调整阈值逻辑。

  2. 核心运算逻辑

    • 复位信号优先级最高,拉高复位时会直接清空寄存器、计数器和输出,适合通信故障时快速重启校验逻辑。

    • 8 次移位循环是 CRC16 算法的核心:通过多项式除法逐位计算校验值,所有运算都是位操作,对 PLC 的资源消耗极低,哪怕 100 点以下的小型 PLC 也能流畅运行。

2

   

典型应用场景

2.1

   

工业串口通信校验(最常用场景)

在 Modbus RTU、自定义串口协议通信中,PLC 作为主站/从站接收数据时,可以在串口接收中断中调用该功能块:

  • 每收到 1 个字节,将字节值赋给dataIn,给dataValid拉高 1 个扫描周期(保证能检测到上升沿)。

  • 接收满一帧数据(比如 Modbus 默认一帧最多 256 字节,可根据需求调整bitCount阈值)后,dataReady会输出一个扫描周期的高电平脉冲,此时读取crcValue,和数据帧末尾的 CRC 校验位比对,即可判断数据是否传输错误。

  • 示例:适配 Modbus RTU 的 CRC16 时,只需将polynomial设为16#8005,复位后拉高,收满一帧后读出的crcValue和 Modbus 协议要求的校验值完全一致。

2.2

   

现场传感器数据完整性校验

很多模拟量传感器、数字仪表会通过 RS485/以太网传输采集数据,如果现场电磁干扰强,很容易出现数据跳变。可以在 PLC 采集端调用该功能块,每凑够 8 字节(或自定义帧长)就做一次 CRC 校验,错误数据直接丢弃,避免干扰数据进入控制逻辑导致设备误动作。

2.3

   

老旧设备改造与边缘网关预处理

老旧设备可能用的是专用 CRC 校验芯片,故障率高且无法适配新协议;用该功能块软实现 CRC 校验,无需额外硬件,还能灵活修改多项式适配新协议。如果是 PLC 做边缘网关的场景,可以在 PLC 端先做 CRC 校验,错误数据直接过滤,不用上传到上位机,大幅降低网络带宽压力。

3

   

拓展优化思考

这个功能块的框架非常灵活,大家可以根据自己的项目需求自行裁剪优化:

  1. 适配更多 CRC 协议如果需要计算 CRC8、CRC32,只需要修改内部变量的位宽即可:比如 CRC32 把crcReg改成 DWORD 类型,polynomial改成 DWORD,移位循环次数改成 32 次,输出改成 DWORD 类型就能直接使用;如果适配左移优先的 CRC 协议,只需要把SHR(右移)改成SHL(左移)即可。

  2. 调整输出频率原程序默认每 8 字节输出一次结果,如果需要每收到 1 个字节就输出 CRC 结果(比如实时校验场景),可以直接删除bitCount的判断逻辑,每次处理完字节后直接更新crcValue并拉高dataReady即可。

  3. 效率优化如果 PLC 内存资源充足,可以把逐位移位的 FOR 循环改成 CRC 查表法,运算速度能提升数倍,适合高速通信场景;如果是资源紧张的小型 PLC,原程序的逐位运算逻辑已经足够轻量,几乎不占用额外资源。

  4. 时序适配如果现场dataValid是窄脉冲触发(比如中断输出脉冲宽度小于 1 个 PLC 扫描周期),可以把上升沿检测逻辑改成下降沿,或者把功能块放在中断任务中调用,避免漏触发。


 

总结

FB_CRCCalculator是一款典型的“小而美”的工业功能块,没有复杂的依赖,逻辑清晰易懂,既可以满足常规通信校验的需求,也能通过简单修改适配绝大多数自定义 CRC 协议。对于经常需要做通信开发的 PLC 工程师来说,把这个功能块放进自己的工具库里,能节省大量重复造轮子的时间,同时避免不同项目中 CRC 逻辑不兼容的问题。 使用时只需要注意三个核心点:上升沿触发dataValid、根据协议配置正确的多项式和初始值、根据帧长调整bitCount阈值,就能稳定运行,是工业现场数据校验的实用工具。

图片

你来出题 我来答图片图片图片

欢迎大家在评论区提一些标准功能块的需求,如果合适,我们会在以后的文章中与大家分享。

图片

往期回顾


 

每周一个编程小例子 :Bit 到 Word 转换的两种实现方案对比

每周一个编程小例子:记录设备运行时间

每周一个编程小例子:基于长按检测的启动控制系统

每周一个编程小例子:探秘 PLC 数据记录功能块

每周一个编程小例子:心跳检测与 PLC 应用

每周一个编程小例子:斜坡发生器

每周一个编程小例子:旋转速度计算

每周一个编程小例子:计算 PLC 循环周期|例程需求征集!

每周一个编程小例子:如何优雅的分割字符串

每周一个编程小例子:带反馈的阀门控制程序

每周一个编程小例子:西门子 LGF 库中的脉冲继电器功能块

每周一个编程小例子:曲线加减速的控制应用

每周一个编程小例子:轻松去除字符串前导字符

每周一个编程小例子:根据阈值更新输入

每周一个编程小例子:官方模拟量输入处理模块

每周一个编程小例子:主从电机的时序控制

每周一个编程小例子:基于一阶滞后滤波的优化方案

每周一个编程小例子:16 进制到浮点数的神奇转换

每周一个编程小例子:PLC 长按控制程序

每周一个编程小例子:PLC 程序实现多位整数拆分

每周一个编程小例子:流量累积计算器

每周一个编程小例子:用于连锁的电机标准控制模块

每周一个编程小例子:PID 控制算法在 PLC 系统中的实现与应用

每周一个编程小例子:自定义种子生成随机数

每周一个编程小例子:可调时间的脉冲生成器

每周一个编程小例子:带滤波的标准模拟量输入功能块

每周一个编程小例子:使用 PLC 将字符串转换为日期格式

每周一个编程小例子:PLC 配方管理

每周一个编程小例子:比较两个 DB 块(STL)

每周一个编程小例子:如何使用 PLC 实现数组逆序

每周一个编程小例子:流量累计与批次控制

每周一个编程小例子:自定义循环执行控制程序

每周一个编程小例子:步进控制功能块

每周一个编程小例子:PLC 权重比例分配器

每周一个编程小例子:数字量输入组监控

每周一个编程小例子:实时数据分析

每周一个编程小例子:PLC 多轴同步控制的理解与应用

每周一个编程小例子:PLC 波形生成器 (正弦 方波 三角波)

每周一个编程小例子:模拟量信号报警

每周一个编程小例子:组合数概率的实现

每周一个编程小例子:多传感器信号融合与优化方案

每周一个编程小例子:监控模拟信号的健康状态

每周一个编程小例子:PLC 程序实现模拟信号的线性校准

每周一个编程小例子:智能报警过滤器:优化报警系统的 PLC 程序

每周一个编程小例子:使用 PLC 程序让灯光呼吸

每周一个编程小例子:自适应性滤波器:动态调整以平衡响应与稳定性

每周一个编程小例子:卡尔曼滤波器在自动化中的简单应用

每周一个编程小例子:多模式定时器

每周一个编程小例子:基于 PLC 的编码器速度计算器

每周一个编程小例子:电机顺序启动与停止程序

每周一个编程小例子:电梯控制程序

每周一个编程小例子:自动门控制系统

每周一个编程小例子:智能停车场计数系统

每周一个编程小例子:智能照明控制系统

每周一个编程小例子:变频器控制

每周一个编程小例子:比例阀控制

每周一个编程小例子:脉冲输出的灵活控制

每周一个编程小例子:计算移动平均值

每周一个编程小例子:速率限制器

每周一个编程小例子:线性插值法线性化传感器

每周一个编程小例子:数据转换的魔力

每周一个编程小例子:PLC 安全监控功能块

每周一个编程小例子:PLC 消抖功能块

每周一个编程小例子:「看门狗」功能块

每周一个编程小例子:实现工业数据轻量压缩

图片

2026年10月