一、 I/O区 (Input/Output Area):PLC的“感官”与“手脚” - 直连外部世界!
- 本质是什么?
- I区 (输入映像区): 是PLC CPU内存里 专门划出来的一块地方,用来 “拍照” 外部物理输入端子 (按钮、开关、传感器) 的状态。它只是个“快照”!
- Q区 (输出映像区): 是PLC CPU内存里 专门划出来的一块地方,用来 “寄存” 程序计算好、准备发给物理输出端子 (继电器、指示灯、阀门线圈) 的命令。它只是个“待发指令”!
- 关键点: I区和Q区本身都在CPU内存里! 它们通过PLC的背板总线和扫描机制,与实际的输入模块和输出模块打交道。它们不和你的程序逻辑直接发生物理连接!
- 怎么工作?(扫描周期的核心!)
- 输入采样阶段: CPU 咔嚓一下,把此时此刻所有输入模块上端子的状态(ON/OFF),一次性、全部复制(刷新)到 I区 对应的位/字节。采样期间,外部输入再变化,I区也不理!
- 程序执行阶段: CPU 吭哧吭哧执行你的程序。程序里看到的、用到的所有 I (例如 I0.0),其实都是在操作 I区 内存里的那个“快照”!程序里写 Q (例如 = Q1.1),是在修改 Q区 内存里的“待发指令”! 这个阶段,外部世界天塌了,I区和Q区里的值也不会自动变!
- 输出刷新阶段: CPU 把 Q区 内存里当前所有的“待发指令”(ON/OFF状态),一次性、全部 发送(刷新)到对应的输出模块端子,驱动外部负载。刷新完了,Q区里的值就完成任务了(直到程序执行又修改它)。
- 应该怎么用?(铁律!)
- 只读 I: 在程序里,永远、只能、必须 从 I 地址 读取 外部输入的状态!你 不能、禁止、不可以 在程序里 写入 一个 I 地址(比如 = I0.0)!那是输入映像区,是给CPU拍照用的,不是给你乱改的!改了也没用,下个扫描周期采样就被覆盖了!
- 只写 Q: 在程序里,你唯一能做的 就是根据逻辑 计算、决定 Q 地址的状态,然后 写入 它(用线圈 ( )、置位 (S)、复位 (R) 或 赋值 :=)。程序 不能、禁止、不应该 去 读取 Q 地址的状态来参与逻辑运算(虽然某些平台允许,但这是极其危险且容易混淆的坏习惯!)。Q区的状态,只在输出刷新时才体现到外部! 程序里想知道输出状态?用你控制它的那个中间变量(比如一个M点)!
- 用途单一: I区只反映外部输入,Q区只决定外部输出。 它们不是用来存放程序中间结果的!不是用来做标志位的!不是用来存数据的!
- 混淆乱用的后果:
- 试图写 I 点?软件可能报错,或者写了也白写(被采样覆盖),逻辑混乱。
- 习惯性读 Q 点做逻辑?可能导致逻辑依赖扫描顺序(双线圈问题变种),程序难调试、难理解。最要命的是,如果某个Q点被强制(Force)了,你读到的Q点状态和实际外部输出状态可能不一致! 埋下重大隐患!
二、 M区 (Marker/Merker Area):程序里的“草稿纸”与“临时工” - 灵活但易逝!
- 本质是什么?
- PLC CPU内存里 划出来的一块 通用、位(bit)寻址为主 的存储区域。你可以把它想象成无数个虚拟的中间继电器。
- 核心特点:
- 掉电不保持 (默认): 绝大多数PLC的M区,默认情况下,PLC一断电,里面的数据(状态)就清零了!下次上电,从头开始。(这点和实物继电器线圈一断电就释放一样!)
- 寻址灵活: 通常按位 (M0.0, M10.7)、字节(MB0)、字(MW0)、双字(MD0)访问。但本质还是位存储,用字节/字/双字访问容易出错(高低字节顺序问题),不如DB区结构化好。
- 全局性: M区的变量通常是全局有效的,程序任何地方(OB, FC, FB)都能直接读写。方便,但也容易造成变量污染和意外修改!
- 应该怎么用?(适用场景)
- 临时标志位/状态位: 比如 AutoMode_Active (自动模式激活), Cycle_Start_Cmd (循环启动命令), Fault_Acknowledged (故障确认)。这些状态通常不需要掉电保持。
- 中间逻辑过渡: 在一个复杂的梯级中,把中间计算结果暂存一下,方便后续逻辑使用。避免梯级过长过乱。
- 简单的单次触发/自锁逻辑: 配合 SET/RESET 或 自身触点实现。注意做好注释!
- 小范围、临时性数据交换: 如果只是两个FC之间传递一两个BOOL量,懒得建DB,可以用M点(但DB通常更规范)。
- 需要掉电保持怎么办? 大部分PLC允许将部分M区配置为掉电保持(Retentive)。在硬件组态或CPU属性里设置。但资源有限,别滥用!
- 混淆乱用的后果:
- 当DB区用,存重要数据: 默认掉电就丢!设备重启后重要状态没了,流程乱套。
- 当I/O区用: 试图用M点直接映射外部IO?完全错误!M点和物理世界没半毛钱关系!
- 滥用全局M点: 程序大了之后,成百上千个无规律的M点散落在各处,谁也搞不清哪个M点是干嘛的、谁在改它。“屎山”代码的温床!
- 用MW/MD存重要数据: 位寻址为主,用字/双字访问需注意字节顺序,且容易受位操作影响(比如你操作了 M10.0,可能意外改了 MW10 的值!)。
三、 DB区 (Data Block Area):程序的“档案柜”与“结构化基石” - 强大且持久!
- 本质是什么?
- PLC CPU内存里 划出来的结构化数据存储区域。像一个自定义的、带标签的表格。
- 核心特点:
- 结构化 (Structured): 最大优势! 你可以像定义结构体(Struct)一样,在DB里定义各种有明确名称、类型、甚至注释的变量。例如:
- 然后在DB里创建这个结构的实例:Motor1 : Motor_Struct; Motor2 : Motor_Struct;
- 类型安全: 定义变量时指定了类型 (BOOL, INT, REAL, TIME, 甚至自定义结构),编译器会检查类型匹配,减少错误。
- 强大的访问控制: 可以设置变量的访问权限(如只读),通过优化的符号寻址访问 ("DB_MotorData".Motor1.ActualSpeed),清晰无比!比 MD100 这种天书强万倍!
- 灵活的掉电保持: 可以针对整个DB或DB内的单个变量设置是否掉电保持 (Retain)。资源管理更精细。
- 实例化与复用 (尤其FB): 功能块(FB)必须绑定一个背景DB(Instance DB)! FB的静态变量(STAT)就存在它的背景DB里。调用多次FB就生成多个背景DB,数据隔离。这是模块化、复用化的核心!
- 数据共享: 全局DB可以被程序任何地方访问(需注意互斥)。背景DB主要服务于其绑定的FB实例。
- 应该怎么用?(最佳实践)
- 存储设备/工艺参数: 速度、压力、温度设定值;配方数据;生产计数等。这些通常需要掉电保持!
- 封装设备状态和接口: 为电机、阀门、气缸等设备创建结构化的数据块 (DB或UDT),包含其所有命令、状态、参数。程序里操作 "DB_Valve".V101.OpenCmd 比用分散的 M100.1 清晰一万倍!
- 功能块(FB)的背景数据: 必须用! FB的所有 STAT 变量、INOUT 接口变量都“住”在它的背景DB里。调用FB时指定一个背景DB。
- 模块间数据交互: 需要跨FC/FB共享数据?定义一个接口明确的全局DB (或通过FB的INOUT参数传递),避免直接用M点全局乱飞!
- 复杂数据类型: 存储数组(ARRAY)、字符串(STRING)、时间日期等,非DB莫属。
- 混淆乱用的后果:
- 该用DB却用M区: 导致数据无结构、无类型、易丢失、难维护。程序规模稍大就失控。
- 背景DB使用不当: 给同一个FB的不同实例用了同一个背景DB?数据打架!或者FB该用背景DB却用了共享DB/全局M?失去封装性!
- 全局DB滥用: 不加控制地随意读写全局DB,造成数据污染,程序耦合度高。
- 忽略符号寻址: 还用绝对地址访问DB变量 (DB10.DBD24),失去了结构化的意义,可读性差,修改容易出错。