文章目录

ST中断服务程序:OB块中ST代码的编写规范与限制

发布于 2026-03-18 22:24:51 · 浏览 3 次 · 评论 0 条

ST中断服务程序:OB块中ST代码的编写规范与限制

在S7-1200/1500系列PLC编程中,组织块(OB)是响应系统事件的核心执行单元。其中,中断组织块(如OB40~OB47、OB60~OB64等) 用于处理硬件中断、时间中断、诊断中断等实时性要求高的任务。当使用结构化文本(Structured Text,ST)语言编写这些OB块时,必须严格遵循IEC 61131-3标准与西门子TIA Portal运行时环境的双重约束——稍有偏差,轻则导致中断丢失、逻辑错乱,重则引发CPU停机或过程失控。本文不讲概念,只列可立即执行的硬性规则与典型陷阱,覆盖从变量声明、语句结构到调试验证的全链路实操要点。


一、OB块选择与触发机制确认(前置动作)

中断OB的编号直接决定其触发条件和执行优先级。必须根据实际硬件信号类型和响应时效要求选择唯一匹配的OB,不可随意替换或复用。

  1. 确认中断源类型

    • 硬件中断(如数字量输入DI的上升沿)→ 固定使用 OB40(默认)或 OB41OB47(需在硬件组态中为每个DI通道单独分配)。
    • 时间中断(周期性执行)→ 使用 OB30OB38(推荐 OB35,默认100ms周期;若需更短周期,必须启用“高精度定时器”并确认CPU支持)。
    • 延迟中断(延时后触发)→ 使用 OB20(仅限S7-1500,S7-1200不支持)。
    • 诊断中断(模块故障)→ 使用 OB82(诊断中断)、OB86(机架故障)、OB121(编程错误)。
  2. 检查硬件组态中的中断使能状态

    • 在TIA Portal中双击对应DI模块 → 进入“属性” → “常规” → “中断” → 勾选“启用过程中断”
    • 在“过程中断”选项卡中 → 设置“事件”为“上升沿”或“下降沿”
    • 点击“分配组织块”下拉菜单 → 明确指定为 OB40(或其他已创建的OB编号)
    • 若未完成此步,即使ST代码完全正确,中断也永远不会触发。
  3. 验证OB优先级与嵌套限制

    • 所有中断OB的优先级均高于主循环OB1(默认优先级1),但彼此之间存在固定顺序:
      OB40(硬件中断) > OB60(同步中断) > OB35(时间中断) > OB82(诊断中断);
    • 同一优先级的多个OB不能同时运行:例如,两个DI通道均配置为 OB40,则第二个中断将在第一个OB40执行完毕后才进入;
    • 禁止在中断OB内调用更高优先级的OB(如在OB40中调用 CALL "OB60"),将导致CPU报错 16#8004(优先级冲突)。

二、ST代码结构:四层强制分区与声明规则

S7-1200/1500的中断OB在ST语言下必须按以下顺序、且仅按此顺序书写代码段。任何错位、缺失或混用均会导致编译失败或运行异常。

  1. 全局变量声明区(仅允许VAR_GLOBAL / VAR_TEMP)

    • 禁止声明VAR或VAR_IN_OUT变量:中断OB无固定输入/输出接口,所有数据交互必须通过全局DB块或M存储区;
    • VAR_TEMP变量仅限本OB内部使用,且每次中断触发时自动初始化为0或FALSE(非保持型);
    • 正确示例:
      VAR_TEMP
          iCounter : INT := 0;           // 每次中断重置为0
          bPulseActive : BOOL := FALSE; // 非保持,无需INIT
      END_VAR
    • 错误示例(禁止):
      VAR
          myVar : REAL; // 编译报错:中断OB不支持VAR区
      END_VAR
  2. 局部常量与符号常量定义区(VAR CONSTANT)

    • 仅允许定义编译期确定的常量,禁止调用函数或访问DB变量
    • 推荐用常量替代魔法数字,提升可读性与维护性;
    • 示例:
      VAR CONSTANT
          MAX_COUNT : INT := 1000;
          PULSE_WIDTH_MS : TIME := T#20MS;
      END_VAR
  3. 指令执行区(主体逻辑)

    • 必须以顺序逻辑为主,严禁使用FOR/WHILE循环(见后文“执行时间限制”章节);

    • 禁止调用非异步安全函数块(FB)或函数(FC):仅允许调用标有“异步安全”(Async-Safe)属性的块(如系统函数 MOVE, TON, CTU 的ST版本);

    • 正确调用方式:

      // ✅ 安全:系统定时器TON,已标记为异步安全
      myTON(IN := bStartPulse, PT := PULSE_WIDTH_MS);
      bPulseActive := myTON.Q;
      
      // ❌ 危险:自定义FB若未勾选"异步安全",调用将导致CPU停机
      // "MyCustomFB"(IN1 := iCounter); // 编译可能通过,运行时报错16#8010
  4. 错误处理区(可选,但强烈建议)

    • 使用 IF ERROR THEN ... END_IF 捕获ST语句级错误(如除零、数组越界);
    • 注意:此ERROR仅捕获当前语句,不覆盖OB整体异常(如内存溢出仍会停机)
    • 示例:
      iResult := iDividend / iDivisor;
      IF ERROR THEN
          iResult := 0;
          bDivError := TRUE;
      END_IF

