1. 外部中断到底在解决什么问题
1.1 从一次产线改造的翻车经历说起
前年帮一家做包装机械的客户改造一条老产线,主控用的是S7-1200 CPU 1214C DC/DC/DC。设备上有个色标检测传感器,用来在薄膜经过时抓取印刷标记,触发切刀动作。原来的方案是用高速计数器配合比较指令,在主循环里轮询。听起来没问题,但实际跑起来就出事了——切刀动作偶尔会晚个十几毫秒,切出来的袋子长短不一,废品率居高不下。
我盯着程序监控看了半天,发现主循环的扫描周期在8到12毫秒之间波动,赶上通信任务多的时候能到15毫秒。色标信号是个窄脉冲,宽度只有2毫秒左右,主循环根本来不及响应。这就是典型的“轮询丢信号”问题。
后来改用外部中断,把色标信号接到CPU本体自带的DI点上,配置上升沿触发硬件中断,在OB40里直接输出切刀动作。扫描周期的影响瞬间消失,响应时间稳定在微秒级,废品率直接降到千分之三以下。
这件事让我意识到,很多做PLC编程的朋友对中断的理解还停留在“知道有这么个东西”的层面,真到用的时候不知道怎么配、怎么分离、怎么避免踩坑。所以我把S7-1200外部中断的创建和分离这套东西整理出来,结合我实际调试中积累的经验,给需要做高速响应的同行们一个可复现的参考。
1.2 外部中断和普通输入采样的本质区别
S7-1200的普通数字量输入,走的是“扫描周期采样”这条路。CPU在每个扫描周期开始时读取输入映像区,执行用户程序,然后刷新输出。这意味着输入信号必须至少保持一个扫描周期以上,才能被可靠捕获。扫描周期越长,能捕获的最小脉冲宽度就越大。
外部中断走的是完全不同的路径。当配置为硬件中断的输入点检测到指定边沿时,CPU会立即暂停当前正在执行的程序,跳转到对应的中断组织块(OB40)去执行。执行完中断程序后,再回到刚才被打断的地方继续执行。这个过程不受扫描周期约束,响应时间通常在微秒级别。
打个比方:普通输入采样就像你每隔十分钟去门口看一眼有没有快递,快递员要是放下包裹两分钟就走了,你很可能错过。外部中断就像给门装了个门铃,快递员一按铃你立刻就知道,不管你在屋里干什么。
1.3 哪些场景必须用外部中断
不是所有场合都需要外部中断。我总结了几类典型场景,如果你遇到了,就得认真考虑用中断来处理:
- 窄脉冲信号捕获:信号宽度小于扫描周期,比如色标检测、编码器索引脉冲、光电传感器的快速遮挡
- 高速计数和位置锁存:需要在特定位置精确记录当前计数值,比如飞剪、追剪应用
- 紧急事件响应:急停按钮、安全门开关、限位保护等需要立即动作的信号
- 精确定时任务:需要以固定微小间隔执行的任务,比如PID控制的快速采样
- 多轴同步触发:多个伺服轴需要在同一时刻启动或停止
反过来,如果信号宽度远大于扫描周期,比如普通的按钮、温控开关、液位浮球,用普通输入采样完全够用,没必要上中断。中断资源是有限的,滥用反而会增加程序复杂度和调试难度。
2. S7-1200外部中断的硬件和软件配置
2.1 硬件层面的限制和选型要点
S7-1200的CPU模块本体上,不是所有DI点都支持硬件中断。以CPU 1214C DC/DC/DC为例,本体集成14个DI,其中只有前6个(I0.0到I0.5)支持硬件中断功能。CPU 1215C和1217C的支持数量会多一些,但也不是全部。
如果你需要更多中断输入点,就得加信号板或信号模块。SB 1223信号板可以增加2个DI,SM 1221数字量输入模块有8点或16点版本。但要注意,不是所有信号模块都支持硬件中断,选型的时候一定要查手册确认。
我踩过的一个坑:有一次用SM 1221 DI 16x24VDC模块接急停信号,配置的时候发现找不到硬件中断选项。后来查手册才知道,这个模块的输入点不支持硬件中断功能,只能做普通DI用。最后换成了SM 1223 DI 16x24VDC/DQ 16x继电器模块,问题才解决。
提示:选型时务必确认模块的技术数据中明确标注“支持硬件中断”或“硬件中断输入”字样,不要想当然认为所有DI点都支持。
另外,硬件中断的响应速度还受输入滤波时间影响。S7-1200的DI点默认滤波时间是6.4毫秒,这个值会直接加到中断响应时间上。如果信号是窄脉冲,必须把滤波时间调小。在博途里可以按通道设置滤波时间,可选值有0.2ms、0.4ms、0.8ms、1.6ms、3.2ms、6.4ms、12.8ms等。滤波时间越短,抗干扰能力越弱,需要根据现场电磁环境权衡。
2.2 博途中的硬件组态步骤
在博途(TIA Portal)里配置外部中断,硬件组态是第一步。我以CPU 1214C DC/DC/DC为例,把完整步骤走一遍:
- 打开设备视图,双击CPU进入设备概览
- 在CPU的属性面板中,找到“数字量输入”节点
- 展开后可以看到每个输入通道的配置项,包括“启用硬件中断”
- 勾选需要用作中断的通道,比如I0.0
- 在“硬件中断”下拉框中,选择触发方式:上升沿、下降沿或上升沿和下降沿
- 如果需要,调整“输入滤波”时间
这里有个细节:博途默认会给每个启用了硬件中断的通道分配一个硬件标识符,比如“硬件中断_1”。这个标识符在编程时要用到,用来区分不同的中断源。你可以在“系统常数”标签页里看到所有已分配的硬件标识符。
配置完成后,编译项目。如果配置正确,博途会自动在程序块中生成对应的OB40。如果没有生成,可以手动添加一个OB40,然后在属性里关联对应的硬件中断事件。
2.3 中断组织块OB40的创建和参数
OB40是S7-1200处理硬件中断的组织块。当配置为硬件中断的输入点检测到指定边沿时,CPU会调用OB40。OB40的优先级默认是16,可以调整,范围是16到26。优先级越高,中断可以打断其他中断的能力越强。
OB40的启动信息里包含了一些有用的参数,可以通过指令读取:
- OB40_MDL_ADDR:触发中断的模块地址
- OB40_POINT_ADDR:触发中断的通道信息
- OB40_PRIORITY:当前中断优先级
这些参数在多个中断源共用同一个OB40时特别有用。比如你有I0.0和I0.1两个中断源都关联到OB40,就可以在OB40里读取OB40_POINT_ADDR来判断到底是哪个通道触发的。
我通常会在OB40的开头加一段判断逻辑,根据不同的中断源执行不同的动作。这样比给每个中断源单独建一个OB40要简洁,也便于统一管理。
3. 外部中断的分离:DETACH指令的实战用法
3.1 为什么需要分离中断
“分离”这个词听起来有点抽象,说白了就是临时关闭某个中断源的触发功能,让它暂时不产生中断。为什么要这么做?我举几个实际场景:
场景一:防止重复触发。色标检测信号在薄膜抖动时可能产生多个边沿,如果每次边沿都触发切刀,就会切出多个废品。这时候可以在第一次中断触发后,立即用DETACH分离该中断,等切刀动作完成后再用ATTACH重新挂载。
场景二:分时复用。一个中断输入点在不同工位有不同的用途。比如在工位A它用来检测工件到位,在工位B它用来检测气缸伸出到位。通过DETACH和ATTACH切换,可以用一个物理点实现两种功能。
场景三:调试和故障隔离。某个中断源频繁误触发,影响其他中断的正常执行。可以临时DETACH掉这个中断源,先保证系统基本运行,等停机后再排查问题。
3.2 DETACH和ATTACH指令的语法和参数
在博途里,DETACH和ATTACH是扩展指令,位于“扩展指令 > 中断 > 硬件中断”下面。
DETACH指令的语法:
DETACH(OB_HW_INT := 硬件中断标识符);参数OB_HW_INT就是你在硬件组态里看到的那个硬件标识符,比如“硬件中断_1”。执行DETACH后,该中断源就不再触发OB40了。
ATTACH指令的语法:
ATTACH(OB_HW_INT := 硬件中断标识符);执行ATTACH后,该中断源重新启用,恢复触发功能。
这两个指令都是异步执行的,调用后不会立即生效,而是在下一个扫描周期边界生效。所以如果你在OB40里调用DETACH,当前这次中断还是会执行完,但下一次边沿就不会再触发了。
3.3 一个完整的分离-重挂载实例
我拿前面提到的色标检测案例,把完整逻辑写一遍。假设色标信号接在I0.0,切刀输出在Q0.0,硬件标识符是“硬件中断_1”。
在OB40里:
// 读取中断触发信息 #triggerAddr := OB40_POINT_ADDR; // 判断是I0.0触发 IF #triggerAddr = 16#00000001 THEN // 立即分离中断,防止抖动重复触发 DETACH(OB_HW_INT := "硬件中断_1"); // 输出切刀动作 "切刀输出" := TRUE; // 启动切刀动作定时器 "切刀定时器".TON(IN := TRUE, PT := T#200MS); END_IF;在OB1的主循环里:
// 切刀动作完成,重新挂载中断 IF "切刀定时器".Q THEN "切刀输出" := FALSE; "切刀定时器".TON(IN := FALSE); // 重新启用中断 ATTACH(OB_HW_INT := "硬件中断_1"); END_IF;这个逻辑的核心思路是:中断触发后立即分离,执行动作,动作完成后重新挂载。这样既保证了响应速度,又避免了抖动导致的重复触发。
注意:DETACH和ATTACH必须成对使用。如果只DETACH不ATTACH,这个中断源就永久失效了,除非重新下载硬件组态或重启CPU。
4. 中断优先级和嵌套的实战处理
4.1 优先级分配的原则
S7-1200的中断优先级范围是16到26,数值越大优先级越高。默认所有OB40的优先级都是16。如果你有多个中断源,且它们之间有响应速度的差异,就需要分配不同的优先级。
我的分配原则是:
- 安全相关中断:优先级最高,比如急停、安全门,设为24到26
- 高速位置捕获:优先级次高,比如编码器索引、飞剪触发,设为20到23
- 普通工艺中断:优先级中等,比如色标检测、工件到位,设为17到19
- 低速监控中断:优先级最低,比如温度报警、液位开关,设为16
优先级高的中断可以打断优先级低的中断,形成嵌套。但嵌套层数不是无限的,S7-1200最多支持嵌套深度取决于CPU型号和当前资源占用情况。嵌套太深会导致堆栈溢出,程序崩溃。
4.2 中断嵌套的注意事项
中断嵌套虽然强大,但用不好就是灾难。我总结了几个必须遵守的规则:
规则一:中断程序尽量短。OB40里只做最紧急的动作,比如置位输出、锁存数据、启动定时器。复杂的计算、通信、文件操作统统放到主循环里做。中断程序执行时间越长,嵌套风险越大。
规则二:避免在中断里调用可能阻塞的指令。比如TSEND_C、TRCV_C这类通信指令,在中断里调用可能导致通信资源冲突。如果非要在中断里发数据,用TSEND(不带C)或者设置数据缓冲区,在主循环里统一发送。
规则三:共享数据要加保护。中断程序和主循环可能同时访问同一个DB变量。如果主循环正在写一个32位变量,中断来了把高16位改了,主循环再写低16位,数据就乱了。解决办法是在主循环访问共享数据前用DIS_AIRT禁用中断,访问完用EN_AIRT恢复。
// 主循环中保护共享数据 DIS_AIRT(); // 禁用中断 "共享数据块".当前值 := #新值; EN_AIRT(); // 恢复中断规则四:中断里不要用上升沿/下降沿指令。因为中断本身就是边沿触发的,在中断里再用边沿指令没有意义,反而可能因为中断执行时间短而丢失边沿。
4.3 优先级冲突的排查方法
如果发现中断响应不及时,或者某些中断偶尔丢失,大概率是优先级冲突导致的。排查步骤:
- 在博途的“在线和诊断”里查看CPU的扫描周期和中断执行时间
- 检查是否有低优先级中断执行时间过长,阻塞了高优先级中断
- 检查是否有中断嵌套过深,导致堆栈告警
- 用OB40_PRIORITY参数在中断里记录当前优先级,通过监控表观察实际执行顺序
我遇到过一次典型问题:一个客户把温度PID的中断优先级设成了20,结果每次PID中断执行时,色标中断(优先级18)就被阻塞,导致切刀动作延迟。后来把PID中断降到16,色标中断升到20,问题解决。
5. 常见问题排查和避坑指南
5.1 中断完全不触发的排查清单
这是最常见的问题,我整理了一个速查表:
| 排查项 | 检查方法 | 常见原因 |
|---|---|---|
| 硬件组态是否启用 | 设备视图查看DI通道属性 | 忘记勾选“启用硬件中断” |
| 触发边沿是否正确 | 检查上升沿/下降沿设置 | 信号实际边沿与配置相反 |
| 输入滤波是否过大 | 查看滤波时间设置 | 窄脉冲被滤波吃掉 |
| OB40是否存在 | 程序块列表查看 | OB40被误删或未生成 |
| 硬件标识符是否匹配 | 系统常数中核对 | DETACH后未ATTACH |
| 模块是否支持中断 | 查手册技术数据 | 选了不支持中断的模块 |
| 信号电平是否达标 | 万用表测量输入电压 | 24V信号只有15V,识别不到 |
5.2 中断误触发和抖动处理
信号抖动导致中断频繁触发,是现场调试中最头疼的问题之一。除了前面说的DETACH/ATTACH方案,还有几种处理方式:
硬件滤波:在输入端加RC滤波电路,或者选用带滤波功能的传感器。这是最根本的解决办法,但需要改硬件。
软件滤波:在OB40里读取输入点状态,延时几十微秒后再读一次,两次一致才认为有效。但这样会增加中断执行时间,不适合高速信号。
中断计数限流:在OB40里对中断次数计数,如果单位时间内超过阈值,就DETACH一段时间再ATTACH。这个方案适合抖动有规律的场合。
我一般优先推荐硬件滤波,实在改不了硬件再用DETACH方案。因为DETACH方案有个副作用:在分离期间,真实的有效信号也会被忽略。如果抖动和有效信号混在一起,可能会漏掉真正的触发。
5.3 中断与主循环的数据一致性
前面提过共享数据保护,这里再展开说一下。S7-1200的中断可以打断主循环的任何位置,包括正在执行的赋值、运算、比较指令。如果主循环正在写一个多字节变量,中断里读这个变量,就可能读到“半新半旧”的数据。
举个例子:主循环正在把一个REAL值写入DB,这个REAL占4个字节。写到第2个字节时中断来了,中断里读这个REAL,读到的就是高2字节是旧值、低2字节是新值的错误数据。
解决办法有三种:
- 用DIS_AIRT/EN_AIRT保护:简单直接,但会短暂禁用所有中断,影响响应速度
- 用双缓冲:主循环写缓冲区A,中断读缓冲区B,写完切换标志位
- 用原子操作:S7-1200的某些指令是原子的,比如MOVE指令移动一个WORD或DWORD,不会被中断打断
我通常用第一种,因为实现简单,而且禁用中断的时间极短(就几条指令),对整体响应影响可以忽略。
5.4 中断程序执行时间超限的后果
OB40的执行时间是有上限的,具体值取决于CPU型号和当前扫描周期。如果中断程序执行时间超过上限,CPU会报“中断组织块执行时间超限”错误,严重时会导致CPU停机。
我实测过CPU 1214C,OB40里执行100条简单指令大约需要50微秒。如果超过200微秒,就有超限风险。所以中断程序一定要精简,能放到主循环的坚决不放中断。
有个技巧:在OB40里只做“置位标志位”和“锁存数据”,然后在主循环里根据标志位执行具体动作。这样中断执行时间可以控制在10微秒以内,几乎不会超限。
6. 进阶应用:多中断源协同和状态机设计
6.1 多个中断源共用一个OB40
当你有多个中断源时,可以给每个源单独建OB40,也可以共用一个OB40。共用的好处是节省程序块数量,便于统一管理;坏处是需要用OB40_POINT_ADDR来区分中断源,逻辑稍微复杂一点。
我一般建议:如果中断源少于4个,且动作逻辑相似,就共用一个OB40;如果中断源多,或者动作逻辑差异大,就分开建。
共用OB40的典型结构:
CASE OB40_POINT_ADDR OF 16#00000001: // I0.0触发 // 执行动作A 16#00000002: // I0.1触发 // 执行动作B 16#00000004: // I0.2触发 // 执行动作C ELSE // 未知中断源,记录报警 END_CASE;6.2 中断驱动的状态机实现
在一些复杂工艺中,中断不只是触发一个动作,而是推动整个状态机流转。比如一个自动装配机,工件到位中断触发状态从“等待”转到“夹紧”,夹紧到位中断触发状态从“夹紧”转到“拧紧”,拧紧完成中断触发状态从“拧紧”转到“松开”。
这种设计的关键是:状态变量必须在中断和主循环之间安全共享。我的做法是把状态变量放在一个专门的DB里,所有中断和主循环都通过这个DB访问,访问前用DIS_AIRT保护。
状态机的每个状态对应一个中断源,通过ATTACH和DETACH控制当前激活哪个中断。比如在“等待”状态时,只ATTACH工件到位中断,其他中断都DETACH。进入“夹紧”状态后,DETACH工件到位中断,ATTACH夹紧到位中断。这样任何时刻只有一个中断源处于激活状态,逻辑清晰,不会互相干扰。
6.3 中断与高速计数器的配合
S7-1200的高速计数器(HSC)本身也支持中断功能。当计数值达到设定值时,可以触发OB40。这个功能在飞剪、追剪、定长切割中非常有用。
配置方法:在CPU属性里启用高速计数器,设置比较值,然后在“硬件中断”里关联OB40。当HSC计数值等于比较值时,OB40被调用。
这里有个坑:HSC的中断和DI的中断共用OB40资源。如果HSC中断和DI中断同时触发,CPU会根据优先级决定先执行哪个。如果两个中断优先级相同,则按触发顺序执行。所以如果HSC中断和DI中断都很关键,建议给它们分配不同的优先级,避免相互阻塞。
我在一个定长切割项目里,用HSC中断触发切刀,用DI中断检测切刀到位。HSC中断优先级设为22,DI中断优先级设为20。这样切刀触发永远优先于到位检测,保证了切割精度。
7. 调试工具和监控技巧
7.1 用监控表观察中断执行
博途的监控表可以监控OB40里的变量,但有个限制:OB40执行时间极短,监控表的刷新周期可能捕捉不到。解决办法是在OB40里把关键变量复制到一个全局DB,然后在监控表里监控这个DB。这样即使OB40执行完了,你也能看到最后一次中断时的数据。
我通常会在OB40里记录以下信息:
- 中断触发时间戳(用RD_SYS_T读取)
- 触发通道号
- 触发时的关键过程数据
- 中断执行次数计数器
这些信息在排查间歇性故障时特别有用。比如切刀偶尔切偏,你可以对比正常和异常时的触发时间戳和过程数据,找出差异。
7.2 用Trace功能抓取中断时序
博途的Trace功能可以以微秒级精度记录变量变化,非常适合分析中断时序。你可以把中断触发标志、输出状态、定时器当前值都加到Trace里,然后触发一次中断,看波形。
我用水色标检测项目做过测试:把色标信号、OB40执行标志、切刀输出都加到Trace,采样周期设为1毫秒。结果清晰看到色标信号上升沿到切刀输出上升沿的延迟是80微秒,其中输入滤波占了60微秒,OB40执行占了20微秒。后来把滤波时间从6.4毫秒改成0.2毫秒,延迟降到25微秒,切割精度明显提升。
7.3 常见故障代码和处理
S7-1200在中断相关故障时会生成诊断缓冲区条目,常见的包括:
| 故障代码 | 含义 | 处理方法 |
|---|---|---|
| 16#3502 | 中断组织块执行时间超限 | 精简OB40代码,减少指令数 |
| 16#3503 | 中断嵌套过深 | 降低中断优先级,减少嵌套层数 |
| 16#3505 | 硬件中断丢失 | 检查输入滤波,缩短滤波时间 |
| 16#3508 | OB40不存在 | 重新生成OB40,检查硬件组态 |
| 16#3509 | 中断源未使能 | 检查ATTACH是否执行 |
这些代码可以在“在线和诊断 > 诊断缓冲区”里看到。每次下载程序后,我都会先清空诊断缓冲区,然后跑一遍完整工艺,再看缓冲区里有没有中断相关报警。这个习惯帮我提前发现了很多潜在问题。
8. 一些个人体会和后续扩展方向
外部中断这个东西,刚接触的时候觉得挺简单,不就是配个OB40嘛。但真正在项目里用起来,才发现细节太多了。滤波时间、优先级、嵌套、数据一致性、DETACH/ATTACH的时机,每一个点没处理好都可能导致偶发故障,而且这种故障往往很难复现,调试起来特别折磨人。
我的经验是:能用主循环解决的,就不要用中断。中断是稀缺资源,只留给真正需要微秒级响应的信号。每用一个中断,就要问自己三个问题:这个信号真的需要中断吗?中断程序能不能再短一点?中断和主循环的数据交互安全吗?
后续如果大家有兴趣,可以进一步研究S7-1200的延时中断(OB20)和循环中断(OB30/OB35),它们和外部中断配合使用,能实现更复杂的时序控制。比如用OB30做PID采样,用OB40做紧急事件响应,两者通过共享DB交换数据,既保证了控制精度,又保证了响应速度。
另外,S7-1200 G2系列在中断处理上做了一些优化,支持更多的中断源和更灵活的优先级配置。如果项目预算允许,G2系列在处理多中断协同场景时会更从容一些。不过对于大多数中小型项目,普通S7-1200的中断能力已经绰绰有余了。