OpenPLC ラズパイでPLC 自動動作の追加
<その2>で作成した仕様に自動動作を追加してみます。お題目の自動運転仕様は場合右端からスタートして、左端に到着(LS2がON)したらLS3(2進数1の桁)、LS4(2進数10の桁)、LS5(2進数100の桁)・・・0~7の値を取得して、5秒停止(停止中はPL1点滅)。その後右方向に移動して、右端(LS1がON)になると取得したLS3~LS5の値(0~7)に1を加算してDP1に表示(1~8)後自動運転を終了する動作とする・・・・昔どっかの検定試験問題集に似たような仕様があった様な・・・動作の条件等は
1:手動・自動スイッチ(SS0)が手動側の場合は先の手動操作で自動側の場合、コンベアが右端(LS1がON)の状態のみPB1を押すことで自動運転開始が開始される。このときPL1が点灯、DP1の値は「0」にクリアされる。
2:動作中にPB5(非常停止)が押された場合は自動運転を中止して停止する。
3:動作中に手動・自動スイッチ(SS0)が手動に変更された場合も自動運転を中止して停止する。
4:自動運転完了時はPL1は消灯する。
5:手動にてもPB5(非常停止)がONしている限りは動作しない。
以上の動作を実装していきます。
OpenPLCのプロジェクトに回路を追加
1:まず自動の回路を作成します。新たにPOUとして「AutoCont」、それの状況、他絵の要求信号を外部に明示する構造体「sAuto」、そして構造体をグローバル変数「G_sAuto」としてインスタンス化します。この自動回路の「G_sAuto」の信号を他のPOUに組み込んでいきます。
変数の内容は「Run」は自動実行中、「stgMovL」・「stgLwait」・「stgMovR」・「stgAutoE」は自動動作の状態変数、「iNum」は取得したLS3~5の値を保持します。
状態変数は自動動作の順にシーケンスを進めていく形でONしていきます。IEA61131-3ではこのシーケンスを進めていく方法に「シーケンシャル・ファンクション・チャート(SFC)」なる方法も定義されています。SFCも各メーカーPLCにおいてサポートされていますが、店主はあまり使ったことありませんので割愛です・・・。多分ラダーにおける表現でも自動の進行は状態遷移であって、左側に記述する接点はシーケンス進行の条件、右側のコイルはシーケンスの状態を表しますので意外とSFCよりラダーもしくはSTの方が複雑なことは実現しやすかもしれません。
ラダーであってもSFCであっても、C言語等であってもほぼ制御構造は同じなので、基本的にはステートマシンを組めばOKデス!!(間違っているかもしれませんが・・・・・)。
次にラダー回路の入力です。自動の条件を検出して、条件がそろえば「Run」を自己保持させて自動を開始します。「AutoCont」のPOUを作成して回路を記述していきます。回路の詳細はコメント記述してあります。
2:手動の動作にPB5の条件を追加します。将来的に他から手動が動作している状態を明示するためsManuの構造体に手動動作中フラグを追加します。またこのフラグがONの場合のみ手動動作要求を出力するように変更します。まずsManuにRunを追加します。
次に回路を変更です。手動動作中のコイル(G_sManu.Run)を追加して、条件はSS0が手動側とPB5がON(通常非常停止ボタンはB接点仕様)としておきます。
3:次はコンベアのプログラム「ConverObj」に自動の要求でも動作するように接点を追加します。
また参照する変数で「G_sAuto」も追加しておきます。
4:次にランプ回路ですが、手動/自動ともにPL1を共通に使用して動作は異なっていますので、ここもコイル(Localな内部メモリ)を手動時のManuL、ManuRと自動時のAutoを追加します。
5:7セグメント出力を担当するオブジェクト「DP0Obj」を作ります。ここでは自動の開始時・終了時に(G_sAuto.stgAutoE)に保持値(G_sAuto.wNum)をDP0に出力します。ちょっと変えてLDではなくSTで書いて見ました。Projectで「+」をクリックして「Program」を追加でPOU名は「DP0Obj」、POUタイプは「Program」、Languageは「ST」で作成します。
作成できたら利用する変数を定義して、プログラムをST言語で作成します。
少しエラーで怒られた点は「;」の忘れでした・・「END_IF」にも必要です。
ロジックIC設計のHDLによく似た言語です。コメントが書きづらいので、この部分のコードは
=====================================
R_TRG1( >>自動の左移動開始で7セグメント表示クリアのタイミング生成
CLK := G_sAuto.stgMovL,
Q => TRG1);
R_TRG2( >>自動の終了で保持値を7セグメント表示のタイミング
CLK := G_sAuto.stgAutoE,
Q => TRG2);
IF TRG1 OR TRG2 THEN >>実行タイミングが発生すれば
wTmp := INT_TO_WORD(G_sAuto.iNum);
>>ビット判定はブール型ビット列が必要みたいなので変換を実行します。
IF ((wTmp AND 1) <> 0) THEN >>0ビットの判定をしてON/OFF
G_sIO.DP1 := TRUE;
ELSE
G_sIO.DP1 := FALSE;
END_IF;
IF ((wTmp AND 2) <> 0) THEN >>1ビットの判定をしてON/OFF
G_sIO.DP2 := TRUE;
ELSE
G_sIO.DP2 := FALSE;
END_IF;
IF ((wTmp AND 4) <> 0) THEN >>2ビットの判定をしてON/OFF
G_sIO.DP4 := TRUE;
ELSE
G_sIO.DP4 := FALSE;
END_IF;
IF ((wTmp AND 8) <> 0) THEN >>3ビットの判定をしてON/OFF
G_sIO.DP8 := TRUE;
ELSE
G_sIO.DP8 := FALSE;
END_IF;
END_IF;
=====================================
で・・・注意事項??になりますが、STでの入力で右側のLibraryタブから「INT_TO_WORD」をテキストのカーソル位置にドラッグすると次のようにコードを生成します。
===============
INT_TO_WORD(
IN := (*INT*),
OUT => (*WORD*))
===============
ここで(*INT*)と(*WORD*)を変更するのですが・・・・・
INT_TO_WORD(
IN := G_sAuto.iNum,
OUT => wTmp);
「Generate Program …..」を実行するとエラーになります???
使い方が間違っているのかどうか不明ですが、次の様に記述すればOKでした。
wTmp := INT_TO_WORD(G_sAuto.iNum);
===============
6:最後に追加したプログラム「AutoCont」と「DP0Obj」をリソースで追加しておきます。
7:これで「Generate Program …..」の下矢印アイコンをクリックして実行ファイルを生成します。
「AutoCnv」で保存しました。
<その3>まとめ
「OpenPLC」を使って店主なりに、ラダープログラムの組み方みたいなものをまとめてみました。
OpenPLCでの不便な点は、デバッグでしょうか・・・OpenPLCサーバーでモニターできるデバイスの操作もやりにくいですし、OpenPLCエディター上のデバッグもいまいち使いにくい状況でした(市販のPLCツールと比較してしまうからでしょうか?・・使い方を知らんだけかも・・)。ただ、一気に一つのプログラムで大きなものを組まずにできるだけオブジェクト単位で記述して、オブジェクト単体でデバッグをしてしまえば意外とたやすく構築できると思います。また、ST言語でのFB呼び出しでも少し悩みましたが、単純なプログラムをプロジェクトとして作って、思考錯誤して見れば分かり易いかもしれません。またデバッグ時は実アドレスに出力して確認しますが、完成時はできるだけLocalな割り付けのない自動割り付けのメモリーに展開しておいた方がいいかもしれません(外部から触られることが無くなるので・・・)。
もう一つ、OpenPLCではラダーのブロックも任意の場所(プログラムの画面の位置)に書くことができるので、できるだけスキャンを利用したトリッキーな回路は書かず遠回りでも単純なロジックで表現していく方がいいようです。少しトリッキーにした場合は一度「Generate Program …..」で出力されたST言語を見ておいた方がいいかもしれません。
<その4>・・・別のお題目で・・・へ続く