← 所有文章

狀態進入時只執行一次 如何分開初始化與每掃描動作

· 站長

把狀態進入、持續與離開分開,以逐掃描案例確認初始化只執行一次。

先把三種動作分清楚

你在 PLC 裡做狀態機時,最常見的錯誤是把「進入狀態」和「留在狀態」寫在同一個條件。只要目前狀態等於 RUN,清除緩衝區的線圈就會每一掃描重複動作,累加值也可能被清成零。本篇先定義三類動作:進入只做一次、持續動作每掃描做、離開時清理一次。以下以 WAIT、RUN、DONE 三個虛擬狀態說明,程式碼是設計用偽碼,不能直接宣稱可在 Q 系列編譯。

動作類型 判斷條件 範例
進入初始化 目前狀態≠前次狀態,且目前=RUN 清除測試緩衝、載入本次起始值
持續動作 目前=RUN 每掃描累加取樣、檢查完成條件
離開清理 前次=RUN,目前≠RUN 關閉允許訊號、保存結果
  1. 先列出所有狀態與合法轉移,再為每個狀態寫進入、持續、離開三欄。

  2. 建立 state_prev 與 state_now;每一掃描結束才把 now 複製到 prev。

  3. 決定重新進入 RUN 是否重新清除緩衝;本例答案是「要」,但同一狀態停留不清除。

建立一次進入事件

在一掃描開始,先讀取目前狀態,再計算 entry_run。規則是 state_now=RUN 且 state_prev≠RUN。初始化放在 entry_run 分支;累加和逾時檢查放在 state_now=RUN 分支。掃描末端才更新 state_prev,這樣 RUN 連續十個掃描只會有一個進入脈衝。若上電後兩個變數都被初始化成 WAIT,第一次切到 RUN 也能產生進入事件。

教學偽碼,非指定PLC語法:
掃描開始:state_now := 上一掃描核准的pending_state
entry_run := state_now=RUN 且 state_prev≠RUN
exit_run := state_prev=RUN 且 state_now≠RUN
若entry_run:test_buffer:=0;sample_count:=0
若state_now=RUN且sample_valid:累加sample;sample_count加一
若exit_run:保存本次資料與離開原因
pending_state := 根據state_now及本次輸入選擇一個合法下一狀態
run_enable := state_now=RUN 且沒有停止或故障
掃描末端:state_prev := state_now
停止與故障直接遮罩輸出,不等下一掃描。
掃描 state_prev state_now entry_run 結果
S1 WAIT WAIT 0 不清除
S2 WAIT RUN 1 清除一次、開始累加
S3 RUN RUN 0 只累加
S4 RUN DONE 0 執行離開清理
S5 DONE RUN 1 重新進入,再清除一次

掃描模型的陷阱與測試

若同一掃描內先把 WAIT 改成 RUN,又立刻改成 DONE,掃描末端只看得到 DONE,RUN 的進入事件會漏掉。你必須選一個規則:限制每掃描最多一次狀態轉移,或改用事件佇列保存中間轉移。本篇採前者,所有轉移要求在下一掃描才生效;若設備真的需要同掃描多階段,請另設 transition_event 陣列並逐項消費。

測試情境 預期 不符時先查
RUN 停留 5 掃描 清除 1 次、累加 5 次 entry 條件是否誤用 state_now=RUN
RUN→DONE 執行一次離開清理 prev 是否在掃描末端更新
DONE→RUN 重新初始化一次 是否把 DONE 當成 RUN 的前態
異常跳 WAIT→DONE 不可執行 RUN 初始化 轉移表是否允許未定義路徑
重新啟動後 RUN 依規格選擇恢復或重新初始化 保持區與上電初始化規則

表格的同一案例設定S2進入RUN時sample_valid=1、sample=12,因此先清零再累加成12;S3加入8,累計20;S4進DONE時保存20,不累加;S5再次進RUN,sample_valid=0,所以清零後保持0。若每掃描都清除,S3只剩8,就能看出錯誤。

實際寫圖時,把每個狀態的進入動作集中在同一網路或同一個功能區,並在旁邊註明觸發來源。不要在不同子程式各自修改 state_prev,否則先後順序會讓 entry_run 不可信。若有中斷或通訊任務會改 state_now,應先把請求寫入 next_state_request,由主循環統一核准;這樣一掃描一次轉移的規則才成立。狀態完成後若仍收到舊的完成脈衝,必須以 job_id 或目前狀態拒絕,避免上一批次的訊號觸發下一批次初始化。測試時也要故意在 entry_run 那一掃描觸發警報,確認初始化資料已清除但虛擬輸出仍被故障條件遮罩。

