西门子PLC S7-SCL等编程资料收集
OB主程序,FB功能块【可用静态变量】,FC函数块,DB存储块
***OB在拖拽调用FB时自动生成的DB会自动映射保存FB中的静态变量,为好维护可定义一个全局DB所有模块可访问***
***PLC的输入输出端口需要格外供电,有源输出本身带电,无源输出只是通断***
***SCL代码内部写 VAR_TEMP / VAR_INPUT / VAR_OUTPUT / VAR_IN_OUT / VAR_STAT … END_VAR,1200 全部不支持,所有局部变量强制在块上方接口表格定义,Constant是常量都是用加#访问,用拖拽FC、FB到OB实现调用***
临时变量、输入变量、输出变量、常量直接在OB、FB、FC中的接口定义,PLC的变量是为了能方便访问I Q M存储区,DB是静态存储类似硬盘存储
借助AI咨询硬件相关问题如:PNP传感器信号线是输出正还是负
西门子(博途)以及绝大多数主流品牌 PLC,可以全程只用文本脚本语言完成整套工程,不需要梯形图;但它不是 Python、JS 这种通用脚本,而是 IEC‑61131‑3 标准的结构化文本**(西门子叫 SCL,其余品牌统称 ST)
***PLC的输入输出端口需要格外供电***
***继电器输出都只是通断,并不供电,需外部供电。晶体管(接直流-输出区分PNP还是NPN型》又分)与晶闸管(接交流,COM一般接零线,端口通过负载接火线)有源输出带电***
晶体管输出带不动大功率设备时可使用-》中间继电器-》设备
传感器NPN = 漏型(Sink,灌入电流,COM口接+24V,IO口常态0V);传感器PNP = 源型(Source,流出电流,COM口接0V,IO口常态+24V),是否有电流通过IO作为是否有信号
不管是NPN还是PNP,只要传感器感应到物体(动作了),PLC程序里读到的就是“1”
无论NPN还是PNP传感器说白了就是通断信号,输入端口是外部传感器控制通断,输出端口是PLC控制通断
西门子全系普通晶体管出厂固定为 PNP‑源型输出,硬件不能切换成 NPN(漏型)
通俗解释(200‑SMART / S7‑1200 / S7‑1500)
1. 晶体管输出公共端子 1M 接 0V、1L + 接 DC24V 正极
2. Q 点导通之后,输出端子直接送出 +24V 高电平(标准 PNP 源型)
西门子直流输入是双向光耦,可以自由切换 NPN / PNP,只靠公共端子 1M 接线决定



***其实 FB 在 OB 里调用也是循环调用,FB 本身需要保存自己当前状态,注意规划如何保持状态,这也是 FB‑Static(VAR 静态区)最根本的价值***
***FB 自己 Input/Output/InOut/Static/Temp `a` 或者 `#a`✅ 可选,加不加都行***
QW112 拆开:Q‑W‑112,112是字节偏移,取W也就是取字(两个字节)
1. **Q** → Output,**输出存储区**
2. **W** → Word,**字,16 位,占 2 个字节**
3. **112 → 【起始字节编号 / 字节偏移号】**
那么 Q0.0 呢为何有 0.0
Q 0 . 0
↑ ↑ ↑
输出区 字节号.位号
- `Q`:输出过程映像区
- 第一个数字 `0`:**字节编号(字节地址)**
- 小数点 `.` 用来分隔字节与位
- 第二个数字 `0`:**该字节内部的位编号**


西门子PLC SCL视频教程
西门子SCL编程手册
博途软件仿真
西门子博途‑SCL 全部变量区域完整总结
关于SCL脚本里变量加#问题
西门子 PLC 的 FB 和 DB 的关系为何调用 FB 必须要有 DB 参与
DB 里为何还有 input output inout 不是应该只是 static 吗? 另外自动分配的 DB 是自动保存 FB 的 static 变量吗?还是需要手动指定
SCL里不同进制数据写法
物理端口对应I、Q
SCL中静态变量,临时变量
PCL 变量表和 FB 里的 static 变量 临时变量区别
西门子博途 SCL 最常用语句总结(S7‑1200/1500)
西门子常用的步进电机,串口通讯,网络通讯,模拟量采集与输出,脉冲输出 ,伺服控制 之类的用 SCL 代码如何实现
既然 static 变量是可保存上一次状态,那么他的初始值怎么定呢
哪些能在DB中定义哪些不能,带逻辑代码的不能,直视单纯的结构可以,带方法的类不行
OB 如何保持上一次循环运行的状态
如何访问一个 FB 里的 static 变量
DB 存取区和 M 存储的区别,他们分别在那种场合实用
PLC程序归化
PLC FB函数的调用注意
西门子PLC 模拟量的换算函数
PCL变量实际工程规划
PLC各种存储区
西门子SCL参数类型转换
西门子WHILE DO
西门子SCL变量存储位置
西门子PLC 创建多路分支语句
如何设置S7-1200的掉电数据保持功能
西门子加减计数器
西门子CTD减计数器
西门子增计数器CTU
西门子TOF关短延迟定时器
西门子TON延迟定时器
寄存器地址意义
PLC 中M100.1、MB100、MW100、MD100的含义
浅析西门子S7-200PLC各数据存储区的含义
西门子PLC编程,存储区域 I Q M L D分别是什么意思
西门子SCL 生成脉冲TP定时器
STEP7中OB,FB,FC 和DB块有什么区别?相当于200系列中什么块
西门子PLC SCL语言开发学习笔记(二)
西门子PLC-1200 SCL语言开发学习笔记 (一)
西门子plc s7-200 中I、Q、M、SM、T、C、V、S、L分别指什么?
西门子PLC学习一
西门子 PLC 的 FB 和 DB 的关系为何调用 FB 必须要有 DB 参与
# 大白话讲清楚 FB、DB,以及为什么调用 FB 就必须生成背景 DB
先把最核心结论放在前面:
FB = 带独立私人数据房间的功能块;背景 DB 就是这个 FB 专属的私人房间。**
FC 没有私人房间,临时变量用完就丢;FB 要求你必须给它分配一间 DB 屋子来存放它自己的数据。
## 1、先分清 4 个角色
- **FC:功能**,没有自己专属的数据存储空间。
- **FB:功能块**,**自带要求:必须有一块独立的存储区保存它的静态数据 (STAT)**。
- **DB:数据块**,一块内存容器,用来存变量。
- **背景 DB (Instance‑DB)**:专门分配给某一个 FB、专供这个 FB 使用的 DB。
## 2、FB 里面有一种特殊变量:静态变量 STAT
在 FB 的接口区你会看到 4 类变量:

👉 **STAT 变量不会在 FB 执行完之后清零!会一直保存上次运行结果**
举个例子:你用 FB 做一个计数器。
你需要记住上次计到多少了。这个计数值,就要放到 `STAT`。
而 STAT 数据**唯一存放地点,就是背景 DB**。
## 3、调用 FB 的时候,为什么必须指定背景 DB?
你拖拽 FB 到网络上,博途一定会弹出窗口让你生成一个背景数据块。
原因:
1. FB 在设计的时候就定义好了:我需要一块内存用来存放我的静态变量 STAT。
2. PLC 运行时,这个 FB 实例所有的 STAT、还有 FB 接口参数副本,全部存在你指定的那个背景 DB 里面。
3. **没有 DB,FB 就没有地方存放它的记忆数据,PLC 根本无法运行这个 FB。**
### 一个生活化类比
- FB = 一份 “租房合同模板”
- 背景 DB = 一间实际的房子
你光拿着合同模板 (FB),不去租一间房子 (DB),你就没有地方住。
**每调用一次 FB,就相当于租一间独立的房子。**
如果你调用同一个 FB 两次,分配两个不同的背景 DB:就等于两份一模一样的合同,租了两间互不干扰的房子。两个 FB 实例的数据完全独立,互不影响!
FB_计数器, DB1 //一号计数器
FB_计数器, DB2 //二号计数器
DB1 里面存计数器 1 的当前数值;DB2 存计数器 2 的,互不打架。
## 4、FC 对比,反衬 FB 为什么不需要绑定 DB
FC 没有 STAT 静态变量,它没有需要长期记住的数据。
FC 运行时,临时变量 (TEMP) 放在**L 堆栈 (临时本地数据栈)**。FC 执行完毕,L 栈数据直接清空,什么记忆都不留。
所以调用 FC,**不需要任何 DB**。
## 5、两种背景 DB 类型(博途选项)
### ① 优化的块访问(默认推荐)
背景 DB 和 FB 接口绑定;你在 FB 改了接口,DB 自动跟着更新。看不到绝对偏移地址。
### ② 非优化(标准 DB,带偏移地址)
DB 里面变量有固定偏移,能按绝对地址寻址。
## 6、最常见误区澄清
### 误区
1:DB 只能给 FB 用?
❌不对。
有**全局 DB**,不属于任何 FB,可以随便存项目的公共数据,和 FB 没关系。
只有**背景 DB**才属于某个 FB 实例。
### 误区 2:FB 里面所有变量都放在背景 DB?
IN,OUT,IN_OUT:实际数据可以来自外部;
但是**FB 会在背景 DB 中生成一份副本**;
STAT:强制放在背景 DB;
TEMP 临时变量:放在 L 堆栈,**不进背景 DB!**
## 7、一句话终极总结
FC:用完就忘 → 不需要房子 (DB)
FB:需要记住历史状态(STAT 静态变量)→ 必须分配一间专属房子(背景 DB)存放记忆,所以调用 FB 必须指定 DB。
DB 里为何还有 input output inout 不是应该只是 static 吗? 另外自动分配的 DB 是自动保存 FB 的 static 变量吗?还是需要手动指定
# 两个核心问题一次性讲透
## 问题 1:背景 DB 里面为什么会有 IN、OUT、INOUT?不是只存 Static 吗?
### 接口区 5 种变量存放位置(铁律)

