
概要
============

LDmicroはラダー図からPIC16やAtmel AVRのネイティブコードを生成するツールです。
マイコンの制御ソフトウェアは通常アセンブラやC、BASICなどのプログラミング言語で書かれます。これは命令文を並べることで動作を定義します。これは機械語命令を順番に実行するというマイクロプロセッサ内部の仕組みをそのまま反映したものといえます。

一方、プログラマブルロジックコントローラ (PLC)はラダーロジックというものを使ってプログラムされます。例えば、単純なプログラムは次のようなものです。

   ||                                                                    ||
   ||    Xbutton1           Tdon           Rchatter           Yred       ||
 1 ||-------]/[---------[TON 1.000 s]-+-------]/[--------------( )-------||
   ||                                 |                                  ||
   ||    Xbutton2           Tdof      |                                  ||
   ||-------]/[---------[TOF 2.000 s]-+                                  ||
   ||                                                                    ||
   ||                                                                    ||
   ||                                                                    ||
   ||    Rchatter            Ton             Tnew           Rchatter     ||
 2 ||-------]/[---------[TON 1.000 s]----[TOF 1.000 s]---------( )-------||
   ||                                                                    ||
   ||                                                                    ||
   ||                                                                    ||
   ||------[END]---------------------------------------------------------||
   ||                                                                    ||
   ||                                                                    ||

--[TON]-- はオンタイマーを表します。一定時間1を入力すると1を出力します。
--[TOF]-- はオフタイマーを表します。一定時間0を入力すると1を出力します。
--] [-- は入力を表し、回路の接点(スイッチ)のように働きます。マイコンの入力ピンに対応します。
--( )-- は出力を表し、回路のコイルのように働きます。マイコンの出力ピンに対応します。

左右に渡る回路の線の一本一本をラングと呼びます。図にはラングが2本あります。
信号が各ラングを左から右に進み、命令を通ってゆきます。
各命令は左から入力信号を受け取り、右に出力信号を出します。命令は並列や直列に並べることができます。
図の左端からは常に論理値で1の信号が入力されます。1の信号は通電、0の信号は絶縁にたとえられます。多くの命令は1の信号を受け取ると動作し、0の信号の入力を受け取ると停止します。
動作の時、信号を右の端子に出力するほかに、ピンに出力を行ったり、変数を設定するなどの副次的な動作を行う命令もあります。

命令の詳細はこのマニュアルで後述します。
ラダーロジックの詳しい説明は他のWeb上のページなどにゆずります。

見た目に明らかな違いは次のようなものです。

    *\tプログラムはテキストではなく箱を線でつないだような図として表します。
\t多くの人はこちらの方が理解しやすいと感じます。

    *\tプログラムがスイッチ(入力)とコイル(出力)だけの単純なものなら
\tそのまま回路図のように読めます。
\tデジタル回路の知識がある方は直感的に感じるでしょう。

    *\tいつ、どこを計算するかはラダー図のコンパイラが判断します。
\t変数を再計算するタイミングに頭を悩ませる必要はありませんし、
\t計算の順番で悩むこともありません。PLCを処理するツールが
\t考えてくれます。

LDmicroはPIC16やAtmel AVRのプログラムを生成することができます。
現在以下のプロセッサに対応しています。

\t・ PIC16F877
\t・ PIC16F628
\t・ PIC16F876 (未テスト)
\t・ PIC16F88 (未テスト)
\t・ PIC16F819 (未テスト)
\t・ PIC16F887 (未テスト)
\t・ PIC16F886 (未テスト)
\t・ ATmega128
\t・ ATmega64
\t・ ATmega162 (未テスト)
\t・ ATmega32 (未テスト)
\t・ ATmega16 (未テスト)
\t・ ATmega8 (未テスト)

対応プロセッサを増やすのは簡単ですが、私にはテストする環境がありません。
特に対応してほしいものがあればご連絡ください。

LDmicroではラダー図でプログラムを書くことができます。また、ロジックをPC上でシミュレートして試すことができます。それでうまく動くことが確認できたら、入出力ピンを割り当ててマイコン用にコンパイルします。出力のHEXファイルはそのままマイコンに書き込むことができます。

LDmicroは一般的なPLCプログラミング環境になるべく似せてあります。いくつか例外はありますが、商用のものでもどのみちまったく規格が共通なわけではありません。よく知っていると思う場合でも、各命令の説明を熟読してください。この文書は読者にラダー・ロジックやPLCの基本的な知識があることを前提としています (入力-計算-出力のサイクルなど)。

追加のターゲット
==================

コンパイラはANSI Cのコードを出力することもできます。Cコンパイラに対応したプロセッサであればこれを使ってプログラムできますが、ランタイムをユーザが用意する必要があります。具体的には、LDmicroは PlcCycle() という関数のコードを生成します。これを外部から毎サイクル呼ぶ必要があります。また、PlcCycle() の使う入出力の部分も実装する必要があります。詳しくは生成コードのコメントをご覧ください。

また、LDmicroはプロセッサ非依存のバイトコードを生成することができます。
これはラダー・ロジックを実行するための仮想マシン(VM)を対象とするもので、実行用のインタプリタのサンプルを同梱しています。これはポータブルなC言語で書かれています。VMをユーザが実装する限り、これはどのようなマシンでも動くはずです。用途としては、ラダー・ロジックを一種のスクリプト言語として大きなプログラム中に組み込んで使うことが考えられます。詳しくはサンプルのインタプリタのコメントをご覧ください。

コマンドラインオプション
====================

ldmicro.exe は通常オプションなしで起動されます。つまり、通常は単にショートカットなどからダブルクリックで起動して、GUIで操作します。

"ldmicro.exe asd.ld" のようにファイル名を指定して起動した場合は、LDmicroはそのファイルを読み込んで立ち上がります。ファイルが読み込めない場合はエラーを表示します。.ld拡張子をLDmicroに関連付けするとラダー図のファイルをダブルクリックするだけで開けるので便利です。

"ldmicro.exe /c src.ld dest.hex" のように引数を与えて起動すると、src.ld をコンパイルして dest.hex に結果を書き出します。成功したかどうかにかかわらず、LDmicroはコンパイル後そのまま終了します。メッセージはコンソールに出力されます。このモードはコマンドラインから実行することを想定しています。

基本
======

LDmicroをダブルクリックで起動すると、空のプログラムが表示されます。
ファイルを与えて起動するとそのファイルを読み込んで起動します。
LDmicroのプログラムの保存形式(.LD)は独自のもので、他の形式からインポートする機能はありません。

初期状態は、空のラングがひとつあるだけのプログラムです。ここに命令を追加することができます。例えば、メニューの[命令]->[挿入: 接点]を選ぶと Xnew という名前の接点が追加されます。命令の挿入する位置はカーソルで指定します。左右に挿入すると直列に、上下に挿入すると並列になります。
XnewのXというのは、その接点がマイコンの入力ピンに割り当てられるという意味です。入出力接点は実際のピンに割り当てる前にマイコンの種類を選んで名前をつける必要があります。ピンを割り当てるには、画面下部の変数一覧表からダブルクリックします。
Xの他にも色々な頭文字があり、変数の種類を表しています。例えば：

\t・X -- マイコンの入力ピン
\t・Y -- マイコンの出力ピン
\t・R -- 内部リレー。1ビットの内部メモリ
\t・T -- タイマー。オン、オフ、積算の3種
\t・C -- カウンター。上昇、下降、循環の3種
\t・A -- AD変換器から読みだした数値
\t・頭文字なし -- 汎用のメモリ上の数値変数

追加した時には "name" の部分は "new" になっています。何のための変数なのかわかりやすいように名前をつけてください。リレーの命令をダブルクリックするか、カーソルを合わせてリターンを押すと編集できます。
プログラム中で同じ名前の変数は常に同じものを指します。例えば、オンタイマ(TON)で Tdelay という変数を使ったら、他の所にあるオフタイマ(TOF)に Tdelay という変数を使うことはできません。
一方、積算タイマ(RTO)に Tdelay と名前をつけてからRESET命令で Tdelay を指定しても問題ありません。この場合は対象のタイマは同じものだからです。

変数名はアルファベット、数字、下線 (_) を使うことができます。頭文字に数字を使うことはできません。また、大文字と小文字は区別されます。XDogとXdOgは別の変数です。

変数一般の命令 (MOV, ADD, EQU など) はどの種類の変数に対しても働きます。つまり、タイマーやカウンタを計算に使うことができるということです。これは例えば、カウンタの値がある範囲内にあるかどうかを調べるために使えます。

全ての変数は16ビット符号付き整数です。値の範囲は -32768 から 32767 です。値は10進数 (0, 1234, -56) で指定できるほか、ASCIIコード ('A', 'z') を指定することもできます。その場合はシングルクォート (') で文字を括って下さい。

画面下部にはプログラム中の変数の一覧表が表示されます。この表はプログラムから自動的に作られます。ほとんどの変数は設定することはありませんが、頭文字が X (入力ピン), Y (出力ピン), A (AD変換値) のものはマイコンのピン番号を割り当てる必要があります。ダブルクリックするとピンの設定が開きます。

プログラムの編集は、ラダー図に命令を挿入したり削除することで行います。プログラム画面で点滅しているカーソルは、今選択している命令と、命令の挿入される位置を示しています。カーソルが望む場所にない場合は、<Tab>を押して切り替えるか、マウスで命令を選んで下さい。そこで<Delete>を押すと命令を削除できます。ダブルクリックや<Enter>を押すと命令の設定ができます。

命令を挿入するときは、カーソルの棒の出方で挿入される位置が変わります。命令の左右に挿入すると直列に、命令の上下に挿入すると並列になります。この上下左右の指定は矢印キーで切り替えます。命令の挿入位置にはいくつかルールがあります。例えば、コイルの右に命令を置くことはできません。

プログラムを新規作成した時には、空のラングがひとつだけあります。メニューの[編集]→[ラングを挿入]で新しいラングを上下に挿入できます。理論的には、命令を全て並列に配置することで巨大なラングが一つあるだけのプログラムを作ることができます。しかし、論理的な単位ごとにラングを分けて作るほうが後々作業しやすいでしょう。

プログラムが完成したら、シミュレータでテストすることができます。また、マイコンに書き込むためのHEXファイルにコンパイルすることができます。

シミュレータ
==========

シミュレーションモードに入るには、メニューの[シミュレータ]→[シミュレーションモード]を選ぶか、<Ctrl+M>を押します。再度押すとシミュレーションモードを抜けます。

シミュレーションモードではプログラムの表示の仕方が変わります。カーソルが消え、通電している命令は赤くハイライトされます。そうでない命令はグレーになります。<Space>を押すとシミュレーションを1サイクル進めます。

リアルタイムシミュレーションを行うには、メニューの[シミュレータ]→[リアルタイムシミュレーション開始]を選ぶか、<Ctrl+R>を押します。このモードではSpaceキーを押し続けるようにサイクルが自動で進んでゆきます。プログラムの状態が変わると即座に反映されます。

画面下部の変数一覧表から、プログラムの入力状態を確認できます。項目をダブルクリックすると、入力を切り替えることができます。これはラダー図中の命令をダブルクリックすることでも行えます。リアルタイムの場合は入力の変化は即座に反映されます。そうでない場合は<Space>を押してサイクルを進めると反映します。

ネイティブコードへのコンパイル
========================

どのようなプログラムも究極的には.hexファイルにしてマイコンに書き込むためにあります。コンパイルするには、まず対象のマイコンを選びます。これはメニューの[設定]→[ターゲットマイコン]から行います。その後、全ての接点 (頭文字Xの変数)、コイル (頭文字Yの変数)、アナログ変換 (頭文字Aの変数)にマイコンの入出力ポートを割り当てる必要があります。これは画面下部の変数一覧からダブルクリックして行います。変数を選ぶとピンの一覧が表示されるので、その中から選択します。

他の設定としては、サイクル時間(周期)とマイコンの動作周波数があります。これらはメニューの[設定]→[マイコン設定]で調整します。ほとんどの場合サイクル時間を10msから変更する必要はありません。動作周波数の欄にはマイコンとともに使う発振子(水晶やセラミック)の周波数を記入して下さい。

これらの設定が終わればコンパイルできます。メニューで[コンパイル]→[コンパイル]を選びます。[名前をつけてコンパイル]を選ぶとコンパイル結果の出力先を変更できます。コンパイルエラーがなければIntel IHEX形式で機械語を書き出します。できあがったHEXファイルをお使いのライターでチップに書き込んで下さい。

動作させる際は、チップの動作設定(ヒューズとも呼ばれます)に注意して下さい。
PIC16では設定ワードとしてHEXファイルに含めてあるので、ライターが認識して設定します。
AVRではHEXファイルには含まず、ツールを使って設定します。

命令リファレンス
======================

> CONTACT, NORMALLY OPEN        Xname           Rname          Yname
                             ----] [----     ----] [----    ----] [----
  接点 (通常), 常開接点, a接点, メイク接点

    この命令を使って入出力ピンや内部リレーの値を調べることができます。
    通常閉じていて、押すと回路が開くスイッチに例えられます。

    この命令に0を入力すると、0を出力します。
    この命令に1を入力すると、関連付けられた入出力ピンや内部リレーが1のとき、1を出力します。
    この命令に1を入力すると、関連付けられた入出力ピンや内部リレーが0のとき、0を出力します。
    つまり、関連付けられた入出力ピンや内部リレーと同じ信号を出力します。

    この命令は頭文字が R か X か Y の変数に関連付けられます。
    Rの場合は内部リレー、Xの場合はピン入力、Yの場合はピン出力です。
    いずれの場合も値は0か1の論理値です。

> CONTACT, NORMALLY CLOSED      Xname           Rname          Yname
                             ----]/[----     ----]/[----    ----]/[----
  接点 (反転), 常閉接点, b接点, ブレイク接点

    この命令を使って入出力ピンや内部リレーの値を調べることができます。
    通常閉じていて、押すと回路が開くスイッチに例えられます。

    この命令に0を入力すると、1を出力します。
    この命令に1を入力すると、関連付けられた入出力ピンや内部リレーが1のとき、0を出力します。
    この命令に1を入力すると、関連付けられた入出力ピンや内部リレーが0のとき、1を出力します。
    つまり、関連付けられた入出力ピンや内部リレーと逆の信号を出力します。
    これは開接点と逆の動作です。

    この命令は頭文字が R か X か Y の変数に関連付けられます。
    Rの場合は内部リレー、Xの場合はピン入力、Yの場合はピン出力です。
    いずれの場合も値は0か1の論理値です。

> COIL, NORMAL                  Rname           Yname
                             ----( )----     ----( )----
  コイル (通常), 通常コイル

    この命令は出力ピンや内部リレーの値の設定に使います。

    この命令に0を入力すると、関連付けられた出力ピンや内部リレーを0にセットします。
    この命令に1を入力すると、関連付けられた出力ピンや内部リレーを1にリセットします。
    つまり、関連付けられた出力ピンや内部リレーに信号と同じ値を設定します。

    この命令は頭文字が R か Y の変数に関連付けられます。
    Rの場合は内部リレー、Yの場合はマイコンのピン出力です。
    いずれの場合も値は0か1の論理値です。

    この命令はラングの右端にしか配置できません。

> COIL, NEGATED                 Rname           Yname
                             ----(/)----     ----(/)----
  コイル (反転), 反転コイル

    この命令は出力ピンや内部リレーの値の設定に使います。

    この命令に0を入力すると、関連付けられた出力ピンや内部リレーを1にセットします。
    この命令に1を入力すると、関連付けられた出力ピンや内部リレーを0にリセットします。
    つまり、関連付けられた出力ピンや内部リレーに信号と逆の値を設定します。

    この命令は頭文字が R か Y の変数に関連付けられます。
    Rの場合は内部リレー、Yの場合はマイコンのピンです。

    この命令はラングの右端にしか配置できません。

> COIL, SET-ONLY                Rname           Yname
                             ----(S)----     ----(S)----
  コイル (セットのみ), セットコイル

    この命令は出力ピンや内部リレーの値の設定に使います。

    この命令に0を入力すると、なにもしません。
    この命令に1を入力すると、関連付けられた出力ピンや内部リレーを1にセットします。

    このコイルによって状態が0にリセットされることはありません。
    多くの場合、リセットコイルとペアで使われます。

    この命令は頭文字が R か Y の変数に関連付けられます。
    Rの場合は内部リレー、Yの場合はマイコンのピン出力です。

    この命令はラングの右端にしか配置できません。

> COIL, RESET-ONLY              Rname           Yname
                             ----(R)----     ----(R)----
  コイル (セットのみ), リセットコイル

    この命令は出力ピンや内部リレーの値の設定に使います。

    この命令に0を入力すると、なにもしません。
    この命令に1を入力すると、関連付けられた出力ピンや内部リレーを0にリセットします。

    このコイルによって状態が1にセットされることはありません。
    多くの場合、セットコイルとペアで使われます。

    この命令は頭文字が R か Y の変数に関連付けられます。
    Rの場合は内部リレー、Yの場合はマイコンのピン出力です。

    この命令はラングの右端にしか配置できません。

> TURN-ON DELAY                 Tdon
                           -[TON 1.000 s]-
  オンタイマー, オンディレイタイマー, 遅延立上, 限時動作即時復帰メイク接点

    この命令はカウント値が設定された秒数以上になると0を出力します。
    反応する秒数はダイアログで調節できます。

    この命令は状態を持ち、出力を保持します。
    この命令に1を1秒間入力し続けると、1を出力しはじめます。
    この命令に0を入力すると、すぐに0を出力しはじめます。
    初期状態では0を出力します。

    カウント値は1が入力される限り0にリセットされ続けます。
    0.8秒1を入力して、0.2秒0を入力して、0.3秒1を入力しても
    この命令が反応することはありません。

    この命令は頭文字が T の変数に関連付けられます。
    値はタイマーのカウント値です。
    1が入力された秒数をサイクル時間を単位として数えます。

    変数の値はMOV命令などで他のところから変更することができます。

> TURN-OFF DELAY                Tdoff
                           -[TOF 1.000 s]-
  オフタイマー, オフディレイタイマー, 遅延立下, 限時動作即時復帰ブレイク接点

    この命令はカウント値が設定された秒数以上になると0を出力します。
    反応する秒数はダイアログで調節できます。

    この命令は状態を持ち、出力を保持します。
    この命令に0を1秒間入力し続けると、0を出力しはじめます。
    この命令に1を入力すると、すぐに1を出力しはじめます。
    初期状態では0を出力します。

    カウント値は1が入力される限り0にリセットされ続けます。
    0.8秒0を入力して、0.2秒1を入力して、0.3秒0を入力しても
    この命令が反応することはありません。

    この命令は頭文字が T の変数に関連付けられます。
    値はタイマーのカウント値です。
    0が入力された秒数をサイクル時間を単位として数えます。

    変数の値はMOV命令などで他のところから変更することができます。

> RETENTIVE TIMER               Trto
                           -[RTO 1.000 s]-
  積算タイマー, 積算オンタイマー

    この命令は1を入力された秒数を数えます。
    それまでに通算で1秒間以上1が入力されていれば1を出力します。

    この命令は状態を持ち、出力を保持します。
    この命令に1を通算で1秒間入力すると、1を出力しはじめます。
    この命令に0を入力すると、すぐに0を出力しはじめます。
    初期状態では0を出力します。

    入力する1秒間は連続している必要はありません。
    例えば、0.6秒1を入力して、2.0秒0を入力して、0.4秒1を入力すると
    この命令は1を出力しはじめます。

    一度1を出力し始めると、この命令は入力が0になっても1を出力し続けます。
    カウントをリセットするには、RESET命令を使います。

    この命令は頭文字が T の変数に関連付けられます。
    値はタイマーのカウント値です。
    1が入力された積算の秒数をサイクル時間を単位として数えます。
    この命令はカウント値が設定された秒数以上になると1を出力します。

    変数の値はMOV命令やRESET命令などで他のところから変更することができます。

> RESET                        Trto             Citems
                           ----{RES}----     ----{RES}----
  リセット, カウンタリセット, タイマーリセット

    タイマやカウンタをリセットする命令です。

    この命令に0を入力すると、なにもしません。
    この命令に1を入力すると、関連付けられたタイマやカウンタのカウント値を0にリセットします。

    TONとTOFは入力によってリセットするため、この命令は必要ありません。
    RTOやCTU/CTDは単独ではリセットされないので、この命令と組み合わせる必要があります。

    この命令はラングの右端にしか配置できません。

> ONE-SHOT RISING               _    _
                           --[_/ OSR/ \_]--
  立ち上がり微分, 上昇時パルス, DIFU, PLS, ワンショット立ち上がり

    入力の立ち上がりを検知してパルス信号を出力します。
    イベントの通知や処理を行うきっかけなどに有用です。

    直前のサイクルでの入力が0で、現在のサイクルでの入力が1のときに限り、1を出力します。
    それ以外の場合は0を出力します。

> ONE-SHOT FALLING            _      _
                           --[ \_OSF/ \_]--
  立ち下がり微分, 下降時パルス, DIFD, PLF, ワンショット立ち下がり

    入力の立ち下がりを検知してパルス信号を出力します。
    イベントの通知や処理を行うきっかけなどに有用です。

    直前のサイクルでの入力が1で、現在のサイクルでの入力が0のときに限り、1を出力します。
    それ以外の場合は0を出力します。

> SHORT CIRCUIT, OPEN CIRCUIT
                           ----+----+----      ----+     +----
  閉回路/開回路, 短絡/開放, ショート/オープン

    閉回路の出力は常に入力と同じになります。
    開回路の出力は入力に関わらず常に0です。
    デバッグやラングをグループにまとめる際に有用です。

> MASTER CONTROL RELAY
                           -{MASTER RLY}-
  マスターコントロールリレー, マスター制御リレー

    全ラングへの入力信号を表します。
    プログラムの動作中は通常、全てのラングの入力は常に1です。
    この命令に0が入力されると、全てのラングの入力が0になります。
    その状態は再度この命令に1が入力されるまで続きます。

    多くの場合、この命令はペアで使われます。
    全体をオフにするものと、それから復帰するためのものです。

> MOVE                      {destvar :=  }      {Tret :=     }
                           -{ 123     MOV}-    -{ srcvar  MOV}-
  代入, 転送, ムーブ

    変数に値を設定する命令です。

    この命令に0を入力すると、なにもしません。
    この命令に1を入力すると、代入先の変数に代入元の変数の値を設定します。

    代入先にはすべての種類の変数が指定できます。
    これにはタイマー変数 (頭文字T)とカウンター変数 (頭文字C)も含みます。
    例えば0を代入するようにすれば、RESET命令と同様の動作になります。

    この命令はラングの右端にしか配置できません。

> ARITHMETIC OPERATION       {ADD  kay  :=}       {SUB  Ccnt :=}
                            -{ 'a' + 10   }-     -{ Ccnt - 10  }-

>                            {MUL  dest :=}       {DIV  dv :=  }
                            -{ var * -990 }-     -{ dv / -10000}-
  四則演算 (加減乗除)

    これらの命令は加減乗除の計算を行い、結果を変数に設定します。

    これらの命令に0を入力すると、なにもしません。
    これらの命令に1を入力すると、代入先の変数に計算結果を代入します。
    1を入力し続ける限り、計算と代入を毎サイクル繰り返し行います。

    右辺の項には変数か数値を指定します。
    数値は10進数 (0, 1234, -56) で指定できるほか、ASCIIコード ('A', 'z') を指定することもできます。
    その場合はシングルクォート (') で文字を括って下さい。

    計算は16ビット符号付き整数で行われます。
    値の範囲は -32768 から 32767 です。

    繰り返しに注意して下さい。例えば、a = a + 100という命令を
    ラングに置いたとすると、変数aの値は毎サイクル100ずつ上がっていきます。
    計算のステップとして1を単に足したいだけの場合は、OSR/OSFなど
    条件を組み合わせて、計算と代入が1度しか実行されないようにしてください。

    割り算は端数を切り落とします。8 / 3 = 2になります。

    この命令はラングの右端にしか配置できません。

> COMPARE               [var ==]        [var >]        [1 >=]
                       -[ var2 ]-      -[ 1   ]-      -[ Ton]-

>                       [var !=]       [-4 <   ]       [1 <=]
                       -[ var2 ]-     -[ vartwo]-     -[ Cup]-
  比較 (等値, 非等値, 超過, 未満, 以上, 以下)

    これらの命令は数値や変数の比較に使います。

    この命令に0を入力すると、なにもしません。
    この命令に1を入力すると、設定された不等式が真である時1を、偽であるとき0を出力します。

    計算は16ビット符号付き整数で行われます。
    値の範囲は -32768 から 32767 です。

    数値は10進数 (0, 1234, -56) で指定できるほか、ASCIIコード ('A', 'z') を指定することもできます。
    その場合はシングルクォート (') で文字を括って下さい。

> COUNTER                      Cname          Cname
                           --[CTU >=5]--  --[CTD > -5]--
  カウンタ (アップカウンタ, ダウンカウンタ)

    信号の立ち上がりを検知して数値を1ずつ上げ下げする命令です。

    直前のサイクルでの入力が0で、
    現在のサイクルでの入力が1のときに限り、カウントを変化させます。
    それ以外の場合、なにもしません。

    アップカウンタはカウント値をインクリメント(1足す)します。
    ダウンカウンタはカウント値をデクリメント(1引く)します。
    カウント値が5以上なら1、そうでなければ0を出力します。
    比較する数値はダイアログで調整できます。

    これらの命令は頭文字が C の変数に関連付けられます。
    値はカウント値です。
    変数の値はRESET命令やMOV命令などで他のところから変更することができます。
    また、同じ変数に対してCTUとCTDの両方を配置することができます。
    これによって、変数を上げ下げすることができます。

> CIRCULAR COUNTER             Cname
                           --{CTC 0:7}--
  循環カウンタ

    信号の立ち上がりを検知して数値を1ずつ上げてゆく命令です。
    数値が上限値を超えると0に戻ります。

    直前のサイクルでの入力が0で、現在のサイクルでの入力が1のときに限り、カウントを変化させます。
    それ以外の場合、なにもしません。

    通常はカウント値をインクリメントします。
    ただしカウンタ値が7の場合は、インクリメントする代わりに0にリセットします。
    それ以外の場合、なにもしません。

    たとえば最大値が7の場合、パルスを送るたびカウンタの値は
      0, 1, 2, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 2,....
    のようになります。

    最大値はダイアログで調整できます。
    順繰りに数値が繰り返すので、シーケンサのように使うことができます。

    この命令は頭文字が C の変数に関連付けられます。
    値はカウント値です。
    変数の値はRESET命令やMOV命令などで他のところから変更することができます。

    この命令はラングの右端にしか配置できません。

> SHIFT REGISTER            {SHIFT REG   }
                           -{ reg0..3    }-
  シフトレジスタ, SFT, SFL

    この命令は通し番号をつけた変数を一度に用意するものです。
    図の例では、reg0, reg1, reg2, reg3の4つの変数があります。
    変数をステージと呼ぶことがあります。

    直前のサイクルでの入力が0で、現在のサイクルでの入力が1のときに限り、各変数の値をシフトします。
    それ以外の場合、なにもしません。

    変数のシフトとは、以下のように変数の値を1つずつずらす動作です。
      reg3 := reg2
      reg2 := reg1
      reg1 := reg0
    reg0の値はそのまま残ります。

    巨大なシフトレジスタはメモリを圧迫することがあるので注意して下さい。

    この命令は通し番号のついた複数の汎用変数に関連付けられます。
    この命令はラングの右端にしか配置できません。

> LOOK-UP TABLE             {dest :=     }
                           -{ LUT[i]     }-
  ルックアップテーブル

    この命令は数値列を記憶しておき、通し番号で参照するものです。
    記憶するデータはダイアログで設定できます。

    この命令に0を入力すると、なにもしません。
    この命令に1を入力すると、i番目のデータを参照して変数destに代入します。
    変数iのことを添字と呼びます。

    添字の範囲は0から要素数-1です。
    添字が範囲外の場合の動作は未定義です。

    この命令は汎用変数に関連付けられます。
    この命令はラングの右端にしか配置できません。

> PIECEWISE LINEAR TABLE    {yvar :=     }
                           -{ PWL[xvar]  }-
  線形連想配列

    この命令は数字と数字の対応 (XY値)を記憶しておき、参照するものです。
    記憶するデータはダイアログで設定できます。

    この命令に0を入力すると、なにもしません。
    この命令に1を入力すると、xvarに対応するデータを計算して変数yvarに代入します。
    変数はダイアログで設定できます。

    この命令は計算が複雑な関数を予め計算しておくのに有用です。
    例えば、センサーの出力電圧をより扱いやすい単位にキャリブレーションするために使えます。

    数値に対応するデータがない場合、出力は前後の数値から線形補間されます。
    例えば、関数 f が次のような値をとるとします。

        f(0)   = 2
        f(5)   = 10
        f(10)  = 50
        f(100) = 100

    これは、グラフで点を打つと次のような4つの点があるということです。

        (x0, y0)   = (  0,   2)
        (x1, y1)   = (  5,  10)
        (x2, y2)   = ( 10,  50)
        (x3, y3)   = (100, 100)

    これをPLTに記憶しておいて、例えば55を参照するとします。
    X=55の点は(10, 50)と(100, 100)の中点(55, 75)なので、75という数値が得られます。

    X側の数値は単調増加になるよう入力する必要があります。

    補完の計算は符号付き16ビット整数で行うため、補完の計算で誤差が生じることがあります。
    例えば、次のようなエントリはコンパイル時にエラーになります。

        (x0, y0)    = (  0,   0)
        (x1, y1)    = (300, 300)

    この場合、点と点の距離を縮めるようにすれば解消できます。

        (x0, y0)    = (  0,   0)
        (x1, y1)    = (150, 150)
        (x2, y2)    = (300, 300)

    5、6の点があれば通常の用途には充分です。それ以上のエントリ数は計算の負荷になる恐れがあります。

    xvarの値がエントリの範囲外(最小のX値より下か、最大のX値より上)の場合の動作は未定義です。

    この命令は汎用変数に関連付けられます。
    この命令はラングの右端にしか配置できません。

> A/D CONVERTER READ           Aname
                           --{READ ADC}--
  アナログ入力読み込み, AD変換読み込み

    LDmicroはマイコンのAD変換機能を利用することができます。

    この命令に0を入力すると、なにもしません。
    この命令に1を入力すると、ピンの入力電圧にAD変換を行い対応する変数 (頭文字A) に代入します。
    この変数は他の計算(比較、四則演算)に使うことができます。

    入力ピンの設定は画面下部の変数一覧からダブルクリックすることで行います。
    多くのマイコンでは、アナログ入力に対応するピンには制限があります。
    マイコンにAVRをお使いの場合は、AREFピンをVddと接続して下さい。

    この命令は頭文字 A の変数に関連付けられます。
    値はアナログ入力ピンの電圧を表す値です。
    0Vは0という値に、参照電極と同じ電圧の入力は1023という値に対応します。
    計算命令を使うことで値の範囲は望むように加工できますが、
    演算が全て16ビット整数であることに留意してください。

    この命令はラングの右端にしか配置できません。

> SET PWM DUTY CYCLE          duty_cycle
                           -{PWM 32.8 kHz}-
  PWMデューティ比設定

    LDmicroはマイコンのPWM機能を利用することができます。
    この命令はPWMのデューティ比を変数に合わせて設定します。

    この命令に0を入力すると、なにもしません。
    この命令に1を入力すると、PWMのデューティ比を変数duty_cycleの値に設定します。

    デューティ比の値の範囲は0から100です。
    0を設定すると常にロー、100を設定すると常にハイになります。
    (クロック数などの変換の計算はLDmicroが行います)

    ダイアログでPWM周波数をHzで設定できます。
    動作周波数や分周との兼ね合いで実際に望みの周波数になるとは限りませんが、
    LDmicroはもっとも近い値になるよう設定を調整します。
    誤差があまりに大きい場合はコンパイル時に警告が出ます。
    速度を余りに早くすると解像度が落ちる恐れがあります。

    マイコンのPWM機能はタイマー機能に関連付けられています。
    LDmicroはサイクルの管理のためタイマーをもう一つ専有します。
    そのため、マイコンには最低2つのタイマーが必要になります。

    出力ピンはPIC16ではCCP1ではなくCCP2側、AVRではOC1AではなくOC2A側を使います。

    この命令は汎用変数に関連付けられます。
    この命令はラングの右端にしか配置できません。

> MAKE PERSISTENT            saved_var
                           --{PERSIST}--
  永続変数, 永続化, EEPROM保存

    この命令は変数をEEPROMに保存します。

    この命令に0を入力すると、なにもしません。
    この命令に1を入力すると、指定した変数の値をEEPROMに保存します。
    これによって、マイコンの電源が切れた後でも値が保存されます。

    値の保存や復元は自動的に行われます。
    変数の値が変化した時に書き込み、電源オンの割り込み時に読み出されます。

    多くのEEPROMは書き込み保証回数が少ない(100000回程度)ので、
    頻繁に変化する変数を指定するのは避けたほうがよいでしょう。

    この命令は汎用変数に関連付けられます。

    この命令はラングの右端にしか配置できません。

> UART (SERIAL) RECEIVE          var
                           --{UART RECV}--
  UART受信

    LDmicroはマイコンのUART機能を利用することができます。
    この命令はUARTで1文字を受信して変数に設定します。

    この命令に0を入力すると、なにもしません。
    この命令に1を入力すると、UARTで1文字を受信しようとします。
    文字を受信したサイクルだけ1を出力します。
    受信した文字は関連付けられた変数に保存されます。
    それ以外の場合は0を出力します。

    AVRの複数のUARTが使える機種では、UART0ではなくUART1側のみに対応しています。
    通信のボーレートはメニューの[設定]→[マイコン設定]から設定できます。
    動作周波数によってはボーレートが実現できない場合もあります。
    その場合はLDmicroは警告を表示します。

    複数のUART命令が一度に起動された場合の動作は未定義です。

    この命令は汎用変数に関連付けられます。
    値は受信した文字です。

> UART (SERIAL) SEND             var
                           --{UART SEND}--
  UART送信

    LDmicroはマイコンのUART機能を利用することができます。
    この命令は変数の値からUARTで1文字を送信します。

    この命令に0を入力すると、なにもしません。
    この命令に1を入力すると、UARTで1文字を送信しようとします。
    文字を送信中のとき1を出力します。
    それ以外の場合は0を出力します。

    AVRの複数のUARTが使える機種では、UART0ではなくUART1側のみに対応しています。
    通信のボーレートはメニューの[設定]→[マイコン設定]から設定できます。
    動作周波数によってはボーレートが実現できない場合もあります。
    その場合はLDmicroは警告を表示します。

    複数のUART命令が一度に起動された場合の動作は未定義です。

    この命令は汎用変数に関連付けられます。
    値は送信するための文字です。
    他のMOV命令やLUT命令などから設定することができます。

    転送には複数サイクルにわたって時間がかかることに注意して下さい。
    複数の文字を送るには、出力の立ち下がりを監視するか、
    タイマーを使って間隔をあけます。
    1を入力していい(送信)のはこの命令から0が出力されている(通信中でない)ときだけです。

    多くの場合は次のFORMAT命令を使うほうが容易です。

> FORMATTED STRING OVER UART                var
                                   -{"Pressure: \\3\\r\\n"}-
  UART書式送信

    LDmicroはマイコンのUART機能を利用することができます。
    この命令は変数の値からUARTで文字列を送信します。

    この命令に0を入力すると、なにもしません。
    この命令に1を入力すると、直前サイクルの入力が0の時に限り、
    UARTで文字列の送信を開始します。
    文字を送信中のとき1を出力します。
    それ以外の場合は0を出力します。

    AVRの複数のUARTが使える機種では、UART0ではなくUART1側のみに対応しています。
    通信のボーレートはメニューの[設定]→[マイコン設定]から設定できます。
    動作周波数によってはボーレートが実現できない場合もあります。
    その場合はLDmicroは警告を表示します。

    複数のUART命令が一度に起動された場合の動作は未定義です。

    設定した書式文字列が '\\3' というエスケープシーケンスを含む場合、
    その部分に変数varの値が文字列として置き換えられます。
    この場合必ず3文字分に置き換えられます。
    例えばvarの値が35の場合、"Pressure: \\3\\r\\n" という書式は
    "Pressure:  35\\r\\n" のようになります。
    varに1432といった4桁の値がある場合、動作は未定義です。
    その可能性がある場合は '\\3' の代わりに '\\4' を使うほうがよいでしょう。

    また、値が負の可能性がある場合、`\\-3d' (あるいは `\\-4d') のように
    指定して下さい。符号をつけるための空白を確保します。

    変数を埋め込まず、単に固定文字列を送信することもできます。
    その場合はエスケープシーケンスを使う必要はありません。

    '\\数字' の他に、次のようなコントロールキャラクタを使うことができます。

\t・ \\\\  -- バックスラッシュ
\t・ \\r   -- キャリッジリターン
\t・ \\n   -- ラインフィード
\t・ \\f   -- フォームフィード
\t・ \\b   -- バックスペース
\t・ \\t   -- horizontal tab
\t・ \\v   -- vertical tab
\t・ \\a   -- alert
\t・ \\xAB -- 16進数の 0xAB

    メモリの点でこの命令はあまり効率的ではありません。最適化にはLDmicro全体にわたる改造が必要そうです。

算術命令の詳細
====================

LDmicroの計算は16ビット符号付き整数で行われます。値の範囲は -32768 から 32767 です。
これは、正しい計算結果を得るためには、計算途中の値もこの範囲に収める必要があることを意味します。
例えば、y = (1/x)*1200 という式を計算するとします。xの範囲を[1, 20]とすると、yは[60, 1200]という範囲になります。
この計算には二通りの実装がありえます。一つはxの逆数を取ってから1200をかける方法です。

   ||         {DIV  temp  :=}          ||
   ||---------{ 1 / x       }----------||
   ||                                  ||
   ||          {MUL  y  :=  }          ||
   ||----------{ temp * 1200}----------||
   ||                                  ||

もしくは、xを直接1200で割ります。

   ||           {DIV  y  :=}           ||
   ||-----------{ 1200 / x }-----------||

数学的にはこの二つは同じ意味です。しかしこの二つを試すと、前者の結果はy=0になってしまいます。なぜなら、計算途中のtempでアンダーフローが起こるからです。例えばx = 3の逆数は0.333で、整数ではなくなります。除算は切り捨てなので、結果は0になります。それに1200を掛けても0のままです。後者の場合はアンダーフローは起こりません。

他にオーバーフローの問題があります。符号付き整数の仕組み上、32767 + 1 = -32768のように循環します。

これらの問題を防ぐには、例えば変数の範囲を-100から100の間にとどめるなどのルールを決めておくとよいでしょう。

数の単位を変換する場合、掛け算の次に割り算を行うとよいでしょう。
y = 1.8*x のような変換は、y = (9/5)*xのように書き換えて、y = (9*x)/5として実装します。

   ||         {MUL  temp  :=}          ||
   ||---------{ x * 9       }----------||
   ||                                  ||
   ||           {DIV  y  :=}           ||
   ||-----------{ temp / 5 }-----------||

この実装は |x| < (32767 / 9) = 3640 の範囲で働きますが、範囲外の場合はtempがオーバーフローします。

コーディングスタイル
============

私は以下のように、一つのラングに複数コイルを使うことがあります。

   ||       Xa               Ya        ||
 1 ||-------] [--------------( )-------||
   ||                                  ||
   ||       Xb               Yb        ||
   ||-------] [------+-------( )-------||
   ||                |                 ||
   ||                |       Yc        ||
   ||                +-------( )-------||
   ||                                  ||

並列のコイルを使わずに書くと、次のようになります。

   ||       Xa               Ya        ||
 1 ||-------] [--------------( )-------||
   ||                                  ||
   ||                                  ||
   ||                                  ||
   ||                                  ||
   ||       Xb               Yb        ||
 2 ||-------] [--------------( )-------||
   ||                                  ||
   ||                                  ||
   ||                                  ||
   ||                                  ||
   ||       Xb               Yc        ||
 3 ||-------] [--------------( )-------||
   ||                                  ||

理論的には、複数のラングを使う必要は全くありません。命令を全て並列に配置することで、巨大なラングが一つあるだけのプログラムを作ることができます。しかし、プログラムが複雑になるにつれてロジックの変更が難しくなってゆくでしょう。新たな機能を追加するために大幅に書き換えが必要になるかもしれません。

適度であれば、ラングをグループ分けするのはよいことです。閉回路やコイルを左端に入れて命令を並列に配置することでラングを束ねることができます。なるべく関係の深いものをまとめ、論理的な単位ごとにラングを分けて設計しましょう。動作としてはラングを束ねず設計する場合と変わりありませんが、ラダー図を見渡すとき、論理的なまとまりを視覚的に把握しやすくなります。

                  *                 *                  *

ラングの優先順序に依存するロジックは、多くの場合混乱を引き起こします。例えば、次のような設計はXaとXbが共に閉じた場合にはYcは1になりますが、直感的ではありません。

   ||       Xa         {v  :=       }  ||
 1 ||-------] [--------{ 12      MOV}--||
   ||                                  ||
   ||       Xb         {v  :=       }  ||
   ||-------] [--------{ 23      MOV}--||
   ||                                  ||
   ||                                  ||
   ||                                  ||
   ||                                  ||
   ||      [v >]             Yc        ||
 2 ||------[ 15]-------------( )-------||
   ||                                  ||

しかし、ラングの優先順序をうまく使うとロジックを短く記述できる場合があります。
Xb3〜Xb0という4つのビットを2進数に変換する処理を私が書くとしたら、このようにします。

   ||                                   {v  :=       }  ||
 3 ||-----------------------------------{ 0       MOV}--||
   ||                                                   ||
   ||       Xb0                  {ADD  v  :=}           ||
   ||-------] [------------------{ v + 1    }-----------||
   ||                                                   ||
   ||       Xb1                  {ADD  v  :=}           ||
   ||-------] [------------------{ v + 2    }-----------||
   ||                                                   ||
   ||       Xb2                  {ADD  v  :=}           ||
   ||-------] [------------------{ v + 4    }-----------||
   ||                                                   ||
   ||       Xb3                  {ADD  v  :=}           ||
   ||-------] [------------------{ v + 8    }-----------||
   ||                                                   ||

この実装でMOV命令を最初以外に配置すると、結果は大きく変わります。
順序をきちんと制御した場合に必要な命令の数を考えると、私はこの方法も悪くないと思います。

バグ
====

LDmicroはコードをそれほど最適化しません。実行は遅く、容量を食います。中型のPICやAVRは充分な性能があるので、小型のPLC程度の用を足すにはこれでよいと私は考えています。

変数名の長さにはきつい制限があります。これはラダー図中の表示に収めるためですが。よい解決方法は今のところ見つかりません。

パフォーマンスやプログラムメモリ、データメモリなどの点で問題があっても、コンパイル時に全て検出することはできません。どこかで実際動かした時にわかります。

ファイルの入出力の実装が適当なので、クラッシュしたりファイルが壊れたりすることがあります。

バグや機能の要望があれば作者にご連絡ください。

Thanks to:
    * Marcelo Solano, for reporting a UI bug under Win98
    * Serge V. Polubarjev, for not only noticing that RA3:0 on the
      PIC16F628 didn't work but also telling me how to fix it
    * Maxim Ibragimov, for reporting and diagnosing major problems
      with the till-then-untested ATmega16 and ATmega162 targets
    * Bill Kishonti, for reporting that the simulator crashed when the
      ladder logic program divided by zero
    * Mohamed Tayae, for reporting that persistent variables were broken
      on the PIC16F628
    * David Rothwell, for reporting several user interface bugs and a
      problem with the "Export as Text" function

COPYING, AND DISCLAIMER
=======================

DO NOT USE CODE GENERATED BY LDMICRO IN APPLICATIONS WHERE SOFTWARE
FAILURE COULD RESULT IN DANGER TO HUMAN LIFE OR DAMAGE TO PROPERTY. THE
AUTHOR ASSUMES NO LIABILITY FOR ANY DAMAGES RESULTING FROM THE OPERATION
OF LDMICRO OR CODE GENERATED BY LDMICRO.

This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.

Jonathan Westhues

Rijswijk      -- Dec 2004
Waterloo ON   -- Jun, Jul 2005
Cambridge MA  -- Sep, Dec 2005
                 Feb, Mar 2006
                 Feb 2007
Seattle WA    -- Feb 2009

Email: user jwesthues, at host cq.cx