操作流程與驗收

本例在掃描開始採用上一掃描已核准的狀態,再比較state_prev與state_now;掃描內只決定一個pending_state,下一掃描生效。離開事件以prev=RUN且now≠RUN判斷。上升緣也可用「state=RUN」布林條件實作,但仍需正確保存前次值。

當你把這套規則搬到實際專案,請先用一個不連接設備的虛擬請求做逐掃描驗證。每次只允許主循環在掃描末端提交 next_state,下一掃描才成為 state_now;因此 WAIT 到 RUN、RUN 到 DONE 不會在同一掃描跳過邊界。若確實需要立即跳轉,請把每次轉移包成事件並設定最大事件數,佇列滿時產生溢位警報。對每個進入事件配置 enter_sequence,初始化完成才設為有效;初始化失敗時保留在 INIT_FAULT,不得假裝已進入 RUN。對離開事件也要設定 cleanup_done,防止通訊重送或重複邊沿再次保存結果。這些欄位要放入監看表,並在測試紀錄寫出掃描編號、前狀態、後狀態、事件計數與資料值。如此即使日後修改狀態圖,也能快速找出是哪一條轉移破壞了一次性規則。

  1. 畫出狀態轉移圖,標記每一條邊的觸發條件。

  2. 在監看表同時放 state_prev、state_now、entry_run、exit_run、test_buffer。

  3. 先以強制的虛擬請求做 WAIT→RUN→DONE→RUN,逐掃描記錄表格。

  4. 再測試重複請求、非法轉移、逾時及重新啟動;把每個結果寫入測試紀錄。

操作流程與驗收 續

實作時請把狀態轉移寫成一張可逐格驗證的表。假設按下啟動後,WAIT 在 S10 變成 RUN,S11 到 S14 都維持 RUN,S15 因完成條件變成 DONE。S10只能清除一次,若當次樣本有效則在清除後累加;S11~S14 每掃描累加一次;S15 要先保存結果再關閉輸出。若啟動按鈕在 S12 仍保持 ON,不應重新初始化,因為啟動是請求而不是進入事件。你可以在監看表加一個 entry_counter,每次 entry_run=1 才加一,測試五次重入後應為五。
邊界案例一是兩個條件同時要求轉移,例如完成與停止同時成立。規格必須給出優先序,本例採故障最高、停止其次、完成最後;因此同一掃描若 stop 與 done 同時為真,下一狀態是 WAIT,下一掃描辨認離開RUN並清理,本次先撤銷輸出,不執行 DONE 的完成處理。邊界案例二是狀態碼被通訊寫成 99,程式不可把它當成 RUN;應進入 SAFE 或 FAULT,關閉需要互鎖的輸出並留下原始碼。邊界案例三是重新啟動時 state_prev 與 state_now 不一致,先以初始化規則把兩者同步,再決定是否要求操作者重新啟動工作,避免上電瞬間誤發 entry。

完成後應看到什麼結果

RUN 的清除或載入動作只在 entry_run=1 的一個掃描發生;RUN 期間累加值連續增加;離開時只保存一次;再次進入 RUN 才重新建立初值。

失敗時先查哪裡

先看 prev 是否真的保存上一掃描,再看狀態是否被同一掃描內多次改寫,最後查上電初始化、保持裝置與非法轉移的處理。不要先用延時掩蓋狀態邊界錯誤。

適用型號與限制

概念適用於 Q06UDVCPU 等以循環掃描執行順序控制的系統;實際語言、保持範圍、任務或中斷行為須依 CPU 型號、參數與 GX Works 版本核對。

常見問題與來源

FAQ

問:可以用上升緣嗎?可以,對「state=RUN」條件取上升緣等同辨認進入,但前次狀態與初始化仍需正確管理。

問:RUN內能再次清除嗎?只有明訂的子週期重置才可,否則累加資料會遺失。問:同掃描跳過中間狀態怎麼辦?本例限制一次轉移,若應用需要多次轉移,應另設有容量與溢位規則的事件記錄。

參考:三菱 QnUCPU 使用手冊 程式執行與裝置資料

延伸閱讀


使用 PLC 工具箱 →