### 大白话解释
FB 的背景 DB 本质是**这个 FB 实例完整的 “快照容器”**,而不是仅仅一块给 STAT 用的小仓库。
1. **IN/OUT/INOUT 在 DB 里存一份本地副本**
每次调用 FB 时,外部实参的值,会先**拷贝一份**放进背景 DB 对应的 IN 变量;FB 运算结束后,OUT 结果又写回到 DB 副本。
即使你这一轮调用结束,你依然可以随时去背景 DB 读取上次输入值、上次输出值。
举例子:
Motor_FB, Motor_DB
// Motor_DB里面可以看到 Motor_FB.Start(IN)、Motor_FB.Run(OUT)、Motor_FB.Counter(STAT)
你哪怕不在 OB1 调用它,你也可以直接读 `Motor_DB.Run`,拿到上一次 FB 运行完之后的输出状态。
FC 就完全不一样:FC 没有副本,IN/OUT 用完就丢;你不调用 FC,就看不到上次参数值。
### 一个非常容易混淆关键点
IN_OUT 有两份数据通路:
① 背景 DB 里面存副本;
② 同时它还**引用外部变量的内存地址**。
## 问题 2:拖拽 FB 自动生成的 DB,会不会自动保存 STAT 变量?需要手动指定吗?
✅ **完全自动,不需要你手动添加任何 STAT 变量到 DB**
1. 你在 FB 接口区定义好 IN/OUT/INOUT/STAT
2. OB1 拖拽 FB,弹出窗口新建背景 DB
3. 博途**自动根据 FB 接口定义,生成完整 DB 结构**,STAT 变量自动出现在 DB 里面,不需要手动往 DB 里敲变量名。
FB 改了接口(新增 STAT 变量)→编译之后背景 DB 结构自动同步更新(优化 DB 默认)。
⚠️ **数据保存行为(记忆功能)是自动生效的**:
STAT 变量的值,FB 执行完毕之后**不会清零**,一直保存在背景 DB;下次再调用 FB,STAT 读取的就是上次留下的值。**不需要额外写任何代码保存**。
### 唯一你可以手动设置的选项:掉电保持(Retain)
默认 STAT 变量**断电会丢失**。
如果你想要 PLC 断电重启后 STAT 数值还在:
打开 FB 接口区 →选中 STAT 变量 →属性勾选**保持性 (Retain)**。
这个属于可选配置,**不是必须**。
# 最常见误区澄清
### 误区 1:背景 DB = 只放 STAT
❌错。STAT 只是 FB**需要 DB 的根本原因**,但 DB 实际装了 IN+OUT+INOUT+STAT 全套接口副本。TEMP 永远不在 DB。
### 误区 2:IN 参数的值永远等于外部引脚的值
❌错。DB 中 IN 是**拷贝快照**。外部变量后续改变,不会自动刷新 DB 里面 IN 副本;只有下一次 FB 被调用时才会重新拷贝新值进来。
# SCL 示例直观感受
//调用FB,Motor_DB自动包含 Start(IN), Run(OUT), Count(STAT)
#MotorInstance(
Start := "StartButton",
Run => "MotorOutput"
);
//不调用FB,直接读取上次运行状态
"Motor_DB".Run;
"Motor_DB".Count;
```
PLC程序归化
1,首选把需要的端口和存储变量定义

2添加.数据类型

3建立全局DB块

4.添加FC块也就是变量和全局DB块最映射

5.建立函数FB块

6.主程序调用

又一个FB函数块

PLC FB函数的调用注意
PLC FB模块调用它的参数是带参数名的输出是=>
入
"启动"(CLK:=12,OUT=>Q0.1)
西门子PLC 模拟量的换算函数
NORM_X 标准化 输入:最大值 最小值 输入值 输出0-1占比值 0-10V 0-100N
模拟量PCL 0-10V对应0-27648
SCALE_X 缩放
减速阀 0-1V 速度0-100%
压力传感器0-10 压力范围0-1000W
处理模拟量 NORM——X SCALE_X
处理工业流程 CASE ...OF
PCL变量实际工程规划
建立所用到的端口变量

添加PLC数据类型

添加DB块
建立FC块映射变量
建立FB

实际使用函数

PLC各种存储区
(一)数字量输入和输出映象区
1.输入映象寄存器(数字量输入映象区)(I)
(1)按“位”方式:从I0.0~I15.7,共有128点
(2)按“字节”方式:从IB0~IB15,共有16个字节
(3)按“字”方式:从IW0~IW14,共有8个字
(4)按“双字”方式:从ID0~ID12,共有4个双字
2.输出映像寄存器(Q)
(1)按“位”方式:从Q0.0~I15.7,共有128点
(2)按“字节”方式:从QB0~QB15,共有16个字节
(3)按“字”方式:从QW0~QW14,共有8个字
(4)按“双字”方式:从QD0~QD12,共有4个双字
(二)模拟量输入映象区和输出映象区
1.模拟量输入映象区(AI区)
从AIW0~AIW30,共有16个字,总共允许有16路模拟量输入。
2.模拟量输出映象区(AQ区)
从AQW0~AQW30,共有16个字,总共允许有16路模拟量输出。
(三)变量存储器(V)(相当于内辅继电器)
(1)按“位”方式:从V0.0~I5119.7,共有40960点。
(2)按“字节”方式:从VB0~VB5119,共有5120个字节
(3)按“字”方式:从VW0~VW5118,共有2560个字
(4)按“双字”方式:从VD0~VD5116,共有1280个双字
(四)位存储器(M)区
(1)按“位”方式:从M0.0~M31.7,共有256点。
(2)按“字节”方式:从MB0~MB31,共有32个字节
(3)按“字”方式:从MW0~MW30,共有16个字
(4)按“双字”方式:从MD0~MD28,共有8个双字
(五)顺序控制继电器区(S)
(1)按“位”方式:从S0.0~S31.7,共有256点。
(2)按“字节”方式:从SB0~SB31,共有32个字节
(3)按“字”方式:从SW0~SW30,共有16个字
(4)按“双字”方式:从SD0~SD28,共有8个双字
(六)局部存储器区(L)(相当于内辅继电器)
(1)按“位”方式:从L0.0~L63.7,共有512点。
(2)按“字节”方式:从LB0~LB63,共有64个字节
(3)按“字”方式:从LW0~LW62,共有32个字
(4)按“双字”方式:从LD0~LD60,共有16个双字
(七)定时器存储器区(T)
T0、T1、……、T255
S7-200有256个定时器。
(八)计数器存储器区(C)
计数器的编号:
C0、C1、……、C255
(九)高速计数器区(HSC)
高速计数器的编号为:HSC0、HSC1、……、HSC5。
(十)累加器区(AC)
S7-200CPU提供了4个32位累加器(AC0、AC1、AC2、AC3)。
西门子SCL参数类型转换
将参数从一个类型转换为另一种类型
REAL_TO_INT("参数")
"参数二":=INT_TO_DINT("参数一");
浮点数取整
<目标值>=ROUND<源值> 取就近的偶数
特别注意例子如21.3 它位于21 和 22 之间 那么取偶数就是22
CEIL 浮点数向上取整 默认为证书
CEIL("Tag_10")
CEIL_REAL("Tag_10") tag_10向上整后再变为浮点型 如 Tag10=0.5 那么向上取整为1 再转换为浮点那么是 1.00
FLOOR 浮点数向下取整
FLOOR("Tag_10")
FLOOR_REAL("Tag_10") tag_10向上整后再变为浮点型 如 Tag10=0.5 那么向下取整为0 再转换为浮点那么是0.00
TRUNC 截尾取整
NORM_X标准化
西门子WHILE DO
WHILE<条件> DO <执行>
END WHILE;
中间退出
EXIT
西门子SCL变量存储位置
变量根据类型选择存储位置

西门子PLC 创建多路分支语句
CASE <表达式> OF
<常量1>:<指令1>
<常量2>:<指令2>
<常量3>:<指令3>
<常量4>:<指令4>
。。。
ELSE <指令0>
END CASE
常数可以是范围 5..7
或数组
如何设置S7-1200的掉电数据保持功能
首先我们认识下S7-1200系列PLC的的内部存储区的构成:S7-1200的内部存储区包括:装载存储区(load memory)、工作存储区(work memory)和保持存储区(retain memory)。
?装载存储区(load memory):
是非易失性存储区,相当于计算机的硬盘。装载存储区用来存放用户项目文件(用户程序、数据和组态)。在不插存储卡的情况下,用户使用STEP
7软件下载项目即是下载到装载存储区。除了用户程序和数据,项目中的符号名和注释也会被下载到装载存储区中,极大地方便了用户的后期调试和维护。
?工作存储区(work memory):
是易失性存储区,相当于计算机的内存。CPU在执行用户程序时会将一些项目内容从装载存储区复制到工作存储区。断电后工作存储区中的内容会丢失,且不能被扩展。
?保持储区(retain memory):
非易失性存储区,CPU检测到电压降低到一定程度时,会将工作存储区中设置成掉电保持的数据保存到保持存储区中。
S7-1200系列PLC支持将位存储区(bit memory)、背景数据块(Insance-DB)及全局数据块(Global-DB)的数据设置为保持性(retentive)。
位存储区(bit memory)的设置总是从第0个字节开始,设置的方法如下:
在【PLC标签(PLC tags)】中,点击“电池”图标,如下图:
弹出如下对话框:
本例中我们把从MB0开始的100个字节设置成断电保持,如下图:
然后我们会发现我们之前建立的变量“Tag_2”,“Tag_3”和“Tag_4”的保持属性被勾选了,如下图:
此时位存储区的MB0~MB99已经具有断电保持的属性。
对于全局数据块而言,可以选择其中的某个(些)数据将其属性设置为【保持性(retain)】,这样就具有断电保持功能。如下图:
背景数据块的保持性设置,是在FB的参数输入中选择是否具有保持性,如下图:
S7-1200系列PLC的保持存储区的大小为10 KB。所有已经设置的、具有保持属性的存储区(位存储区、背景数据块和全局数据块)的大小的总和,不能超过10 KB。
怎样查看保持存储区的使用情况呢?
在离线的情况下,可以通过TIA-Portal环境下项目树的【程序信息(Program info)】查看当前存储区的使用情况,如下图:
在线的情况下可以通过诊断-存储区(Diagnostic-Memory)查看当前CPU保持存储区的使用情况,如下图:

西门子加减计数器
“IEC_Counter_0_DB_2".CTUD(
CU:=<操作数>,//加计数输入端
CD:=<操作数>,//减计数输入端
R:=<操作数>,复位输入端
LD=<操作数>,//装载预设值输入端
PV:=<操作数据>//计数器预设值
QU=><操作数> //加计数器运行状态
QD=><操作数> //减计数器运行状态
CV=> <操作数> //计数器当前计数值
| 参数说明 | 声明 | 数据类型 | 存储区 | 说明 |
| CU | Input | BOOL | I Q M D L | 加计数输入端 |
| CD | Input | BOOL | I Q M D L | 减计数器输入端 |
| R | Input | BOOL | I Q M D L | 复位输入端 |
| LD | Input | BOOL | I Q M D L P | 装载预设值与复位输入值 |
| PV | Input | 整数 | I Q M D L P | 计数器预设值 |
| QU | Output | BOOL | I Q M D L | 加计数器状态 |
| QD | Output | BOOL | I Q M D L | 减计数器状态 |
| CV | Output | 整数 CHAR WCHAR DATE | I Q M D L P | 当前计数器的值 |
表示CD来一次高电平计数-1 ,CU 来一次高电平计数+1
西门子CTD减计数器
“IEC_Counter_0_DB_2".CTD(
CD:=<操作数>,//计数输入端
LD=<操作数>,//装载预设值与复位输入值
PV:=<操作数据>//计数器预设值
Q=><操作数> //技术器运行状态
CV=> <操作数> //计数器当前计数值
| 参数说明 | 声明 | 数据类型 | 存储区 | 说明 |
| CD | Input | BOOL | I Q M D L | 计数输入 |
| LD | Input | BOOL | I Q M D L P | 装载预设值与复位输入值 |
| PV | Input | 整数 | I Q M D L P | 计数器预设值 |
| Q | Output | BOOL | I Q M D L | 计数器状态 |
| CV | Output | 整数 CHAR WCHAR DATE | I Q M D L P | 当前计数器的值 |
表示CD来一次高电平计数-1
西门子增计数器CTU
“IEC_Counter_0_DB_2".CTU(
CU:=<操作数>,//计数输入端
R:=<操作数>,//复位操作数
PV:=<操作数据>//计数器预设值
Q=><操作数> //技术器运行状态
CV=> <操作数> //计数器当前计数值
| 参数说明 | 声明 | 数据类型 | 存储区 | 说明 |
| CU | Input | BOOL | I Q M D L | 计数输入 |
| R | Input | BOOL | I Q M D L P | 复位输入 |
| PV | Input | 整数 | I Q M D L P | 置位输出Q的目标值 |
| Q | Output | BOOL | I Q M D L | 计数器状态 |
| CV | Output | 整数 CHAR WCHAR DATE | I Q M D L P | 当前计数器的值 |
表示CU来一次高电平计数+1
西门子TOF关短延迟定时器
“IEC_Timer_0_DB_2".TOF(IN:="关闭信号", //定时器启动输入端子
PT:=T#5S, //定时设置值
Q=>"状态位", //当定时器时间到达时Q端置0
ET=>"当前值") ;//定时器当前值存储
IN一直按下后ET从0开始增加当ET大于等于PT时候Q 置位1,如IN按下时间小于5秒时时不触发Q
西门子TON延迟定时器
“IEC_Timer_0_DB_2".TON(IN:="启动信号",PT:=T#5S,Q=>"状态位",ET=>"当前值")
IN一直按下后ET从0开始增加当ET大于等于PT时候Q 置位1,如IN按下时间小于5秒时时不户触发Q
寄存器地址意义
寄存器地址是方便我们写的程序取数据和写数据,也便于CPU去读写数据执行相应操作
PLC 中M100.1、MB100、MW100、MD100的含义(符号后的数字始终是字节为单位)
M100.1是位状态,为M区域(辅助继电器)的第100个字节的第1位。
MB100是字节类型地址,为M区域(辅助继电器)的第100个字节,一个字节8个位,所以MB100就包含了M100.0~M100.7。
MW100是字类型地址,为M区域(辅助继电器)的第100个字节,一个字2个字节16个位,所以MW100就包含了MB100、MB101,即包含M100.0~M100.7、M101.0~M100.7这16个位。
MD100是字节类型地址,为M区域(辅助继电器)的第100个字节,一个双字2个字4个字节32个位,所以MD100就包含了MW100~MW102。
即包括MB100、MB101、MB102、MB103这4个字节,也包括M100.0~M100.7、M101.0~M101.7、M102.0~M102.7、M103.0~M103.7。

浅析西门子S7-200PLC各数据存储区的含义
今日咱们了解一下西门子S7-200PLC的接线与S7-200数据存储区:
首要,咱们看一下怎么接线:

S7-200数据存储区:
1. 输入/输出映像寄存器:S7-200 PLC编址规模(I0.0~I15.7)。 输入映像寄存器(该区域能够按位操作又称输入继电器)输入继电器线圈由外部信号驱动,常开触点和常闭触点供用户编程运用。
输出映像寄存器:S7-200 PLC编址规模(Q0.0~Q15.7)。输出映像寄存器(又称输出继电器)是用来将PLC的输出信号传递给负载,线圈用程序指令驱动。PLC的每一个I/O点都是一个断定的物理点。CPU 224主机有I0.0~I0.7,I1.0~I1.5共14个数字量输入端点,Q0.0~Q0.7、Q1.0、Q1.1共10个数字量输出端点。
2.变量存储器V
用以存储运算的中心成果和其它数据。CPU 224有VB0.0~VB5119.7的5K存储字节。可按位、字节、字或双字运用。
3. 内部标志位(M)存储区
M作为操控继电器(又称中心继电器),用来存储中心操作数或其它操控信息。
S7-200 PLC编址规模M0.0~M31.7,能够按位、字节、字或双字来存取存储区的数据。
4. 次序操控继电器(S)存储区
S又称状况元件,以完成次序操控和步进操控。
S7-200 PLC编址规模S0.0~S31.7,能够按位、字节、字或双字来存取数据。
5.特殊标志位(SM)存储器
① SMB0为状况位字节,在每次扫描循环结尾由S7-200 CPU更新,界说如下:
SM0.0 RUN状况监控,PLC在运转RUN状况,该位始终为1。
SM0.1 初次扫描时为1,PLC由STOP转为RUN状况时,ON(1态)一个扫描周期,用
于程序的初始化。
SM0.2 当RAM中数据丢失时,ON一个扫描周期,用于犯错处理。
SM0.3 PLC上电进入RUN方法,ON一个扫描周期。
SM0.4 分脉冲,该位输出一个占空比为50%的分时钟脉冲。用作时刻基准或简易延时。
SM0.5 秒脉冲,该位输出一个占空比为50%的秒时钟脉冲。可用作时刻基准。
SM0.6 扫描时钟,一个扫描周期为ON(高电平),另一为OFF(低电平)循环替换。
SM0.7 工作方法开关方位指示,0为TERM方位,1为RUN方位。为1时,使自由端
通讯方法有用。
② SMB1为指令状况位字节,常用于表及数学操作,部分位界说如下:
SM1.0 零标志,运算成果为0时,该方位1。
SM1.1 溢出标志,运算成果溢出或查出不合法数值时,该方位1 。
SM1.2 负数标志,数学运算成果为负时,该位为1。
6. 部分存储器(L)
S7-200有64个字节的部分存储器,编址规模LB0.0~LB63.7,其中60个字节能够用作暂时存储器或者给子程序传递参数,最终4个字节为系统保留字节。
7. 定时器(相当于时刻继电器)
S7-200 CPU中的定时器是对内部时钟累计时刻增量的设备,用于时刻操控。编址规模T0~T255(22X);T0~T127(21X)。
8. 计数器
计数器首要用来累计输入脉冲个数。有16位预置值和当时值寄存器各一个,以及1位状况位,当时值寄存器用以累计脉冲个数,计数器当时值大于或等于预置值时,状况方位1。 S7-200 CPU供给有三种类型的计数器,增计数、减计数、增/减计数。编址规模C0~C255(22X),C0~C127(21X)。
9. 模拟量输入/输出映像寄存器(AI/AQ)
S7-200的模拟量输入电路将外部输入的模拟量(如温度、电压)等转换成1个字长(16位)的数字量,存入模拟量输入映像寄存器区域。
AI编址规模AIW0,AIW2,……AIW62,开始地址界说为偶数字节地址,共有32个模拟量输入点。
S7-200模拟量输出电路用来将模拟量输出映像寄存器区域的1个字长(16位)数字值转换为模拟电流或电压输输出。
AQ编址规模AQW0,AQW2,……AQW62,开始地址也采用偶数字节地址,共有32个模拟量输出点
10. 累加器(AC)
累加器是用来暂存数据, S7-200 PLC供给了4个32位累加器AC0~AC3。累加器支持以字节(B)、字(W)和双字(D)的存取。。
11. 高速计数器(HC)
CPU 22X供给了6个高速计数器HC0、HC1……HC5 (每个计数器最高频率为30KHz)用来累计比CPU扫描速率更快的事件。高速计数器的当时值为双字长的符号整数。
西门子PLC编程,存储区域 I Q M L D分别是什么意思
I:输入地址,对应输入模块通道
q:输出地址,对应输出模块通道
M:辅助寄存器地址
L:局部变量地址,相当于程序中的内部变量地址
D:应该对应的书DB数据块地址。
西门子SCL 生成脉冲TP定时器
“IEC_Timer_0_DB_2".TP(IN:="启动信号",PT:=T#10S,Q=>"状态位",ET=>"当前值")
表示IN为真时那么Q会立即变为1 同时开始开始记时,ET输出当前值,当 ET=PT时Q状态变为0
| 参数说明 | 声明 | 数据类型 | 存储区 | 说明 |
| IN | Input | BOOL | I Q M D L P | 启动输入 |
| PT | Input | TIME | I Q M D L P | 定时时间设定值,PT参数的值必须为正数 |
| Q | Output | BOOL | I Q M D L P | 在PT持续时间内保持置位状态的操作数 |
| ET | Output | TIME | I Q M D L P | 当前定时器的值 |
实际代码
IF "开门按钮"=1 THEN
"安全门电磁阀":=1;
END IF;
IF"关门按钮"=1 THEN
"安全门面电磁阀":=0;
END IF;
"IEC_Timer_0_DB.TP(IN:="开门按钮"=1 OR "关门按钮"=1,
PT:T#6S,
Q=>"定时器状态信号",
ET=>"定时器当前值);
//安全指示灯控制程序
“安全门指示灯:="Clock_2Hz" AND "定时器状态信号"
STEP7中OB,FB,FC 和DB块有什么区别?相当于200系列中什么块
在OB中可以调用FC,在FC中可以调用FB.FB再调用DB,OB是主程序,FC和FB是用来实现控制功能的子程序。 DB是数据块。
西门子PLC SCL语言开发学习笔记(二)
今天来讲下scl两个关键的点
一、按键事件
比如地址I0.0是某个按钮的状态,他只有True和False两个状态,所以我们要获得按下事件需要我们自己模拟。
#KeyPress := "Btn1" AND NOT #LastKey;
#LastKey := "Btn1";
这样通过对#KeyPress判断是否为True即可获得一次点击事件,相当于 |P|
把上一次的存起来,然后判断按下,如果你想把按键放在松开时也可以。
因为大多数事情都是用按键触发的,掌握这一点才能开始跑代码。
二、定时器
PLC在梯形图中使用定时器很困难,比如TON 5秒,需要输入一直保持True,持续5s后输出变为True,TOF则需要输入从True变成False,然后持续5秒后输出False,自带各种逻辑,一旦碰到复杂的条件判断头都晕了,我是只能做到间隔时间打开几个灯,但要做另一个按钮还能间隔关上就不会了。
我们写程序的只需要一个计时器就好了,不需要你带这么多乱七八糟的逻辑,我们自己决定到时间了干嘛就好了。
1) 使用TON TORN
使用TON很简单,让一个变量保持True,然后判断输出是否时间到了,时间到了既可以做事情了,同时让输入变为False。
TORN则还含有Reset功能,他能累计计时,同时能取到计时了多久,下面这个程序展示了一开始就给TimerStart设置为True,开始计时并变灯的情况。
下面是代码块,不要忘了这是被循环不断执行的,大概功能是第五秒打开一个灯,第10秒再打开一个,到15秒重置, 为了代码简单理解就不加各种为了严谨的判断了
#TimerStart := TRUE; // 随便打开一下,就不写事件了
"IEC_Timer_0_DB".TONR(IN := #TimerStart,
PT := T#15S, // 计时15秒
Q => #TmpOut, //计时完毕,我们不去用这个
R:= #Reset,
ET => #Time1);// ET为取出计时了多久
IF #Reset THEN
#Reset := FALSE;
END_IF;
IF #Time1 >= T#5s THEN
"Light1" := TRUE;
END_IF;
IF #Time1 >= T#10s THEN
"Light2" := TRUE;
END_IF;
IF #TmpOut THEN
"Light1" := FALSE;
"Light2" := FALSE;
#Reset := TRUE;
END_IF;
2)时钟存储器位
首先右键PLC进入属性
找到时钟存储器位
我们把地址放到100那里去,这样子这几个存储器里,就会有震荡,不断在True False之间来回变化,比图1HZ会在一秒的开始变为True,到一半变为False,再下一秒开始变True,通过他的变化就能计时了
下面这个程序,再按下按钮1后亮一个灯,过5秒再亮一个,再过5秒再亮一个,关闭时也是间隔灭灯,不考虑打开过程中关闭等情况。
IF #TT <> "Clock_1Hz" THEN //一个不断根据震荡时间增加的Count 计数器
#Count += 1;
END_IF;
#TT := "Clock_1Hz";
#KeyPress := "Btn1" AND NOT #LastKey;
#LastKey := "Btn1";
IF #KeyPress AND NOT "Light1" THEN // 开灯事件
#TurnOn := TRUE;
#TurnOff := FALSE;
#Count := 0; //重置计数器
END_IF;
IF #KeyPress AND "Light1" THEN // 关灯事件
#TurnOn := FALSE;
#TurnOff := TRUE;
#Count := 0;
END_IF;
IF #TurnOn THEN // 持续处理开灯
"Light1" := TRUE;
IF #Count >= 5 * 2 THEN
"Light2" := TRUE;
ELSIF #Count >= 20 THEN
"Light3" := TRUE;
END_IF;
END_IF;
IF #TurnOff THEN
"Light1" := FALSE;
IF #Count >= 10 THEN
"Light2" := FALSE;
ELSIF #Count >= 20 THEN
"Light3" := FALSE;
END_IF;
END_IF;
3) 时间函数
其实PLC里面本来也有很多时间函数,比如获取当前机器时间,比较时间差等,我们完全可以很灵活的去使用这些方法,变得和编程无异,这里就不介绍了
结语:我们可以识别按钮事件,然后获得了好用的计时器后,scl的逻辑处理剩下的就都是写代码了和调方法了,没有什么能难倒我们程序员了
SCL逻辑比梯形图更严谨明白,复杂逻辑控制更简单,是我们程序员的好帮手。
以后会继续挖掘SCL的写法,教大家SCL能代替梯形图的写法,做到是个程序员就无门槛
暂时告一段落
西门子PLC-1200 SCL语言开发学习笔记 (一)
一、简介和背景
PLC一般使用梯形图开发,但是梯形图适合电工使用而不是程序员使用,对我们来说开发困难,门槛高,幸好PLC的开发标准还带了类pascal的高级语言,在西门子这里叫SCL语言,这对于我们程序员来说门槛就很低了。
要开发好复杂PLC逻辑,梯形图困难重重,市场上要价颇高,而使用SCL语言则非常合适处理复杂逻辑以及运算。
二、新建SCL程序块

在博图软件的项目视图中,便有添加新快,双击

推荐使用FB模块,便于存放变量,语言选择SCL
三、变量的创建和访问
在打开FB模块后,上方就是变量定义区

Input则是输入参数,Output是返回值或者理解为C# out关键字的输出变量,InOut则是类似Ref的既输入又输出的,Static变量才是我们可以保留的变量,Temp 相当于方法内的局部变量,这次执行过后会被重设
我们的代码会被放在一个方法块内
不用担心用Static后每次调用使用这个变量会冲突,每个使用它的地方都会新建一套数据块副本,相当于实例化一个类,调用实例方法,并非完全静态的变量。
要写代码当然要会访问各个能访问的变量,
FB里面定义的变量用#开头来访问
访问地址用%开头,


变量表里的变量相当于全局静态变量
我们给一些地址起了名字,既可以用 %I0.0 也可以用Btn1,最终都会变成”Btn1”。
访问别的实例的DB块中的变量则使用DB快的名字在用 . 进行访问,比如第一张图中块的 "块_2_DB_1".BtnPress
四、变量类型
大部分的变量类型大家基本都认识了,比较搞得是时间类型,简单说下
Time 相当于TimeSpan,表示一段时间 比如 T#5s表示5秒,再比如 T#10h2m
LTime 长的Time,表达的时间更长
Date 日期
Date And Time DT类型,也就是datetime
Time Of Day TOD 就是时间部分了
博图的帮助中也提供了详细的解释,帮助文档也是比较全面的
结束:
下期讲解在梯形图中重要且复杂难用又难以组合的,定时器,在SCL中如何处理
西门子plc s7-200 中I、Q、M、SM、T、C、V、S、L分别指什么?
输入继电器 I
输出继电器Q
通用辅助继电器M
特殊继电器SM
变量存储器V
局部变量存储器L
顺序控制继电器S
定时器T
计数器C
模拟量输入映像寄存器AI
模拟量输出映像寄存器AQ
高速计数器HC
累加器AC
西门子PLC学习一
当你添加完模块后就可以在博途软件里选择上模块就可以在属性里看到地址,一般不用动地址,要用那个地址直接属性里去看
西门子 PLC 的地址的分配方式
根据不同的 PLC 配置情况确定 I/O 地址是 PLC 编程的前提与基础,程序中的地址必须与实际物理连接点一一对应,才能确保动作的正确执行。当选择了 PLC 之后,首先需要确定的是系统中各 I/O 点的绝对地址。在西门子 S7 系列 PLC 中 I/O 绝对地址的分配方式共有固定地址型、自动分配型、用定义型 3 种。实际所使用的方式决定于所采用的 PLC 的 CPU 型号、编程软件、软件版本、编程人员的选择等因素。
1.固定地址型
固定地址分配方式是一种对 PLC 安装机架上的每一个安装位置(插槽)都规定地址的分配方式。其特点如下:
①PLC 的每一个安装位置都按照该系列 PLC 全部模块中可能存在的最大 I/O 点数分配地址。
例如:S7-300 系列 I/O 模块中最大开关量输入/输出为32 点,因此,每一个安装位置都必须分配 32 点地址:如果实际安装的模块只有 16 点输入,那么剩余的 I/O 地址将不可以再作为物理输入点使用。
②对于输入或输出来说, I/O 地址是间断的,而且,在
输入与输出中不可以使用相同的二进制字节与位。
例如: S7-300 系列 I/O 模块的第 1 安装位中安装了 32点输入模块, 地址数据中的 0.0~3.7 就被该模块所占用, 地址固定为 I0.0 ~13.7; 即使第 2 安装位中安装了 32 点输出模块,其输出地址也只能是 Q4.O ~Q7.7 ,而不可以是 QO.O ~Q3.7 ,在实际编程时 QO.O ~Q3.7 就变成了不存在的输出。同样,如果在第 3 安装位中接着安装了 16 点输入模块,其地址将为 I8.0~19.7 ,在实际编程时 I4.0 ~17.7 就变成了不
存在的输入。
以上分配原则对模拟量模块同样适用。
2.自动分配型
自动地址分配方式是一种通过自动检测 PLC 所安装的实际模块,自动、连续分配地址的分配方式。其特点如下:
①PLC 的每一个安装位置的 I/O 点数量无规定, PLC 根据模块自动分配地址。
例如:当每一个安装位置安装了 32 点模块后, PLC 自动分配给该模块 0.0~3.7 的地址:如果实际安装的模块只有16 点输入,那么 PLC 自动分配给该模块的地址就成为 0.0 ~1.7。
②输入与输出的地址均从 0.0 起连续编排、自动识别,I/O 地址连续、有序。
例如: PLC 的第 1 安装位中安装了 32 点输入模块,地址为 I0.0 ~13.7; 当第 2 安装位中安装了 32 点输出模块后,其输出地址自动分配为 QO.O ~Q3.7 。同样,如果在第 3 安装位中接着安装了 16 点输入模块,其地址将为 I4.0 ~15.7。
I/O 地址中没有不存在的输入与输出。
以上分配原则对模拟量模块同样适用。
对于 S7-300 系列,由于生产时间、软件版本的不同,安装于 PLC 主机上的部分 I/O 模块, CPU 的地址分配可能会出现断续的情况, CPU 仍然按照最大开关量输入/输出进行地址分配,当使用 32 点以下模块时,多余的地址不可以再使用。但是, 、对于远程 I/O 单元,地址总是连续分配的。
3.用户设定型
用户设定型分配方式是一种可以通过编程软件进行任意定义的地址分配方式。其特点如下:
①PLC 的每一个安装位置的地址可以任意定义, I/O 点数量无规定,但同 - PLC 中不可以重复。 ’
例如:当每一个安装位置安装了 32 点输入模块后,用户可以分配给该模块 I0.0 ~13.7 的地址; 也可以分配其他任意地址,如 I8.0 ~I11.7 等。但在分配 I0.0 ~13.7 后,后续的同类模块中不可以再使用地址 I0.0 ~13.~。
②输入与输出的地址既可以是间断的,也可以不按照次序排列。
例如: PLC 的第 1 安装位中安装了 32 点输入模块,地址定义为 I8.0 ~111.7; 第 2 安装位中再安装 32 点输入模块,地址定义为 I0.0 ~13.7 ,这样的分配同样也允许。
以上分配原则对模拟量模块同样适用。
各地址和数据关系




西门子博途‑SCL 全部变量区域完整总结
S7‑1200/1500,区分 OB、FC、FB、DB,通俗易懂
先给一句核心总览:
- **OB:循环执行的任务入口,程序运行的容器**
- **FC:普通函数,执行一次性运算,不带专属记忆空间**
- **FB:带独立内存的功能块,可以保存自身状态,适合封装设备(穿梭车、传送带、电机)**
- **DB:全局数据仓库,存放所有设备参数、坐标、通讯数据、结构体**
PLC 系统 → OB(调度入口)→ 调用 FB / FC → 所有持久数据存放在 DB**
1. OB 是**总指挥、程序运行入口**,负责循环扫描、挨个调用子程序;
2. FB、FC 是被调用的功能子程序,用来拆分功能;
3. DB 是独立的数据仓库,所有公共数据、FB 实例全部存放于此,**DB 不会主动运行代码,只存数据**。
**VAR‑TEMP 临时变量扫描结束销毁;Static 静态变量永久保存;Input 输入、Output 输出、InOut 用于接口;全局变量放在 DB 块**
## 一、先分清四种接口变量(FB/FC 顶部接口区)
### 1. Input(输入变量 → VAR_INPUT)
- 作用:接收外部传进来的数据,只读,函数内部代码**不能改写它**
- 来源:硬件 IO、DB 数据、别的功能块传来的参数
- 例子:启动按钮、限位信号、目标位置、编码器数值
```
VAR_INPUT
Start:BOOL;
Target_Pos:DINT;
END_VAR
```
### 2. Output(输出变量 → VAR_OUTPUT)
- 作用:向外输出结果,可以读写;功能块执行完毕把状态传出
- 例子:电机输出、到位标志、故障代码
### 3. InOut(输入输出 → VAR_IN_OUT)
- 双向参数,可以读取、也可以修改原值;
适合坐标、计数、需要函数内部修改的外部变量,**传递的是地址而非拷贝数值**。
典型用途:小车当前位置、计数器数值
### 4. Static 静态变量(仅 FB 才有,FC 不存在 Static)
```
VAR_STATIC
Lock:BOOL;
Now_Position:DINT;
END_VAR
```
- FB 专属,**断电保持(可设置)、扫描周期之间数据永久留存**
- 自锁标记、小车坐标、计时数值、状态机状态全部放 Static
- 重点:**OB1 里面的本地静态变量同样属于 Static**
## 二、临时局部变量 VAR(TEMP 临时变量)
```
VAR
i:INT;
Distance:DINT;
END_VAR
```
1. 生命周期:**单次扫描周期用完直接清空,数值不会保存**
2. FC 内部所有中间计算变量只能使用 TEMP
3. 禁止拿 TEMP 做自锁、计数器、位置缓存,每次循环数值随机
4. 用途:循环 i、临时计算差值、中间过渡参数
通俗比方:TEMP 相当于草稿纸,一轮运算结束草稿丢掉;Static 是笔记本,可以一直存数据
## 三、DB‑数据块变量(全局变量,整个项目任意 OB、FB、FC 都可以读取)
### 1. 优化型 DB(1200/1500 默认)
变量名寻址,**没有固定偏移地址**,推荐 SCL 全程使用;
适合小车结构体、设备状态、上位机交互任务数据、IO 映射。
### 2. 非优化 DB(传统地址寻址)
可以直接按地址偏移访问,用于和上位机、Modbus、OPC 通讯。
DB 内变量类型:
- BOOL、INT、DINT、REAL、Array 数组、UDT 自定义结构体
穿梭车示例:小车坐标 X、Y、运行状态、故障码、目标货位全部存进 DB
## 四、OB、FC、FB 三者变量区域区别(重中之重)
### 1. OB(OB1 主循环)
接口区只有:
- Input、Output、InOut(极少使用)
- **Static:OB 静态变量,全局常驻**
- TEMP:临时变量
OB 没有独立 FB‑Static 之外的存储,长期状态优先新建 DB 块存放
### 2. FC 函数(无独立存储)
可用:Input / Output / InOut / TEMP
**FC 没有 Static 静态存储**,所有中间状态必须依靠 DB 全局变量保存。
### 3. FB 功能块(自带独立存储空间,最常用)
可用:Input、Output、InOut、**Static、TEMP**
每调用一次 FB 就生成一份独立实例数据,多台输送机、多台小车直接多次调用 FB 即可,互不干扰。
##五、一张对照表

## 六、SCL 编程通用规范(自动化项目标准写法)
1. 硬件 IO → 先在 PLC 变量表定义;
2. 设备长期状态(位置、自锁、故障、模式)→ **DB 全局变量 / FB‑Static**;
3. 中间运算、循环、差值计算 → TEMP 临时变量;
4. 小车、输送机封装成 FB,依靠 Static 保存自身状态;
5. FC 只用来做计算、工具函数,状态一律放在 DB;
6. 多设备就多次调用 FB 实例,每个实例自带独立 Static 存储空间。
## 七、新手高频错误总结
1. 自锁标记写到 TEMP 临时变量 → 扫描结束清零,自锁失效;
2. FC 里面试图依靠局部变量保存计数,必须改用 DB;
3. 循环变量 i 定义成 Static,多次调用会互相干扰;
4. 大批量设备状态优先 UDT 结构体 + DB 数组,不要写一堆零散变量。
SCL里不同进制数据写法
# 博途 SCL 里面 4 种进制写法(西门子 S7‑1200 / S7‑1500 通用)
## 1、十六进制(最常用于伺服控制字、IO 状态)
前缀:16#
val := 16#0006;
val := 16#000F;
val := 16#A0;
val := 16#FFFF;
## 2、二进制(看位最直观,调试控制字首选)
前缀:2#
val := 2#0000_0000_0000_0110; //对应上面16#0006
val := 2#0000_0000_0000_1111; //对应16#000F
val := 2#1011;
下划线 `_` 只是分隔符,方便阅读,**不影响数值**。
## 3、十进制(日常速度、距离、整数,默认进制,无前缀)
无前缀就是十进制
speed := 500; //十进制500
offset := -120;
## 4、八进制(极少用,几乎不会在伺服项目出现)
前缀:8#
val := 8#12;
SCL中静态变量,临时变量
VAR
Axis_Step:TO_PositioningAxis; // 工艺轴对象
MC_Power_Step:MC_Power;
MC_MovePos:MC_MoveAbsolute;
MC_MoveRel:MC_MoveRelative;
END_VAR
# 结论先说
放在 FB 的 `VAR` 区 → **属于静态变量 (Static)**
放在 OB / FC 的 `VAR` → 临时变量 (Temp),**绝对不能这么写运动指令!**
西门子常用的步进电机,串口通讯,网络通讯,模拟量采集与输出,脉冲输出,伺服控制 之类的用 SCL 代码如何实现
系统内部自带完整状态机;PLC 上电、下载后工艺对象自动初始化。
如果上电轴状态异常,用MC_Reset故障复位指令,而不是手动给结构体赋值
## 目录
1. 脉冲输出‑步进电机定位(PTO 工艺轴)
2. RS485 串口自由通讯(PtP)
3. 以太网 TCP Socket 通讯(开放式通讯)
4. 模拟量 AI 采集(4‑20mA / 0‑10V)
5. 模拟量 AO 输出
6. 通用注意事项 & 坑点
方案 A:脉冲伺服(脉冲 + 方向,PTO 输出)
方案 B:PROFINET 总线伺服(西门子 V90‑PN 工艺轴控制,推荐)
方案 C:PROFINET 伺服【直接报文控制,不使用工艺轴 TO】
## 1、脉冲输出|步进电机定位(PTO 脉冲 + 方向)
前提:先在项目里插入工艺对象【轴】`Axis_Step`,脉冲 Q0.0,方向 Q0.1
新建 FB:`FB_Step_Position`,带背景 DB
VAR_INPUT
Execute: BOOL; //启动信号(可以给长信号)
Position_Pulse: LREAL; //目标脉冲数
Velocity: LREAL; //速度 脉冲/s
Absolute: BOOL; //TRUE=绝对定位,FALSE=相对
Reset_Error: BOOL; //故障复位按钮
Axis_Enable: BOOL; //【新增】轴使能开关
END_VAR
VAR_OUTPUT
Done: BOOL;
Busy: BOOL;
Error: BOOL;
ErrorID: WORD;
Axis_Status: BOOL; //【新增】轴实际使能状态反馈
END_VAR
VAR //Static静态区!不可Temp
Axis_Step: TO_PositioningAxis;
MC_Power: MC_Power;
MC_MoveAbs: MC_MoveAbsolute;
MC_MoveRel: MC_MoveRelative;
MC_Reset: MC_Reset;
//【新增边沿实例!必须Static】
Exec_RTrig: R_TRIG;
Reset_RTrig: R_TRIG;
END_VAR
// ========== 1、上升沿转换:长信号 → 单周期脉冲 ==========
#Exec_RTrig(CLK:=#Execute);
#Reset_RTrig(CLK:=#Reset_Error);
// ========== 2、轴使能上电 ==========
#MC_Power.Axis := #Axis_Step;
#MC_Power.Enable := #Axis_Enable; //不再永久TRUE,外部可控
#MC_Power();
#Axis_Status := #MC_Power.Status; //读取真实使能状态
// ========== 3、故障复位(使用脉冲) ==========
#MC_Reset.Axis := #Axis_Step;
#MC_Reset.Execute := #Reset_RTrig.Q; //边沿脉冲触发复位
#MC_Reset();
// ========== 4、定位运动 ==========
IF #Absolute THEN
#MC_MoveAbs.Axis := #Axis_Step;
#MC_MoveAbs.Execute := #Exec_RTrig.Q; //边沿脉冲启动!
#MC_MoveAbs.Position := #Position_Pulse;
#MC_MoveAbs.Velocity := #Velocity;
#MC_MoveAbs.Acceleration := 5000.0;
#MC_MoveAbs.Deceleration := 5000.0;
#MC_MoveAbs.Jerk := 0.0;
#MC_MoveAbs();
#Done := #MC_MoveAbs.Done;
#Busy := #MC_MoveAbs.Busy;
#Error := #MC_MoveAbs.Error;
#ErrorID := #MC_MoveAbs.ErrorID;
ELSE
#MC_MoveRel.Axis := #Axis_Step;
#MC_MoveRel.Execute := #Exec_RTrig.Q; //边沿脉冲启动!
#MC_MoveRel.Distance := #Position_Pulse;
#MC_MoveRel.Velocity := #Velocity;
#MC_MoveRel.Acceleration := 5000.0;
#MC_MoveRel.Deceleration := 5000.0;
#MC_MoveRel.Jerk := 0.0;
#MC_MoveRel();
#Done := #MC_MoveRel.Done;
#Busy := #MC_MoveRel.Busy;
#Error := #MC_MoveRel.Error;
#ErrorID := #MC_MoveRel.ErrorID;
END_IF;
## 2、串口 RS485 自由通讯 PtP‑Send / PtP‑Receive(CM1241‑RS485)
硬件:信号板 CM1241‑RS485,组态端口参数:9600‑8‑N‑1
新建 FB `FB_RS485_Free`
VAR_INPUT
Send_Trigger: BOOL; //发送触发上升沿
Send_Data: ARRAY[0..19] OF BYTE; //发送缓冲区
END_VAR
VAR_OUTPUT
Recv_Data: ARRAY[0..19] OF BYTE; //接收缓冲区
Send_Done: BOOL;
Send_Busy: BOOL;
Send_Error: BOOL;
Recv_Done: BOOL;
Recv_Error: BOOL;
END_VAR
VAR //Static
PtP_Send: PtP_Send;
PtP_Rcv: PtP_Receive;
END_VAR
//发送
PtP_Send.Port := 1; //CM1241端口号
PtP_Send.Send := Send_Trigger;
PtP_Send.Buffer := Send_Data;
PtP_Send.Length := 20;
PtP_Send();
Send_Done := PtP_Send.Done;
Send_Busy := PtP_Send.Busy;
Send_Error := PtP_Send.Error;
//接收,循环监听
PtP_Rcv.Port := 1;
PtP_Rcv.Enable := TRUE; //一直开启接收
PtP_Rcv.Buffer := Recv_Data;
PtP_Rcv.Length := 20;
PtP_Rcv();
Recv_Done := PtP_Rcv.Done;
Recv_Error := PtP_Rcv.Error;
注意:Modbus‑RTU 不要用自由口自己解析,直接调用库 `MBUS_CTRL / MBUS_SLAVE / MBUS_CLIENT`
## 3、以太网开放式 TCP 通讯(TCON / TSEND / TRCV)
### TCP‑客户端示例,主动连接远端设备
FB: `FB_TCP_Client`
VAR_INPUT
Connect_Req: BOOL;
Send_Trigger: BOOL;
Send_Buf: ARRAY[0..31] OF BYTE;
END_VAR
VAR_OUTPUT
Recv_Buf: ARRAY[0..31] OF BYTE;
Connected: BOOL;
Send_Done: BOOL;
Recv_Done: BOOL;
Error: BOOL;
ErrorID: WORD;
END_VAR
VAR //Static
Tcon: TCON;
Tsend: TSEND;
Trcv: TRCV;
Conn_Data: TCON_Configured;
END_VAR
// =====连接参数配置=====
Conn_Data.RemoteAddress := '192.168.1.100'; //对方IP
Conn_Data.RemotePort := 502; //对方端口
Conn_Data.LocalPort := 0; //客户端自动分配
//建立连接
Tcon.Req := Connect_Req;
Tcon.InterfaceId := 64; //S7‑1200以太网接口ID固定64
Tcon.ConnectionType := 16#01; //TCP
Tcon.Config := Conn_Data;
Tcon();
Connected := Tcon.Done;
Error := Tcon.Error;
ErrorID := Tcon.ErrorId;
//发送数据
Tsend.Req := Send_Trigger;
Tsend.ID := Tcon.ID;
Tsend.DATA := Send_Buf;
Tsend.LEN := 10;
Tsend();
Send_Done := Tsend.Done;
//循环接收
Trcv.EN_R := TRUE;
Trcv.ID := Tcon.ID;
Trcv.DATA := Recv_Buf;
Trcv();
Recv_Done := Trcv.NDR;
TCP 服务器:把 TCON 连接类型改成被动连接即可。
## 4、模拟量 AI 采集 (4‑20mA)
S7‑1200 AI 模块原始值范围:`0‑27648` 对应 4‑20mA
物理量换算公式:
工程量 = (原始值 / 27648.0)* 量程跨度 + 偏移量
VAR
AI_Raw: INT; //%IW64 模拟量输入通道
AI_Value: REAL; //实际工程值
Range_Min: REAL:= 0.0;
Range_Max: REAL:= 100.0; //示例:0‑100吨压力
END_VAR
AI_Raw := "IW64"; //读取硬件输入字
IF AI_Raw < 0 THEN
AI_Raw := 0;
END_IF;
//4‑20mA换算
AI_Value := (REAL(AI_Raw) / 27648.0) * (Range_Max - Range_Min) + Range_Min;
```
0‑10V 信号同样用 0‑27648 换算。
## 5、模拟量 AO 输出
工程量 → 转换成 0‑27648,写到输出字 `%QW64`
VAR_INPUT
Set_Value: REAL; //目标工程值
END_VAR
VAR
AO_Raw: INT;
Range_Min: REAL:= 0.0;
Range_Max: REAL:= 100.0;
END_VAR
//限值保护
IF Set_Value < Range_Min THEN
Set_Value := Range_Min;
ELSIF Set_Value > Range_Max THEN
Set_Value := Range_Max;
END_IF;
AO_Raw := INT( (Set_Value - Range_Min) / (Range_Max - Range_Min) * 27648.0 );
"QW64" := AO_Raw; //输出到AO通道
# 六大非常重要的通用坑点总结
1. **运动指令 MC_xxx、TSEND、TCON、PtP_Send 必须放在 FB‑Static (VAR) 区,禁止 VAR_TEMP 临时区!状态机会丢失。**
2. 脉冲步进电机:编码器接闭环驱动器,**不要接到 PLC**,PLC 只发送脉冲方向信号。
3. TCP 通讯 ID 号必须统一,TCON 输出的 ID 给到 TSEND/TRCV。
4. 模拟量:硬件通道地址 IW、QW 直接访问;一定要做上下限防超量程。
5. 串口 RS485:如果是 Modbus RTU,优先使用官方 Modbus 库,不要自己字节解析校验码。
6. 上升沿触发信号(Execute,Req)只给一个周期高电平,不要长期置 TRUE。
## 方案 A:脉冲伺服(脉冲 + 方向,PTO 输出)
硬件:伺服驱动器设置为脉冲位置控制模式;PLC 晶体管输出 Q0.0 脉冲,Q0.1 方向。
TIA 组态:新建工艺轴对象,轴类型 = 脉冲 + 方向,和步进电机配置完全一样。
新建 FB:`FB_Servo_Pulse`,带背景 DB
VAR_INPUT
Execute: BOOL; //上升沿触发定位
Target_Pos: LREAL; //目标位置【脉冲】
Velocity: LREAL; //速度 脉冲/s
Acc: LREAL := 5000.0; //加速度
Dec: LREAL := 5000.0; //减速度
Absolute: BOOL; //TRUE=绝对定位 FALSE=相对
Jog_Fwd: BOOL; //正向点动
Jog_Bwd: BOOL; //反向点动
Reset_Error: BOOL; //故障复位
END_VAR
VAR_OUTPUT
Done: BOOL;
Busy: BOOL;
Error: BOOL;
ErrorID: WORD;
Act_Pos: LREAL; //轴实际位置
END_VAR
VAR //【Static静态区!禁止VAR_TEMP】
Axis_Servo_Pulse: TO_PositioningAxis;
MC_Power: MC_Power;
MC_MoveAbs: MC_MoveAbsolute;
MC_MoveRel: MC_MoveRelative;
MC_Jog: MC_Jog;
MC_Reset: MC_Reset;
END_VAR
//1、轴使能
MC_Power.Axis := Axis_Servo_Pulse;
MC_Power.Enable := TRUE;
MC_Power();
//2、故障复位
MC_Reset.Axis := Axis_Servo_Pulse;
MC_Reset.Execute := Reset_Error;
MC_Reset();
//3、点动Jog
MC_Jog.Axis := Axis_Servo_Pulse;
MC_Jog.JogForward := Jog_Fwd;
MC_Jog.JogBackward := Jog_Bwd;
MC_Jog.Velocity := 2000.0;
MC_Jog.Acceleration := Acc;
MC_Jog.Deceleration := Dec;
MC_Jog();
//4、绝对/相对定位
IF Absolute THEN
MC_MoveAbs.Axis := Axis_Servo_Pulse;
MC_MoveAbs.Execute := Execute;
MC_MoveAbs.Position := Target_Pos;
MC_MoveAbs.Velocity := Velocity;
MC_MoveAbs.Acceleration:= Acc;
MC_MoveAbs.Deceleration:= Dec;
MC_MoveAbs.Jerk := 0.0;
MC_MoveAbs();
Done := MC_MoveAbs.Done;
Busy := MC_MoveAbs.Busy;
Error := MC_MoveAbs.Error;
ErrorID := MC_MoveAbs.ErrorID;
ELSE
MC_MoveRel.Axis := Axis_Servo_Pulse;
MC_MoveRel.Execute := Execute;
MC_MoveRel.Distance := Target_Pos;
MC_MoveRel.Velocity := Velocity;
MC_MoveRel.Acceleration:= Acc;
MC_MoveRel.Deceleration:= Dec;
MC_MoveRel.Jerk := 0.0;
MC_MoveRel();
Done := MC_MoveRel.Done;
Busy := MC_MoveRel.Busy;
Error := MC_MoveRel.Error;
ErrorID := MC_MoveRel.ErrorID;
END_IF;
//读取轴实际位置
Act_Pos := Axis_Servo_Pulse.ActualPosition;
参数换算示例(伺服 2500 线,16 倍频):一圈 10000 脉冲。
## 方案 B:PROFINET 总线伺服(西门子 V90‑PN 工艺轴控制,推荐)
硬件组态步骤:
1. 网络里添加 V90‑PN,分配 IP,选择**标准报文 1: PZD‑2/2**
2. 添加工艺对象轴,轴类型:**PROFINET‑IOP(驱动器通过工艺通道)**
3. 在工艺轴里关联驱动器的控制字、状态字、位置给定、实际位置
FB 块 `FB_Servo_PN` SCL 代码,脉冲伺服代码几乎完全通用,**仅工艺轴对象更换**
VAR_INPUT
Execute: BOOL;
Target_Pos: LREAL;
Velocity: LREAL;
Acc: LREAL:= 8000.0;
Dec: LREAL:= 8000.0;
Absolute: BOOL;
Jog_Fwd: BOOL;
Jog_Bwd: BOOL;
Reset_Error: BOOL;
END_VAR
VAR_OUTPUT
Done: BOOL;
Busy: BOOL;
Error: BOOL;
ErrorID: WORD;
Act_Pos: LREAL;
Act_Vel: LREAL;
END_VAR
VAR //Static
Axis_V90_PN: TO_PositioningAxis; //PROFINET总线工艺轴
MC_Power: MC_Power;
MC_Home: MC_Home; //伺服回零
MC_MoveAbs: MC_MoveAbsolute;
MC_MoveRel: MC_MoveRelative;
MC_Jog: MC_Jog;
MC_Reset: MC_Reset;
END_VAR
//伺服上电使能
MC_Power.Axis := Axis_V90_PN;
MC_Power.Enable := TRUE;
MC_Power();
//故障复位
MC_Reset.Axis := Axis_V90_PN;
MC_Reset.Execute := Reset_Error;
MC_Reset();
//回零示例
MC_Home.Axis := Axis_V90_PN;
MC_Home.Execute := FALSE; //外部触发时置上升沿
MC_Home.HomingMode := 1; //主动回零
MC_Home.Position := 0.0;
MC_Home.Velocity := 500.0;
MC_Home.Acceleration := Acc;
MC_Home.Deceleration := Dec;
MC_Home();
//点动
MC_Jog.Axis := Axis_V90_PN;
MC_Jog.JogForward := Jog_Fwd;
MC_Jog.JogBackward := Jog_Bwd;
MC_Jog.Velocity := 1000.0;
MC_Jog.Acceleration := Acc;
MC_Jog.Deceleration := Dec;
MC_Jog();
//定位
IF Absolute THEN
MC_MoveAbs.Axis := Axis_V90_PN;
MC_MoveAbs.Execute := Execute;
MC_MoveAbs.Position := Target_Pos;
MC_MoveAbs.Velocity := Velocity;
MC_MoveAbs.Acceleration:= Acc;
MC_MoveAbs.Deceleration:= Dec;
MC_MoveAbs.Jerk := 0.0;
MC_MoveAbs();
Done := MC_MoveAbs.Done;
Busy := MC_MoveAbs.Busy;
Error := MC_MoveAbs.Error;
ErrorID := MC_MoveAbs.ErrorID;
ELSE
MC_MoveRel.Axis := Axis_V90_PN;
MC_MoveRel.Execute := Execute;
MC_MoveRel.Distance := Target_Pos;
MC_MoveRel.Velocity := Velocity;
MC_MoveRel.Acceleration:= Acc;
MC_MoveRel.Deceleration:= Dec;
MC_MoveRel.Jerk := 0.0;
MC_MoveRel();
Done := MC_MoveRel.Done;
Busy := MC_MoveRel.Busy;
Error := MC_MoveRel.Error;
ErrorID := MC_MoveRel.ErrorID;
END_IF;
Act_Pos := Axis_V90_PN.ActualPosition;
Act_Vel := Axis_V90_PN.ActualVelocity;
## 方案 C:PROFINET 伺服【直接报文控制,不使用工艺轴 TO】
适合不想用工艺对象,直接读写 V90 报文(报文 1:PZD2/2)
IO 地址示例:
- 控制字:`%QW256`
- 速度给定:`%QW258`
- 状态字:`%IW256`
- 实际速度:`%IW258`
VAR
CtrlWord: WORD;
StatusWord: WORD;
Speed_Set: INT;
END_VAR
//读取驱动器状态
StatusWord := "IW256";
//伺服就绪,上电使能
IF (StatusWord AND 16#0031) = 16#0031 THEN
CtrlWord := 16#000F; //使能
ELSE
CtrlWord := 16#0006; //断电
END_IF;
Speed_Set := 500; //转速设定值
"QW256" := CtrlWord;
"QW258" := INT(Speed_Set);
⚠️报文直接控制只有速度模式,定位必须自己写位置闭环逻辑,**强烈优先用工艺轴 TO**。
# 伺服控制重要注意事项
1. `MC_xxx`运动指令实例**必须放在 FB‑VAR (Static) 区,严禁 VAR_TEMP 临时变量!**
2. 脉冲伺服接线:PLC 脉冲输出→伺服 PUL+;方向→DIR+;PUL‑ DIR‑共接 PLC 0V (M),24V 输出注意光耦耐压,必要串 1‑2k 电阻。
3. 总线伺服 V90‑PN:工艺轴模式下,**不需要自己处理报文,TO 自动读写 PZD**。
4. Execute 触发信号只给**一个扫描周期上升沿脉冲**,不能一直为 TRUE。
5. 伺服一定要做回零程序 `MC_Home`,建立机械坐标系。
既然 static 变量是可保存上一次状态,那么他的初始值怎么定呢
## 4 种设置 Static 静态变量初始值的完整方法(S7‑1200/1500 SCL)
## 方法 1:FB 接口区声明的时候直接赋值(最推荐)
在 FB 的`VAR`(Static)区,直接 `:= 值`
VAR
Counter:INT:= 0;
Speed_Set:REAL:= 1200.0;
Init_Done:BOOL:= TRUE; //上电第一次为TRUE,用来做一次性初始化标记
//运动指令实例,MC系列本身自带系统内部初始状态,一般不用手动:=赋值
MC_Power_Step:MC_Power;
MC_MoveAbs:MC_MoveAbsolute;
END_VAR
原理:这个值叫**默认值 Default Value**;新建背景 DB 的时候,会自动复制到 DB 的【起始值】。PLC 从 STOP 切 RUN 时,非保持变量就加载这个起始值。
✅注意:**`MC_Power`、`TO_PositioningAxis`、`TCON`、`PtP_Send`这类系统结构体,你不要手动给它写:=xxx,系统自己管理初始状态,强行赋值会报错**。只需要初始化你自己写的普通变量、标记位即可。
## 方法 2:单独修改背景 DB 的【起始值】,实现一 FB 多实例不同初值
同一个 FB,调用两次生成两个背景 DB,想实例 1 速度 = 1000,实例 2 速度 = 2000:
1. 打开生成好的背景 DB
2. 找到 Static 变量,修改【起始值】那一列;实际值运行时变化,起始值是上电加载的基准。
优先级:**背景 DB 起始值 > FB 接口默认值**。
## 方法 3:程序运行时一次性上电初始化(SCL 代码控制)
适合复杂初始化逻辑,例如上电复位运动指令状态,清零缓存。
利用一个初始值 = TRUE 的静态标记位,只执行一次初始化代码:
VAR
Init_Flag:BOOL:= TRUE; //上电初始TRUE
MC_Power_Step:MC_Power;
END_VAR
//上电只跑一次初始化
IF Init_Flag THEN
MC_Power_Step.Enable:= FALSE;
Init_Flag:= FALSE; //执行完立刻关闭,后面循环再也不会进来
END_IF;
//正常运动逻辑
MC_Power_Step.Enable:= TRUE;
MC_Power_Step();
也可以用 OB100(暖启动块)全局初始化,但是 OB100 是全局上电执行,不适合 FB 实例独立初始化。
## 方法 4:保持性 (Retain) —— 断电也不丢失数值
在 FB 接口区,Static 变量勾选【保持】。
勾选保持:PLC 断电再上电,**不去加载起始值,直接使用断电前最后保存的值**。
典型场景:位置偏移量、计数累计值;
❌运动指令`MC_Power`、通讯指令`TCON`实例**不要勾选保持性**,上电容易通讯卡死、轴状态异常。
哪些能在DB中定义哪些不能,带逻辑代码的不能,直视单纯的结构可以,带方法的类不行
由于表格太小可用-》Ctrl+鼠标上下滑动放大缩小查看
不能直接在 DB 里定义,为何在 FB 里 static 可以定义呢
`TCON`、`MC_Power`、`TSEND` 它们本身就是 FB(系统功能块)
任何 FB 想要运行,必须分配一块专属的实例存储区(背景数据区),用来保存它内部的状态机:Busy、Done、Error、内部缓存、连接状态。


OB 如何保持上一次循环运行的状态
# OB 本身没有任何存储记忆能力
OB 没有背景 DB、没有 Static 静态区;OB 的`VAR_TEMP`临时变量扫描结束就释放,**不可能记住上一周期状态**。
想要跨扫描周期保存状态,只有 3 种正规方案,下面从最推荐到备选依次讲解。
## 方案 1:封装 FB,状态全部放在 FB‑Static(✅工程首选)
把所有带状态机的逻辑:运动指令`MC_Power`、TCP 通讯`TCON/TSEND`、边沿标记、计数器、设备运行状态,全部封装进 FB。
FB 自带背景 DB,`VAR`(Static) 区永久保存状态。OB 只负责调用 FB,不存任何状态。
### 示例
// FB_DeviceCtrl
VAR_INPUT
Start: BOOL;
END_VAR
VAR // Static,永久保存状态
LastStart: BOOL:=FALSE; //边沿历史状态
RunFlag: BOOL:=FALSE;
MC_Pow: MC_Power;
END_VAR
//上升沿
RTrig:= Start AND NOT LastStart;
LastStart:= Start;
IF RTrig THEN
RunFlag:= TRUE;
END_IF;
MC_Pow.Axis:= AxisX;
MC_Pow.Enable:= RunFlag;
MC_Pow();
// OB1(循环块),只调用,不存状态
#FB_Device_Instance(Start:="Start_Button");
OB 只充当调度者;**全部记忆存储交给 FB 背景 DB**。
这也是西门子运动、通讯项目标准架构。
## 方案 2:使用【全局 DB】存放状态(适合简单标记,不用 FB)
你新建一个**全局数据块 DB**,所有需要跨周期记忆的变量放在这里。
OB 的 TEMP 变量用完即丢;但全局 DB 属于 PLC 的全局 RAM 区,OB 跑完不会清空,数值永久保留。
⚠️注意:**FB 实例(TCON、MC_Power)不能放进全局 DB**,这条方案仅适合普通标记位、计数器、状态编号等普通变量。
示例:
全局 DB `DB_Global_State`
VAR
bMotorRunning: BOOL;
Last_Button: BOOL;
State_Num: INT;
END_VAR
OB1 内部代码:
VAR_TEMP
R_Trig: BOOL;
END_VAR
R_Trig := "DB_Global_State".StartBtn AND NOT "DB_Global_State".Last_Button;
IF R_Trig THEN
"DB_Global_State".bMotorRunning:=TRUE;
END_IF;
"DB_Global_State".Last_Button := "DB_Global_State".StartBtn;
全局 DB 里面的值会一直保存到下一次 OB 循环。
缺点:如果多个设备,所有状态全部堆在全局 DB,代码容易混乱,多实例无法隔离,因此复杂任务优先选 FB。
## 方案 3:多重背景 / 单实例 DB,OB 直接调用指令(不推荐长期开发)
你可以拖拽`MC_Power`、`TCON`到 OB1,博途弹出窗口,生成一块**独立的实例 DB(单实例 DB)**。
这块实例 DB 就是状态存储器。
OB 调用的时候指定这块 DB。
"DB_McPower_Instance"(
Axis:= Axis_X,
Enable:= "DB_Global_State".bMotorRunning
);
状态保存在独立实例 DB,OB 本身依然不保存任何东西。
缺点:OB 程序会变得臃肿,不方便复用;大型项目一般不这么写,优先封装 FB。
# ❌绝对不可行的错误做法
1. 在 OB‑VAR_TEMP 里面放状态变量,指望它记住上一轮的值。
- L 堆栈执行结束释放,数据丢失;硬件 PLC 值随机脏数据,仿真清零。
2. OB‑TEMP 中放置 `TCON` / `MC_Power` 实例
- 扫描结束状态机销毁 → 每周期重复触发动作,失控。
三种方案对比表

如何访问一个 FB 里的 static 变量
# 访问 FB‑Static (静态) 变量完整方法
Static 变量 = FB 接口区 `VAR` 下面定义的变量,保存在**FB 的背景数据块**。
FB 的 Static 变量**默认是可以外部读写访问**(没有隐藏)。
举一个示例 FB:
// FB_AxisCtrl
VAR_INPUT
Execute: BOOL;
END_VAR
VAR // Static静态区
RunFlag: BOOL;
PosSet: REAL;
McAbs: MC_MoveAbsolute;
END_VAR
调用 FB 生成背景 DB:`DB10_AxisX`
## 方式 1:直接通过背景 DB 的绝对名称访问(最常用)
格式:`"背景DB名".Static变量名`
// 在OB1 / 另一个FB/FC中读取
#Read_Run := "DB10_AxisX".RunFlag;
// 写入
"DB10_AxisX".PosSet := 500.0;
`McAbs` 也可以访问它内部成员:
"DB10_AxisX".McAbs.Execute := TRUE;
## 方式 2:使用指针 / 符号名访问(多重背景场景)
如果你用**多重背景**(父 FB 里面包含子 FB 实例)
//父FB FB_Station
VAR
Axis1: FB_AxisCtrl; //子FB实例,Static藏在父FB背景DB
END_VAR
背景 DB:`DB20_Station`
访问写法:
"DB20_Station".Axis1.RunFlag := TRUE;
# 方式 3:FB 内部对外暴露接口(推荐规范做法,不要直接读写 Static)
直接外部读写 FB 内部 Static 属于**打破封装**,大型项目不推荐。
标准做法:你增加 `VAR_OUTPUT` 输出变量把状态引出来。
//FB_AxisCtrl
VAR_OUTPUT
AxisRunning: BOOL;
END_VAR
VAR
RunFlag: BOOL; //内部static
END_VAR
AxisRunning := RunFlag; //输出对外
OB 调用 FB:
DB10_AxisX(Execute:=#Start, AxisRunning=>#Local_Run_State);
## 访问权限:如何禁止外部读写 Static(隐藏变量)
如果你不想外面代码修改 Static 变量:
1. FB 变量右键 → 属性 → **仅从内部访问 (Access‑protected / 从 HMI 外部不可见)**
开启后:外部程序依旧可以读写(代码不受限),但是 HMI 面板看不到;
西门子博途 SCL 最常用语句总结(S7‑1200/1500)
# 西门子博途 SCL 最常用语句总结(S7‑1200/1500)
适配你当前 FB/FC 开发,区分**必学基础、流程控制、边沿指令、运算、循环、分支选择**,附带易错坑点。
## 一、赋值语句(最基础)
#Out := #In1 AND #In2; // 逻辑与
#Out := #In1 OR #In2; // 逻辑或
#Out := NOT #In1; // 取反
#Val := 100; // 常数赋值
#RealVal := 3.14;
⚠️规则:
1. 赋值符号 **`:=`**,不是单一个 `=`
2. Input (IN) 只能读,**不能用 := 写入**
## 二、IF 判断语句(你刚踩坑最多的)
### 1. 单分支
IF #a = TRUE THEN
#dd := FALSE;
END_IF; // 末尾必须带英文分号!!
### 2. 二分支 IF‑ELSE
IF #Speed > 50 THEN
#Alarm := TRUE;
ELSE
#Alarm := FALSE;
END_IF;
### 3. 多分支 IF‑ELSIF‑ELSE
IF #Count < 10 THEN
#Level := 1;
ELSIF #Count < 20 THEN
#Level := 2;
ELSE
#Level := 3;
END_IF;
❗含 IF 的代码**无法转回梯形图 LAD**
## 三、CASE 多条件选择(替代一堆 IF‑ELSIF)
CASE #Step OF
1: #Run := TRUE;
2: #Run := FALSE;
3: #Reset := TRUE;
ELSE
#Error := TRUE;
END_CASE;
## 四、循环语句(慎用!PLC 循环容易扫描超时)
### FOR 循环(固定次数)
FOR #i := 1 TO 10 DO
#Array[#i] := FALSE;
END_FOR;
### WHILE 循环(条件满足就循环)
WHILE #Count < 100 DO
#Count := #Count + 1;
END_WHILE;
⚠️循环卡死 = PLC 看门狗报错停机;循环代码**不能转回梯形图**
## 五、边沿检测指令(脉冲、上升沿 / 下降沿,高频自动化用法)
FB 内部边沿指令,必须定义背景 DB 变量,不能直接写 #a

#RTrig_FB(CLK:=#a); //调用上升沿功能块
#RTrig := #RTrig_FB.Q; //取出边沿脉冲结果
#FTrig_FB(CLK:=#a); //调用下降沿功能块
#FTrig := #FTrig_FB.Q; //取出下降沿脉冲结果
## 六、定时器 / 计数器调用

//TON 通电延时
#MyTon(IN:=#Start, PT:=T#5S);
#Out := #MyTon.Q;
/*
逻辑流程:
1. `#Start` **通电(变为 1)** → 定时器立刻开始计时
2. 必须**一直保持通电,不能断掉**,持续 5 秒
3. 5 秒时间到 → `#Out` 输出 1
4. 一旦 `#Start` **断电(变成 0)** → `#Out`马上变成 0,计时器清零
*/
//TOF 断电延时
#MyTof(IN:=#Start, PT:=T#3S);
/*
逻辑流程:
1. `#Start` **通电 (1)** → `#Out2` **立刻输出 1,不等待**
2. `#Start` **断电 (0)** → 才开始倒计时 3 秒
3. 倒计时的这 3 秒,输出依旧保持 1
4. 3 秒走完,输出才变成 0
*/
//CTU加计数器
#MyCtu(CU:=#Pulse, R:=#Reset, PV:=10);
#CntComplete := #MyCtu.Q; //计数到10,输出标志
#NowCount := #MyCtu.CV; //读取当前累计的数字
/*
通俗时序
1. #Reset=0;每触发一次 #Pulse(0→1)计数 + 1
2. 脉冲 1‑9 次:Q=OFF
3. 第 10 次脉冲到来 → Q=ON(一直保持接通)
4. 直到 #Reset 变成 TRUE,计数器清零,Q 断开
*/
## 七、数学运算
#Result := #A + #B;
#Result := #A - #B;
#Result := #A * #B;
#Result := #A / #B;
//内置函数
#SinVal := SIN(#Angle);
#MaxVal := MAX(#x,#y);
#MinVal := MIN(#x,#y);
## 八、比较指令
#Flag := (#Value > 100);
#Flag := (#Value < 50);
#Flag := (#Value >= 20);
#Flag := (#Value <= 80);
#Flag := (#Value = 10);
#Flag := (#Value <> 0); //不等于
## 九、RET 返回(提前跳出 FC/FB)
IF #Enable = FALSE THEN
RET; //直接结束当前块,后面代码不再执行
END_IF;
# 🚩高频易错清单(你一定要记住)
1. `END_IF;` /`END_CASE;` /`END_FOR;` **结尾必须带分号**,缺分号直接爆红
2. TRUE、FALSE **大写**,小写报错
3. IN (Input) 参数只读,禁止 `#a:=xxx` 写入
4. IF、FOR、CASE、WHILE → **不能转回梯形图**
5. 边沿指令 R_TRIG/F_TRIG 必须用静态变量,不要用临时 Temp 变量
博途软件仿真
无实物博途学习西门子PLC方法.rar
物理端口对应I、Q

**I1.0 = 本体端子 DI8**
小数点前的数字:
0 → I0 字节; 1 → I1 字节; 2 → I2 字节
## I2.0 从哪里来?
CPU 本体**没有 DI14 端子,也就没有 I2.0**。
你需要在 CPU 右侧插槽,插上一块数字输入扩展模块,例如 SM1221(8 路 DI)。
### 地址分配规则:从左往右,自动排号
1 号槽(紧贴 CPU 右边) → 自动分配地址 **IB2**
这块扩展模块外壳印刷端子:

2 号槽的扩展模块,地址就是 IB3(I3.0‑I3.7),以此往后顺延。

# S7‑1200 三大类扩展硬件区分
1. **SM 信号模块**:插在 CPU**右侧**,宽度大,增加大量 IO,最常用数字量、模拟量
2. **SB 信号板**:装在 CPU**正面盖板里面**,体积很小,不增加控制柜宽度;只加少量点位,数字量、模拟量
3. **CM/CP 通信模块**:右侧插槽,用来接 RS485、RS232、以太网,走 Modbus、变频器、仪表通讯西门子工业.
DB 存取区和 M 存储的区别,他们分别在那种场合实用
# DB 块 与 M(位存储器 / Memory)完整对比 S7‑1200/1500
## 一、本质定义
### M 存储区(位存储器 / Memory)
CPU 自带的**全局中间寄存器**,独立存储区,不属于任何 FB/FC。
地址格式:`M0.0,M0.1,MB0,MW0,MD0`
- 全局变量:**整个项目所有 FB、FC、OB 都可以直接读写**
- 没有背景属性,无初始值管理,没有保护。
### DB 数据块 (Data Block)
一块用户创建的**数据存储区域**,存在 PLC 装载内存 + 工作内存。
分两大类:
1. **全局 DB 块**:全局变量,所有程序访问
2. **背景 DB 块 (Instance‑DB)**:FB 功能块配套生成,存 FB 里面的 Static 静态变量(你之前运动指令 MC_Power、R_TRIG 都存在这里)
地址写法:`"MyDB".Var1`;绝对地址:`DB1.DBX0.0`

# 三、什么时候用 M 区(适合的场合)
## ✅M 适用场景(少量临时全局标志位)
1. **少量全局布尔标志**:系统运行标志、故障总报警、启停总使能
2. OB1 循环里快速中转的少量中间信号
3. 老项目兼容,从 S7‑200 移植过来的旧逻辑
## ❌绝对不建议用 M 做这些
- 大批量的数据存储(计数、位置、速度参数),M 空间只有 8KB 很容易用完
- FB 内部的 Static 静态变量!**运动指令、边沿 R_TRIG 定时器绝对不能放到 M 区**,必须 FB‑Static(背景 DB)
- 多段程序共用大量中间变量,容易地址打架
M 口诀:**少量全局开关标志可以用,大批量数据别用 M**
# 四、什么时候用 DB 块(优先!绝大多数场景)
## 1、全局 DB 块 —— 项目公共参数
适合:
- 农机设备所有工艺参数:目标脉冲、速度、加减速、限位阈值
- 需要触摸屏 HMI 修改、显示的参数
- 配方数据:多组播种间距、速度配置
- 故障记录、计数器累计数值
示例:`"ParaDB".Seeding_Speed := 5000.0;`
优点:触摸屏直接选符号名,不用记地址;下载程序可以一键恢复默认参数。
## 2、背景 DB(FB 的 Static 变量)【你运动控制必须用!】
你之前写的运动控制 FB:
这些变量**自动保存在 FB 对应的背景 DB 块**。
✅定时器 TON、边沿 R_TRIG、运动指令 FB 实例**必须放在这里,不能放 M 区!**
PCL 变量表和 FB 里的 static 变量 临时变量区别
## PLC 变量表(项目树 → PLC 变量)
也就是**标记变量 / 符号表**,本质:给 M、I、Q、外设地址起别名。
底层存储区:**M 存储区 (位存储器)、I 输入、Q 输出**
- 属于**全局变量**,整个 PLC 所有 OB、FB、FC 随便读写
- 断电保持可设置(M 区勾选保持)
- 生命周期:上电一直存在,永久保存数值
- 和 DB 块互相独立
示例:
在 PLC 变量表新建:`StartButton : Bool 地址 %M0.0`
代码任何位置:`#StartFB := StartButton;`
注意:**PLC 变量表 ≠ 全局 DB**,这是很多人混淆的两个东西
PLC 变量表 → 映射 I/Q/M 物理地址
全局 DB → DB 存储区
## 2、FB‑Static 静态变量(VAR 区)
存储位置:**FB 对应的【背景数据块】RAM 内存**
- 归属:**只属于当前这个 FB 实例**
- 生命周期:FB 调用结束,**数值不会丢失**,跨 OB 扫描周期永久保存状态
- 适合存状态机、边沿历史、运动 / 通讯指令实例(TCON、MC_Power)
- 多个 FB 实例,Static 完全独立互不干扰(多台轴互不影响)
- 外部可以通过背景 DB 名字访问:`"DB_Axis".RunFlag`
## 3、临时变量 VAR_TEMP(局部临时变量)
存储位置:**L‑Stack 局部堆栈**
- 归属:仅当前这一次块的运行周期
- 生命周期:**FB/OB/FC 执行完毕立刻释放堆栈内存,数据丢失**
- ⚠️真实 PLC:TEMP 不会自动清零,里面是脏数据,必须**先赋值再读取**
- ❌**绝对不能存放状态机、边沿标记、MC/TCON 实例**

# 通俗比喻
1. **PLC 变量表 (M 区)**:厂区里面一块**公共公告板**,所有人都能读写;适合少量全局开关标记。缺点:多设备共用容易名字冲突,乱改互相干扰。
2. **FB‑Static(背景 DB)**:**每个设备自己专属储物柜**。柜子里面存自己的运行状态、历史标记、运动驱动器实例;别的柜子互不干扰。自动化设备、轴控制首选。
3. **VAR_TEMP 临时变量**:一张**一次性草稿纸**。块开始运行拿出来写计算结果;代码跑完直接扔掉,下次进来又是一张纸。只用来做中间临时运算。
# 工程选型使用建议
1. **设备状态、轴运动、通讯状态机、边沿历史变量 → FB‑Static**(首选)
2. **少数全局联锁信号、启停总开关 → M 区 (PLC 变量表) 或者 全局 DB**
3. **中间计算值、临时比较结果 → VAR_TEMP 临时变量**
使用变量时候变量前要加#
## 什么时候必须加 `#`
**块接口变量(局部变量)在块内使用时,前面必须加 `#`**,这是 TIA 的强制规范:
- `VAR_INPUT` / `VAR_OUTPUT` / `VAR_IN_OUT`(形参)
- `VAR`(静态变量)
- `VAR_TEMP`(临时变量)
FUNCTION_BLOCK FB_Demo
VAR_INPUT
bStart : BOOL;
END_VAR
VAR
iCount : INT;
END_VAR
IF #bStart THEN // 输入变量 → 加#
#iCount := #iCount + 1; // 静态变量 → 加#
END_IF;

## 为什么必须加
TIA Portal V13 之后,SCL 编译器把**不带 `#` 的标识符**当作 "全局符号" 去符号表里找 —— 找不到就编译报错("未定义符号")。所以:
- 加 `#` = 明确告诉编译器 "这是本块的局部变量"
- 不加 `#` = 编译器去全局符号表找,找不到就报错,找到就误用全局变量(很危险,容易出现逻辑错乱)
完整例子:
FUNCTION_BLOCK FB_ConveyorMove
VAR_INPUT
bStart : BOOL;
bEStop : BOOL;
bAtPos : BOOL;
bFwdFree : BOOL;
END_VAR
VAR_OUTPUT
bMotorFwd : BOOL;
bDone : BOOL;
bAlarm : BOOL;
END_VAR
VAR
tonRun : TON;
rTrigStart : R_TRIG;
stState : INT := 0;
END_VAR
#rTrigStart(CLK := #bStart);
CASE #stState OF
0:
IF #rTrigStart.Q AND NOT #bEStop AND #bFwdFree THEN
#stState := 10;
END_IF;
10:
#bMotorFwd := TRUE;
#tonRun(IN := TRUE, PT := T#30S);
IF #bAtPos THEN
#bMotorFwd := FALSE;
#bDone := TRUE;
#stState := 20;
ELSIF #tonRun.Q THEN
#bMotorFwd := FALSE;
#bAlarm := TRUE;
#stState := 90;
END_IF;
20:
IF NOT #bStart THEN
#bDone := FALSE;
#stState := 0;
END_IF;
90:
IF NOT #bStart THEN
#bAlarm := FALSE;
#stState := 0;
END_IF;
END_CASE;