FANUC PMCプログラミング入門(第2回)

# 機能命令

[前回](https://qiita.com/oshino_maru/items/c51b7455b1278b51fb36)の記事ではPMCの基本命令を使った信号の論理演算を紹介しました。ビット演算だけでは複雑な処理を実現することは困難です。そこで複雑な処理を容易に実現するために「機能命令」が提供されています。

## 機能命令の構成

機能命令は以下のような四角い箱でラダー図内に配置します。

![PMC#1-機能命令.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390544/53fee913-a1f7-02e8-a7a5-7bda203f5de2.png)

* 制御条件  
機能命令実行の条件を枠の左側に接点を繋げて入力します。  
命令によって入力する条件数が異なります。  
特に多いのがACT入力です。ACT入力がONの時に命令が実行されます。  
制御条件が沢山ある機能命令もON固定、OFF固定で入力する場合が多いのでZ0091.1(常時ON), Z0091.0(常時OFF)のシステムリレーを活用しましょう。
* SUB番号  
機能命令には番号が割り当てられます。機能命令を配置する際には命令名か番号のいずれかで指定します。似たような名前も多いので、慣れると番号の方が便利だったりします。  
番号は1から最大は約500程です。とはいえ500個も機能命令を覚える必要があるわけではありません。欠番や類似の命令、使用するデータ形式が異なるだけの共通の命令とグループ化されます。頻繁に使用する数個から数十個覚えれば十分でしょう。
* 命令名  
機能命令の機能を表すアルファベット数文字の短い文字列で構成されます。
上の図では「NUMEB」はバイナリ定数設定命令を表します。
* パラメータ  
機能命令の枠の右側には機能命令のパラメータを指定します。C言語で言えば関数の引数のようなものです。  
NUMEBの場合、「データサイズ」「設定値」「設定先のアドレス」の3つのパラメータを指定します。  
上記の図ではACT入力がONの時にD0010信号に定数100を1byteセットします。   
(ACTには常時ONのシステムリレーを指定しているので同ネット実行時に必ずセットされます)
* 出力  
機能命令の結果は出力側のネットに配置される出力コイルに書き込まれます。  
(命令の出力コイルはW1と呼びます。)  
また一部の演算処理関連の機能命令の結果はシステムリレーのZ0000(R9000)-Z0005(R9005)に出力されます。
(※NUMEBの場合出力コイルおよび演算処理はありません。)

## タイマ命令

機械制御において入力条件がONしている間の時間を内部で加算し、設定時間に到達したら出力コイルをONする機能を**ONディレータイマ**と呼びます。PMCラダープログラムではONディレータイマを実現するのにタイマ命令を使用します。

![PMC#1-オンディレイタイマ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390544/40b66472-05ed-a203-becd-360beedd7b03.png)

タイマアドレスによる遅延時間の分解能の違いや数種類のタイマ命令があるので少し混乱しますが、使用頻度の高い重要な命令です。

### TMR/TMRB命令を使ったONディレイタイマ

ONディレイタイマを実現するには一番基本的な可変タイマ命令のTMR(SUB3)と固定タイマ命令のTMRB(SUB24)、さらにTMRC(SUB54)の3種類があります。
いきなりどれ使えばいいのかわからないですね…TMRCは上級者向けなので一旦無視しましょう。
TMR/TMRBは、

* TMR : タイムアップ時間を変数で指定したい場合
* TMRB : いちいちタイムアップ時間を変数にセットするのが面倒。ラダーに直接書き込みたい場合。

と使い分けます。

#### TMR

TMR命令はタイムアップ時間をタイマ番号に紐づけて管理します。

![PMC#1-TMR (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390544/70d6edfb-bb23-5756-3598-15c6491fd390.png)


タイムアップ時間をラダー内に直接記述する必要はなく、外部からの設定やラダープログラムで可変な時間を設定することが出来ます。一つ注意が必要な点はタイマ番号はタイマアドレス(T****)の数字とは異なるという点ですタイマアドレス2バイト分で1つのタイマを構成します。
これはPMC画面でのタイマ設定画面のイメージです。各タイマ番号に対して設定値を入力するとそのタイマ番号を使用するTMR命令に反映されます。
![PMC#1-タイマ画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390544/76c68d82-1fc2-9c71-a2ac-41ef6e7077c8.png)
設定値は精度(ミリ秒)の倍数で指定します。例えば上のTMR命令の場合タイマ番号は5番を使用していたので2400ミリ秒(2.4秒)のONディレイタイマになります。
※対応するTアドレスに対して2byte書き込みでも設定可能です。
Tアドレスの値はキープリレー同様不揮発データになるため電源オフでも保持されます。

#### TMRB

TMRB命令はTMR命令とほぼ同じ動作ですがパラメータが一つ多くて機能命令の枠の中に直接タイムアップ時間を登録します。タイマ設定画面の設定値などは使用しません。
下の図は先ほどのTMR命令と動作は同じですが、タイマ番号にタイムアップ時間を設定する必要がありません。
プログラム時にタイムアップ時間が決定している場合などはこちらを使用した方がいいでしょう。

![PMC#1-TMRB (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390544/83bfab6d-9b4e-59e5-ff78-fae90d43970f.png)


タイマには他にもOFFディレイタイマや積算型のタイマ命令があります。必要に応じて使い分けるといいでしょう。
一つ注意点ですが各種タイマ命令で使用するタイマ番号はユニークにする必要があります。重複しないように注意しましょう。同じタイマ番号を同時に使用すると正しく動作しない場合があります。

---

## カウンタ命令

カウンタは入力信号がONした回数をカウントし、設定値に到達すると出力コイルをONする機能です。

カウンタ動作の一例に以下のようなものがあります。

![PMC#1-カウンタ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390544/519321e7-99df-e376-3ab0-d931980dbb52.png)

この場合、出力をONするカウントアップ値を**プリセット値**と呼びます。

PMCプログラムで使用するカウンタ命令は4種類あります。

| SUB番号 | 命令名 |                 |
|---------|--------|-----------------|
|5        | CTR    | カウンタ命令     |
|56       | CTRB   | 固定カウンタ命令 |
|55       | CTRC   | カウンタ命令(アドレス指定/2byteカウンタ) |
|57       | CTRD   | カウンタ命令(アドレス指定/4byteカウンタ) |

4つの命令の違いはカウンタ動作のプリセット値（カウントアップ値）の扱いの違いになります。
CTRはカウンタ番号に紐づけてプリセット値を設定します。
CTRBはカウンタ番号を指定しますがプリセット値をパラメータとして直接指定します。プログラミング時に直接記入するという点でTMRB命令と同様ですね。
CTRCとCTRDはカウンタ番号を使いません。プリセット値を任意の信号領域に予め設定しておき、そのアドレスをパラメータに設定します。

### CTR

CTR命令は4つの制御条件と1つのパラメータを持ちます。パラメータにはカウンタ番号を指定します。

![PMC#1-CTR (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390544/e17f8621-741c-076c-60d9-b39d95309ace.png)

制御条件（入力リレー）は以下の4個です

* CNO   
リセット時のカウンタの初期値を指定します。  
(OFF:0, ON:1)

* UPD  
カウントアップ・カウントダウンの動作を指定します。  
(OFF:カウンタアップ,ON:カウントダウン)

* RST  
カウンタ積算値を初期化します。

* ACT  
カウント指示します。  
OFF: カウンタは動作しません。出力コイルの状態は変化しません。  
ON: ACT信号の立ち上がりにカウンタ動作します。 

制御条件はCTRB,CTRC,CTRDも同様になります。

以下の図はPMC画面でのカウンタ設定画面のイメージです。各カウンタ番号に対して設定値を入力するとそのカウンタ番号を使用するCTR命令に反映されます。
カウンタの積算値もこの画面で確認することが出来ます。

![PMC#1-ページ16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390544/16edb0e2-3e7b-4edb-9f1a-c6335c621ed3.png)

カウンタ番号1つにつきカウンタアドレス4byteが割り当てられていますね。つまり設置値と積算値を2byteずつ管理しています。

### CTRB

CTRB命令は固定カウンタとしてプリセット値をパラメータに直接指定します。
それ以外はCTR命令と同じです。

![PMC#1-CTRB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390544/677cadb7-2060-0345-087d-c71431c021ee.png)

> ※バイナリカウンタとBCDカウンタについて  
マニュアルを見るとCTR命令はカウンタデータを**BCD**として扱うことができる（但しパラメータ設定が必要）のに対し、他のカウンタではバイナリデータのみと記載があります。
ちょっと意味ﾜｶﾝﾅｲﾝｽｹﾄﾞ:disappointed_relieved:  
BCDとは10進数のデータを表現する形式の一つで, 0～9の1桁を4ビットで表現します。例えば15の数字であれば0x15となりします。
BCDの利点としては10進に馴染み深い人間が読みやすい？という点と、浮動小数を10進に変換する際の誤差を防止するために利用出来るという二つの点が挙げられます。(詳しくは[Wikipediaの記事](https://ja.wikipedia.org/wiki/%E4%BA%8C%E9%80%B2%E5%8C%96%E5%8D%81%E9%80%B2%E8%A1%A8%E7%8F%BE)を参考)  
しかし整数カウンタ処理でデータをBCDで扱っても利点が少ないのでCTRより後に追加された命令ではサポートされなくなっているようです。  
CTRもバイナリ形式でカウントし、BCD形式表現を用いたいときだけバイナリ⇔BCD変換命令(TBCD*,FBCD*)を使った方が余計なトラブルを回避出来て良いでしょう。

### CTRC/CTRD

CTRCとCTRDはカウンタ番号(Cアドレス)を使わず、プリセット値および積算値をCアドレス以外の信号領域に確保して使用します。パラメータには信号領域のアドレスを指定します。

![PMC#1-CTRC (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390544/52ccc2b7-655c-721b-c2d2-a21a671481ea.png)

CTRCは2byteカウンタでプリセット値の最大は`32767`(0x7FFF)、CTRDは4byteカウンタでプリセット値の最大は`2147483647`(0x7FFFFFFF)です。

積算値アドレスは積算値(2byte or 4byte)の後ろ2byteがワーク領域として確保しておく必要があります。ご注意ください。

## データ転送命令

### 信号間データ転送 (MOVB, MOVW, MOVD, MOVR, MOVL)

MOVB, MOVW, MOVD, MOVR, MOVLはパラメータ指定した転送元アドレスから転送先アドレスへデータを転送します。それぞれの命令の違いは扱うデータ長の違いになります。

C言語では以下のような処理になります。

```c
// MOVB
if ( ACT )
{
    *(unsigned char*)PRM2 = *(unsigned char*)PRM1;
    W1 = true;
}
```

PMCラダープログラムでは以下のように記述します。

![PMC#1-MOVB (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390544/a6e6cb41-6856-9e80-d94d-498d503e1492.png)

ACTがONの時に転送元から転送先にデータをコピーします。転送完了時に出力コイルがONしますが、**ほとんど意味がない(ACTと同じ)**ので省略することが多いです。
（MOV系のようにいくつかの機能命令は出力コイルを配置することも省略することも可能です）

例：出力コイルを省略したMOVD命令
![PMC#1-MOVD.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390544/47263dad-fbba-7bf4-4e19-bbdc36930479.png)

| SUB番号 | 命令名 |                 |
|---------|--------|-----------------|
|43       | MOVB   | 1byteデータ転送 |
|44       | MOVW   | 2byteデータ転送 |
|47       | MOVD   | 4byteデータ転送 |
|350      | MOVR   | 単精度浮動小数(4byte)データ転送 |
|351      | MOVL   | 倍精度浮動小数(8byte)データ転送 |

### MOVN

任意バイトをコピーする際にはMOVNを使用します。
C言語では以下のような処理になります。

```c
// MOVN
if ( ACT )
{
    memcpy( PMR3, PRM2, PRM1);
    W1 = true;
}
```

PMCラダープログラムでは以下のように記述します。

![PMC#1-MOVN.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390544/fe15446f-1ab4-0e35-8fc9-b5b04b977791.png)

> ※ オールマイティ?
「MOVNを使えば別にMOVB/MOVW/MOVD...とか覚えなくても、これでオールマイティじゃん」と思いますよね。
まあ、そうなんですが命令によって処理速度に差があります。MOVN 4バイト転送よりMOVD転送命令の方が高速ですのでラダープログラムが大きくなりすぎで速度チューニングが必要になった場合には不要なMOVN命令が無いか見直した方がいいでしょう。

### SETB,SETW,SETD

MOVB,MOVW,MOVDとそっくりな命令としてSETB,SETW,SETDがあります。制御条件がACT一つで出力コイルW1(省略可), パラメータは転送元と転送先の二つというものです。

SET系は「転送元は信号アドレスでも定数でもどちらでもよい」という点が異なります。本記事最初に機能命令のサンプルにあるNUMEBというのがありましたが、これは定数を指定バイト数で転送先にセットする命令です。

それにしても似たような機能命令が複数あるので混乱しますね。:thinking:
表にまとめると以下のようになります。
![PMC#1-転送命令.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390544/13f907b8-fec1-4448-2eea-fee3ee5461af.png)

データ転送一つをとっても同じことを実現するために複数の手段があります。それぞれプログラムのサイズや実行速度など細かい違いはありますが、**「こういった場合にはこの命令を使うべき」**といった細かいお作法はありません。
それより大事なのは**「作成するプログラムの中で使用する命令に一貫性を持たせること」**です。自分以外のエンジニアがラダープログラムを読むときの負担を軽減するように心がけることが大切です。

※ MOVNとSETNBは転送元アドレスがインクリメントされないなど動きが異なるものもあります。

第2回では機能命令の使い方を説明しました。また別の機会に今回取り上げなかった他の機能命令も紹介したいと思います。

次回は「上から下に流れるだけのラダープログラム」で構造化プログラムにおける分岐やループ、関数処理のような制御文をどう実現するか紹介したいと思います。

---

[目次へ](https://qiita.com/oshino_maru/items/0ae69da0c668efd5061c)

※読んでいただきありがとうございました。質問・修正点等ありましたらお気軽にご連絡ください。
正直ニッチ過ぎるネタを取り上げたかなと少し後悔もしているのでLGTMを押していただけると嬉しいです。