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要干这些事:
- 读取输入(把物理输入信号读到映像区)
- 执行OB1里的用户程序
- 写入输出(把映像区的值写到物理输出)
- 处理通信、自诊断等系统任务
然后,再从头开始。周而复始,直到断电。
为什么循环执行机制很重要?
因为它决定了程序的“实时性”。如果一个扫描周期是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发来的指令。这个我们后面章节会详细讲。