← 所有文章

子程式沒有每掃描執行 內部計時與邊緣判斷會怎樣

· 站長

用每100ms時間表比較條件跳過呼叫與每掃描呼叫,追蹤R_TRIG前值、輸出保留、恢復和計時策略。

先分清兩種條件呼叫

子程式被條件分支跳過時,其中靠呼叫更新的狀態不會執行。本篇比較 A 只在 Enable 為真時呼叫,與 B 每掃描呼叫但由外層決定是否採用輸出。先用自訂邊緣模型追蹤前值,再決定停用期間要繼續取樣、凍結,還是恢復時重新同步。外部時鐘仍可能前進,因此不能把未呼叫等同所有計時都暫停。

以CODESYS R_TRIG作平台參考,功能塊需要在實際呼叫時取得CLK並更新記憶;官方文件不支持未呼叫時在背景執行的推論。若程式沒有呼叫實例,就不能期待捕捉每個掃描的輸入變化。

呼叫圖要標出每個條件分支與實例名稱,因為功能塊只在通過該分支時才有執行證據。監看時先看呼叫點,再看輸入與輸出,才能分辨是資料錯還是根本沒有進入。

本例的基礎模型固定為:每次呼叫先算 Pulse=CLK AND NOT Previous,再令 Previous=CLK;不呼叫時兩個欄位都保留。Previous 初值為 FALSE。它是教學用狀態模型,沒有 Reset 腳位;下文另加的同步與 Reset 都是自訂包裝層,不能當成標準 R_TRIG 的介面。

重新呼叫時若時間缺口過大,可回報TIME_GAP並重新建立前值。直接把長時間缺口當成一個普通掃描,會使事件時間線難以解釋。

下列表格逐列表示連續時間,必須沿用上一列的 Previous,只有換成另一條獨立測試才重新初始化。請依指定的時間與輸入序列逐列核對;偽碼須依目標 PLC 語法調整。

100ms時間線與前值更新

輸入在0、100、200、300、400ms為0、1、0、1、1。A在100、200ms跳過,B每100ms呼叫,兩實例起始Previous均為0。A的前值在跳過期間保持0,所以300ms重新呼叫時把CLK=1判成上升;B在100ms判斷、200ms更新成0、300ms才再次產生上升。

時間 ms CLK A 呼叫/前值前→後/Pulse B 前值前→後/Pulse
0 0 是/0→0/0 0→0/0
100 1 否/0→0/0 0→1/1
200 0 否/0→0/0 1→0/0
300 1 是/0→1/1 0→1/1
400 1 是/1→1/0 1→1/0

表中的 Pulse 是實例保存的輸出,不是已發送到下游的事件。本例 A 在跳過前恰好為 0,所以跳過時仍為 0;若先在 100 ms 呼叫得到 1,再於 200、300 ms 跳過,Pulse 便會保持 1。外層應以本次確實呼叫及允許採用等條件控制事件,不能每掃描見到保留的 1 就累加。

若子程式返回後輸出仍保留上一值,畫面上的Pulse可能長時間像真事件。若外層每掃描清零,又可能讓事件被清除後下游尚未讀到。輸出生命週期需要明確的單次事件契約。

若輸入在兩次呼叫間快速往返,兩種設計都可能漏掉事件;差別只在已呼叫的模組能否看到取樣點。要捕捉短脈衝,應查硬體或高速任務規格。

A和B交換呼叫順序後,獨立實例的結果應維持;若結果改變,查共享全域變數、診斷結構、輸出暫存或同一個功能塊實例。

時間表的呼叫前Previous、呼叫後Previous、Q與Enable都要記錄。若A只在300ms看到高電位,結果表達的是恢復時的判斷,不是證明輸入在300ms才上升。

啟用輸入不等於跳過實例

若停用期間仍要追蹤輸入,只是不讓事件往下游傳遞,可每掃描呼叫R_TRIG,再用Enable AND Q採用事件。Previous仍更新,恢復時不會把停用期間維持的高電位誤判成新事件。

若需求是凍結狀態,才跳過呼叫;恢復時要選接受高電位、先同步前值或回報狀態缺口,這不是R_TRIG自動決定的政策。

再做一條獨立測試:0 ms 的 CLK=0,100 ms 變成 1 後一直維持;Enable 在 100、200 ms 為 FALSE,300 ms 恢復。基礎 A 在 300 ms 得 Pulse=1,B 的原始 Pulse 在 100 ms 已出現,但當時被 Enable 抑制;300 ms 時 B 得 0。這個差異才是停用期間維持高電位的重點。

