3、PLC编程基础回顾(二):数据块(DB)与变量表设计、函数块(FB)与函数(FC)的创建与调用、组织块(OB)的循环执行机制

好,咱们接着聊。上一节我们把PLC的硬件和基本指令捋了一遍,这一节要深入一点,聊聊真正让PLC程序变得灵活、可维护的核心——数据块、函数块、函数,还有那个永远在后台默默工作的组织块。

说实话,我刚开始带团队的时候,发现很多新手工程师写程序,喜欢把所有变量都扔在OB1里,一个块写几百行。结果呢?项目后期调试,改一个点要翻半天,还容易改出bug。后来我强制要求大家用DB和FB来组织代码,效率提升不是一点半点。

3.1 数据块(DB)与变量表设计

数据块,说白了就是PLC的“仓库”。你把需要长期保存的数据,比如设备状态、工艺参数、配方值,都放在这里。它不像临时变量,断电就丢了。

DB分两种:全局DB和背景DB。

  • 全局DB:谁都能访问,像个公共储物柜。我习惯把系统级的参数,比如产线速度、报警阈值,放在这里。
  • 背景DB:专属于某个FB(函数块)的私有仓库。只有这个FB能读写,外部程序想访问,得通过FB的接口。这其实就是面向对象编程里的“封装”思想。

变量表设计原则(我踩过的坑)

我曾经在一个项目中,把电机启停信号和温度传感器数值放在同一个DB里,结果现场调试时,维护工程师改了一个地址,把启停信号覆盖了,电机直接飞车。从那以后,我定了个规矩:

  • 按功能分区:电机控制一个DB,模拟量采集一个DB,报警管理一个DB。
  • 命名规范:变量名要能“望文生义”。比如“Motor_Start_CMD”就比“M1”好一万倍。
  • 数据类型明确:BOOL、INT、REAL别混用,否则数据转换会把你搞疯。

举个例子,创建一个电机控制DB:

// 电机控制数据块 (DB_MotorCtrl)
// 地址偏移 | 变量名          | 数据类型 | 初始值 | 注释
// 0.0      | Start_CMD       | BOOL     | FALSE  | 启动命令
// 0.1      | Stop_CMD        | BOOL     | FALSE  | 停止命令
// 0.2      | Running_Status  | BOOL     | FALSE  | 运行反馈
// 2.0      | Speed_Setpoint  | INT      | 0      | 速度设定值 (rpm)
// 4.0      | Speed_Actual    | INT      | 0      | 速度实际值 (rpm)
// 6.0      | Current         | REAL     | 0.0    | 电机电流 (A)

小技巧:在博途(TIA Portal)里,你可以给DB变量设置“保持性”(Retain)。这样PLC断电重启后,数据还在。比如计数器当前值、配方编号,我一般都会勾上。

3.2 函数块(FB)与函数(FC)的创建与调用

这两个东西,是PLC程序模块化的灵魂。很多新手分不清,我简单说说:

  • FB(函数块):有“记忆”的块。它自带一个背景DB,能记住上次运行的状态。比如一个电机控制FB,你调用它一次,它就能记住电机是转还是停。
  • FC(函数):没“记忆”的块。它像一把计算器,输入几个数,算个结果给你,完事就忘。比如一个计算圆面积的FC,输入半径,输出面积,不保留任何中间状态。

什么时候用FB,什么时候用FC?

我个人习惯:凡是需要“保持状态”的,用FB;凡是纯计算、逻辑判断的,用FC。

举个例子:

  • 控制一个阀门(需要记住开/关状态)→ 用FB
  • 计算两个数的平均值 → 用FC

创建FB时,你需要定义它的接口:

// 函数块:FB_MotorControl
// 输入参数 (Input):
//    Start : BOOL;   // 启动按钮
//    Stop  : BOOL;   // 停止按钮
// 输出参数 (Output):
//    Run   : BOOL;   // 运行输出
// 静态变量 (Static):
//    Timer : TON;    // 内部定时器(用于延时保护)
// 临时变量 (Temp):
//    Temp_Flag : BOOL; // 临时标志位

// 内部逻辑(简化版):
IF Start AND NOT Stop THEN
    Run := TRUE;
ELSIF Stop THEN
    Run := FALSE;
END_IF;

调用的时候,在OB1里拖出来,系统会自动生成一个背景DB(比如DB_MotorCtrl_1)。你可以在线修改这个DB里的值,就能控制电机了。

注意:FC因为没有背景DB,它的临时变量(Temp)在每次调用时都是不确定的初始值。我曾经有个同事,在FC里用了一个Temp变量做累加,结果每次调用都清零,查了半天bug。记住:FC里别用Temp变量做“记忆”用途!

3.3 组织块(OB)的循环执行机制

组织块,是PLC的“调度员”。它决定了什么时候执行什么代码。

最核心的是OB1——主循环组织块。

PLC上电后,CPU会不停地、循环地执行OB1里的程序。这个循环周期,就是扫描周期。你想想看,一个循环里,CPU要干这些事:

  1. 读取输入(把物理输入信号读到映像区)
  2. 执行OB1里的用户程序
  3. 写入输出(把映像区的值写到物理输出)
  4. 处理通信、自诊断等系统任务

然后,再从头开始。周而复始,直到断电。

为什么循环执行机制很重要?

因为它决定了程序的“实时性”。如果一个扫描周期是10ms,那么你的程序每10ms才能响应一次输入变化。如果程序写得太大,扫描周期变成100ms,那响应就慢了。

我在一个高速包装线上就遇到过这个问题。因为OB1里塞了太多通信和数据处理代码,扫描周期飙到了50ms,结果传感器信号采集滞后,包装袋切偏了。后来我把通信任务挪到了循环中断OB(OB35)里,OB1只做核心逻辑,扫描周期降到了8ms,问题解决。

其他常用的OB:

OB编号 名称 触发方式 典型用途
OB1 主循环 循环执行 核心控制逻辑
OB35 循环中断 固定时间间隔(如10ms) 高速采集、PID调节
OB40~OB47 硬件中断 外部信号触发(如编码器脉冲) 高速计数、位置捕获
OB100 启动组织块 PLC上电时执行一次 初始化参数、设置默认值
OB82 诊断中断 模块故障时触发 故障报警、日志记录

避坑指南:我曾经在OB100里写了一大段初始化代码,结果调试时发现,每次PLC从STOP切到RUN,都会重新初始化,把生产数据清零了。后来我加了个判断:只在第一次上电时初始化,正常重启时保留数据。嗯,这个细节很重要。

最后说一句,理解OB的循环机制,是理解PLC与IPC协同的基础。因为IPC(工控机)的程序是事件驱动的,而PLC是循环驱动的。两者要配合好,就得知道PLC什么时候“有空”去处理IPC发来的指令。这个我们后面章节会详细讲。