三、关键语法限制与避坑清单

以下为高频致错点,每一条均对应真实工程故障案例,必须逐条核对。

限制类型 具体规则 违反后果 实操对策
执行时间 单次OB执行时间 ≤ 1ms(S7-1200)或 ≤ 500μs(S7-1500高速模式);超时将触发 OB40 中断丢失并记录诊断缓冲区 CPU持续报 16#8001(OB执行超时),后续中断被丢弃 禁用所有循环语句;用查表法(ARRAY)替代FOR;用移位寄存器(SHL/SHR)替代计数累加;对长逻辑拆分为多级状态机,每次中断只推进1步
数据访问 禁止访问未优化的DB块(即DB块属性中“优化的块访问”未勾选);仅允许访问“优化的DB”或M存储区 运行时报错 16#800F(访问冲突),CPU停机 在DB块属性中 勾选“优化的块访问”;若需访问非优化DB,改用 AT 指令映射到临时变量后再操作
浮点运算 禁止在中断OB中执行复杂浮点计算(如SIN、COS、LN、EXP);仅允许 +, -, *, /, ABS 等基础运算 浮点协处理器占用时间不可控,极易超时 将复杂计算移至OB1主循环预计算并存入全局变量;中断OB中仅做查表或线性插值
字符串操作 完全禁止使用STRING、WSTRING类型及CONCAT、LEFT、MID等字符串函数 字符串动态内存分配引发不可预测延迟,必然超时 用固定长度的BYTE数组(ARRAY[0..19] OF BYTE)替代STRING;ASCII码值直接比较(如 aByte[0] = 16#41 代表'A')
地址计算 禁止使用ADR()、PEEK、POKE指令;禁止指针运算(如 pVar^ := 100 地址非法访问触发硬件保护,CPU立即停机 所有数据地址通过符号名直接访问;需动态索引时,用 ARRAY[0..n] OF INT + 整数下标

四、典型安全编码模板(OB40硬件中断)

以下为经TIA Portal V18实测通过的OB40 ST代码模板,满足全部硬性约束,可直接复制修改:

// OB40 - 硬件中断服务程序(DI通道上升沿触发)
// 执行时间实测:< 350μs(S7-1500 CPU 1511-1PN)

VAR_TEMP
    // 临时计数器(每次中断清零)
    iPulseCount : INT := 0;
    // 脉冲宽度计时器(TON实例)
    tPulseTimer : TON;
    // 状态标志(非保持)
    bPulseDetected : BOOL := FALSE;
END_VAR

VAR CONSTANT
    // 防抖时间(硬件滤波后仍需软件确认)
    DEBOUNCE_TIME : TIME := T#20MS;
    // 最大允许脉冲频率(防误触发)
    MAX_PULSES_PER_SEC : INT := 50;
END_VAR

// 主逻辑:检测有效脉冲并计数
// 步骤1:启动防抖定时器(上升沿触发瞬间)
tPulseTimer(IN := TRUE, PT := DEBOUNCE_TIME);

// 步骤2:防抖完成后确认脉冲有效
IF tPulseTimer.Q THEN
    // 步骤3:检查频率限制(使用全局DB中的计时器)
    IF "DB_Global".iLastPulseTime <> 0 THEN
        IF (TOD - "DB_Global".iLastPulseTime) >= T#20MS THEN
            iPulseCount := "DB_Global".iTotalPulses + 1;
            "DB_Global".iTotalPulses := iPulseCount;
            "DB_Global".iLastPulseTime := TOD;
            bPulseDetected := TRUE;
        END_IF;
    ELSE
        // 首次触发
        "DB_Global".iTotalPulses := 1;
        "DB_Global".iLastPulseTime := TOD;
        bPulseDetected := TRUE;
    END_IF;
END_IF;

// 步骤4:错误处理(仅针对本OB内可能出错的语句)
IF ERROR THEN
    "DB_Global".iOB40_ErrCnt := "DB_Global".iOB40_ErrCnt + 1;
END_IF

关键说明

  • 全部变量均位于 VAR_TEMP 区,无 VARVAR_IN_OUT
  • 未使用任何循环、字符串、浮点函数、指针;
  • TOD(当前时间)为系统常量,读取无开销;
  • "DB_Global" 为已启用“优化的块访问”的全局DB;
  • 防抖与频率限制逻辑均采用时间差比较(TOD - ...),避免循环等待。

五、调试与验证必做步骤

代码写完不等于可用。必须通过以下四项验证,缺一不可:

  1. 静态检查(编译前)

    • 在TIA Portal中右键OB → “检查块” → 确认无 Warning 22(非异步安全调用)或 Warning 15(变量声明违规);
    • 查看“块信息” → “属性” → 确认“语言”为 ST,“类型”为 OB,“优先级”与硬件组态一致。
  2. 执行时间实测(在线监控)

    • 下载程序后,在“监视表”中添加系统变量:
      "OB40".Runtime(单位:μs);
    • 强制触发中断100次 → 记录最大值;
    • 要求:最大值 < 80%阈值(S7-1500为400μs,S7-1200为800μs)。
  3. 中断丢失测试

    • 用信号发生器向DI通道发送1kHz方波(占空比50%);
    • 在OB40内对 "DB_Global".iTotalPulses 每次加1;
    • 同时在OB1中每秒读取该值并计算实际频率;
    • 合格标准:实测频率 ≥ 990Hz(允许1%丢失,超过即需优化)。
  4. 压力工况验证

    • 同时触发3个不同中断OB(如OB40、OB35、OB82);
    • 监控各OB的 Runtime 及诊断缓冲区;
    • 确认无 16#800116#800416#800F 报错,且各OB执行时间未显著增长

六、进阶技巧:跨OB数据同步安全方案

当需在中断OB与主循环OB1间共享数据(如传感器采样值),必须解决竞态问题。绝对禁止直接读写同一变量

  1. 双缓冲机制(推荐,零风险)

    • 创建全局DB,含两套相同结构的变量组:BufferABufferB
    • OB40只写 BufferA,OB1只读 BufferB
    • OB1末尾执行:BufferB := BufferA;
    • OB40开头执行:BufferA.NewData := TRUE;
    • OB1通过检查 BufferB.NewData 判断数据新鲜度。
  2. 原子标志位法(轻量级)

    • 使用单个 BOOL 标志(如 "DB_Sync".bDataReady);
    • OB40写完数据后执行:"DB_Sync".bDataReady := TRUE;
    • OB1检测到 TRUE 后立即读取数据,并立刻置 FALSE
    • 关键:标志位操作必须为单条ST语句(如 :=),确保原子性
  3. 绝对禁止方案

    • 在OB40中直接修改OB1使用的中间变量(如 "DB_Mid".fValue);
    • 在OB1中用 IF "DB_Mid".fValue > 0 THEN ... END_IF 判断——因读写非原子,可能导致 fValue 被部分更新,产生无效中间值。

七、常见报错速查表

错误代码 中文描述 根本原因 快速定位方法
16#8001 OB执行超时 循环、浮点函数、长数组拷贝 查看 OBx.Runtime 值;注释掉疑似语句分段测试
16#8004 优先级冲突 在低优先级OB中调用高优先级OB 检查所有 CALL 语句的目标OB编号
16#800F 数据访问冲突 访问非优化DB或地址越界 检查DB属性;确认所有数组下标在 [0..n]
16#8010 异步调用错误 调用了非异步安全FB/FC 在块调用处右键 → “属性” → 查看“异步安全”复选框
16#8020 堆栈溢出 VAR_TEMP声明过大(>2KB)或递归调用 减少临时变量;禁用所有递归逻辑

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文