Reset和初始化不是同一件事。初始化可在第一次啟動建立前值,Reset則是運轉中的明確命令;兩者同掃描出現時的優先序必須寫在需求。

計時也要先選契約。例如在 0 ms 起算、期限 300 ms,100 與 200 ms 不呼叫:自訂絕對期限模型在 300 ms 恢復時,以 Now-Start>=300 判定到期;自訂有效取樣模型若缺口作廢,則先回 TIME_GAP,不直接宣布到期。兩種都可描述需求,但都不是對所有平台 TON 的保證。

每次恢復都要記錄是否採用新事件,並讓下游知道事件來自正常取樣還是恢復同步。這個欄位能避免維護人員把補判結果誤認成原始邊緣。

TON不能泛稱一定累加掃描或未呼叫一定停住。Q與ET須在實際呼叫語意下核對;無證據時用自訂狀態偽碼明寫每次呼叫才更新,不能冒充現成TON。

恢復 Reset與遺失事件

若輸入由0變1又回0,A完全未呼叫便無法知道事件;恢復時CLK=0,單一目前值不能還原短脈衝。這是取樣限制,需要另查硬體捕捉或高速任務。

若希望恢復的高電位不自動形成命令,可在基礎 A 外再加一個同步包裝層 A_sync。第一次恢復只令 Previous=CLK、Pulse=0;後續連續呼叫才使用邊緣公式。前面時間表仍是未包裝的 A,因此 300 ms 為 1;改用 A_sync,同一列必須改為 0。不要把兩個策略混成同一份驗收答案。

在100ms呼叫時,實際Now可能是97或114ms;時間表只是驗收框架。若控制器要求準確逾時,保存可信時間戳,不能由計數器名稱推定精度。

驗收證據至少包含呼叫條件、CLK、Previous前值、Previous後值、Q、輸出採用旗標和錯誤狀態。只截最後一個Q位元不足以重建事件。

若需每次事件只通知一次,事件採用後要由接收端或事件緩衝明確清除。讓Pulse一直保留,會把一個事件誤當成持續命令。

若選擇重新建立前值,第一筆恢復資料只作基準,不輸出事件;第二筆連續有效資料才恢復一般判斷。這是本案例的明確政策。

本例包裝層的 Reset 優先於一般判斷:當次令 Previous=CLK、Pulse=0,並清除缺口診斷;它不把 Previous 一律清成 0。若 CLK 持續為 1,解除 Reset 後仍不出新事件;必須先觀察到 0,再觀察到 1。失敗先查是否誤用清零策略、是否共用實例,以及呼叫是否真的發生。

驗收 FAQ與平台限制

完成後應能說明A為何300ms才判斷,B為何100與300ms各出一次。測試包括全程不呼叫、恢復CLK為0、恢復CLK為1、停用短脈衝和Reset同掃描;記錄呼叫次數、CLK、Previous、Q、Enable、Reset。

情境 A策略 B策略 重點
正常連續呼叫 依邊緣公式 依邊緣公式 前值逐列延續
跳過兩次 前值與輸出保留 仍取樣 A 可能漏事件
停用但仍呼叫 已不屬於基礎 A 外層 Enable 抑制 B 前值仍更新
A_sync 恢復且 CLK=1 同步前值,Pulse=0 前值已持續更新 區分基礎 A 與包裝層

FAQ:一、未呼叫R_TRIG會背景更新嗎?不能假定。二、每掃描呼叫但Enable為假與跳過相同嗎?不相同。三、TON未呼叫一定累加或停住嗎?不能泛稱,依官方文件與平台測試。四、恢復能補回漏掉短脈衝嗎?不能,未取樣資料已不存在。

適用限制:CODESYS Function Block、R_TRIG與TON文件作查閱來源;Q系列和其他PLC細節需依CPU核對。

參考:CODESYS Function Block

參考:CODESYS R_TRIG

停用期間仍呼叫的設計適合需要追蹤輸入的模組;跳過呼叫的設計適合刻意凍結狀態,但都要記錄停用起點、恢復時刻與是否允許補判。

若移植到另一個PLC,先查功能塊是否需要實例、時間型別的解析度、未呼叫時輸出是否保留、Reset命令及任務排程。不能以相同指令名稱推論相同語意。

本篇不涉及安全功能。任何與設備動作相關的輸出,都要經過獨立互鎖、模式和安全控制設計,不能由R_TRIG或TON的呼叫策略代替。

參考:CODESYS TON

延伸閱讀


使用 PLC 工具箱 →