|
1: 2016-08-19 (金) 19:49:28 takaboo |
| + | TITLE:Dynamixel2 Library |
| + | **概要 [#va21cbfb] |
| + | Dynamixel2 LibraryはDynamixel2の通信プロトコルをサポートした製品をWindows等のOSから操作するためのライブラリ集です。~ |
| + | 従来はユーザ自らシリアル通信に関するAPIを使用してタイミングやエラー処理を加えて利用するものでしたが、本APIを介する事でシリアル通信である事をほとんど意識すること無くアプリケーションの作りこみに専念することができるはずです。 |
| | | |
| + | なお、PCと[[BTE061D]]・[[BTE061E]]・[[BTE068]]・[[BTE068B]]・[[BTE082]]・[[BTE083]]のいずれかがUSBケーブルで接続され、PC上にWindowsのデバイスとして仮想COMポートが増設された状態で使用するものとします。 |
| + | |
| + | |CENTER:BGCOLOR(red): :idea:|Dynamixel2プロトコルと[[旧来のDynamixelプロトコル>DXLIB]]を装備した装置は同一ノードでの運用はできない。| |
| + | |
| + | **ライブラリおよびサンプルプログラムのダウンロード [#j8bd290f] |
| + | 以下のリンクよりライブラリ及びサンプルプログラムをアーカイブしたファイルがダウンロードできます。 |
| + | -''2014/05/xx Ver.1.0''~ |
| + | #ref(http://www.besttechnology.co.jp/download/DXLIB2_V1.0.zip) |
| + | 内容 |
| + | --初期リリース~ |
| + | |
| + | アーカイブファイルには以下のファイルが同梱されます。必要に応じて解凍してください。 |
| + | |DXLIB2|dxlib2_x32.dll|<|ライブラリ本体 | |
| + | |~|dxlib2_x64.dll|<|~| |
| + | |~|libdxlib2_x32.a|<|GCC用ライブラリ(定義のみ) | |
| + | |~|libdxlib2_x64.a|<|~| |
| + | |~|dxlib2_x32.llb|<|MSVC用ライブラリ(定義のみ) | |
| + | |~|dxlib2_x64.lib|<|~| |
| + | |~|dxlib2.c|<|ライブラリソース | |
| + | |~|dxlib2.h|<|ライブラリヘッダ | |
| + | |~|dxlib2_matlab.h|<|matlab用ヘッダ | |
| + | |~|makelib.bat|<|ライブラリ再構築用バッチ | |
| + | |~|83.bat|<|~| |
| + | |SampleCode|GCCDeveloperLite|smpl1(template).c |サンプル | |
| + | |~|~|smpl2(ping).c |~| |
| + | |~|~|smpl3(byte_rw).c |~| |
| + | |~|~|smpl4(word_rw).c |~| |
| + | |~|~|smpl5(multithread).c |~| |
| + | |~|~|smpl6(sync).c |~| |
| + | |~|~|smpl7(rawpacket).c |~| |
| + | |~|~|smpl8(dualport).c |~| |
| + | |~|~|smpl9(DynamicLoad).c |~| |
| + | |~|~|smpl10(USS3).c |~| |
| + | |~|~|dxlib2.h |DXLIB2フォルダに収録されるものと同一 | |
| + | |~|~|dxmemmap.h |~| |
| + | |~|~|dxlib_x32.dll |~| |
| + | |~|~|dxlib_x64.dll |~| |
| + | |~|DELPHI|Project1.dpr |サンプル | |
| + | |~|~|Unit1.dfm |~| |
| + | |~|~|Unit1.pas |~| |
| + | |~|~|DXLIB2.pas |~| |
| + | |~|~|dxlib2_x32.dll |DXLIB2フォルダに収録されるものと同一 | |
| + | |~|~|dxlib2_x64.dll |~| |
| + | |~|LabVIEW2011|sample.vi |サンプル | |
| + | |~|~|sample_mx.vi |~| |
| + | |~|~|synctest.vi |~| |
| + | |~|~|dbyte2byte.vi |サブvi | |
| + | |~|~|dbyte2word.vi |~| |
| + | |~|~|errcnt.vi |~| |
| + | |~|~|DXLIB2.llb |DXLIB2ラッパーライブラリ | |
| + | |~|~|dxlib2_x32.dll |DXLIB2フォルダに収録されるものと同一 | |
| + | |~|EXCEL|text.xls |サンプルシート | |
| + | |~|~|dxlib2_x32.dll |DXLIB2フォルダに収録されるものと同一 | |
| + | |~|~|dxlib2_x64.dll |~| |
| + | |~|Linux|smpl2.c |サンプル | |
| + | |~|~|smpl4.c |~| |
| + | |~|Ruby|SMPL1.c |サンプル | |
| + | |~|~|SMPL3.c |~| |
| + | |~|Python|SMPL1.py |サンプル | |
| + | |~|~|SMPL3.py |~| |
| + | |
| + | **開発環境毎の設定 [#u3ff03d9] |
| + | ここでは3つのツールで使用する場合の設定方法を説明します。ここで紹介しないツールでも必要とされる操作は概ね同様ですが、全てに対応できるとまでは明言しません。 |
| + | |
| + | ***GCC Developer Lite [#e1d4017c] |
| + | GCC Developer Liteの詳細については[[こちら>GCC Developer Lite]]。~ |
| + | 'SampleCode\GCCDeveloperLite'フォルダにはAPIの基本的な使い方を紹介したサンプルが同梱されます。ポート・ボーレート・ID等は使用する環境に合わせて適宜修正して使用します。 |
| + | |
| + | ****DLLの静的リンク [#la562acf] |
| + | 静的にDLLを使用する場合は以下の手順でDLLをリンクする指定を行った上でコンパイルします。なお32bit環境を前提とします。 |
| + | +ファイルの準備~ |
| + | ~ダウンロードファイルを解凍後、コンパイルするソースファイルと同一フォルダに以下のファイルをコピー~ |
| + | |=ファイル |=ファイル名 |=備考 | |
| + | |<ヘッダ |<dxlib2.h |<必要な宣言を集約 | |
| + | |<DLL |<dxlib2_x32.dll |<DLL本体(実行時およびリンク時に必要) | |
| + | +コンパイルオプションの選択 |
| + | ~ツールメニュー→コンパイラオプションをクリックし、表示されるダイアログボックスの設定リストから'x86 (Console)'を選択~ |
| + | #ref(GCC_CompileOption_sel.png)~ |
| + | +ライブラリの追加 |
| + | ~etc...タブ内の追加ボタンを押し、新規に行を追加~ |
| + | #ref(GCC_CompileOption_AddEtc.png)~ |
| + | ~新規に追加された空の行をクリックし'dxlib2_x32.dll'と入力~ |
| + | #ref(GCC_CompileOption_AddDxlib2.png)~ |
| + | |
| + | ~OKを押して設定を反映 |
| + | |
| + | ****DLLの動的リンク [#y15bf22f] |
| + | 動的にDLLを使用する場合はDLL自体をコンパイラオプションへ追記する必要はありません。その代わりにソース中でdxlib2.hをインクルードする前に_DYNAMICLOADマクロを定義しておきます。 |
| + | #define _DYNAMICLOAD |
| + | #include "dxlib2.h" |
| + | これによりヘッダファイル内の諸定義が切り替わり、DLLのロード及びアンロードを行うLoadDLLとUnloadDLLが利用できるようになります。 |
| + | TDeviceID dev; |
| + | // DLLをロード |
| + | if (LoadDLL ()) { |
| + | if ((dev = DX2_OpenPort (COMPORT, BAUDRATE))) { |
| + | ... |
| + | DX2_ClosePort (dev); |
| + | } |
| + | // DLLをアンロード |
| + | UnloadDLL (); |
| + | } |
| + | |
| + | ***Microsoft Visual C++ [#j5a74d33] |
| + | [[Visual C++ 2010 Express:http://www.microsoft.com/japan/msdn/vstudio/express/]]でDLLの静的なリンク方法のみ紹介します。 |
| + | +ファイルの準備~ |
| + | ~ダウンロードファイルを解凍後、コンパイルするソースファイルと同一フォルダに以下のファイルをコピー~ |
| + | |=ファイル |=ファイル名 |=備考 | |
| + | |<ヘッダ |<dxlib2.h |<必要な宣言を集約 | |
| + | |<DLL |<dxlib2_x32.dll |<実行時に必要 | |
| + | |<ライブラリ |<dxlib2_x32.lib |<リンク時に必要 | |
| + | +プロジェクトのプロパティを変更~ |
| + | ~構成プロパティ→C/C++→全般→追加のインクルードディレクトリの項目にヘッダファイルの格納フォルダを指定~ |
| + | #ref(VCC_property_include.png)~ |
| + | ~構成プロパティ→リンカー→全般→追加のライブラリディレクトリの項目にLIBファイルの格納フォルダを指定~ |
| + | #ref(VCC_property_lib_path.png)~ |
| + | ~構成プロパティ→リンカー→入力→追加の依存ファイルの項目にLIBファイル名を指定~ |
| + | #ref(VCC_property_lib_file.png)~ |
| + | ~構成プロパティ→C/C++→プリコンパイル済ヘッダーの項目にプリコンパイル済ヘッダーを使用しないを指定~ |
| + | #ref(VCC_property_precompile.png) |
| + | |
| + | ***National Instruments LabVIEW [#mdd5d507] |
| + | 'SampleCode\LabVIEW2011'フォルダにサンプルが同梱されます。ポート・ボーレート・ID等は使用する環境に合わせて適宜修正して使用します。~ |
| + | [[LabVIEW:http://www.ni.com/labview/ja/]]には外部のDLLへアクセスする手段が提供されています。しかしながらDXLIB2そのままの定義ではLabVIEWからは使いづらいため、サンプルプログラムではサブviでラッピングしてDXLIB2.llbにまとめて提供しています。 |
| + | #ref(vi_frontpanel.png) |
| + | #ref(vi_diagram.png) |
| + | |
| + | ***DELPHI [#bdd8ef87] |
| + | 'SampleCode\DELPHI'フォルダにサンプルが同梱されます。ポート・ボーレート・ID等は使用する環境に合わせて適宜修正して使用します。~ |
| + | [[DELPHI:http://www.embarcadero.com/jp/products/delphi]]はPASCAL言語によるPC向け開発ツールであり、外部のDLLへ容易にアクセスする事が出来ます。サンプルに含まれるDXLIB2.pas内にdxlib2_x32.dllないしdxlib2_x64.dllを動的にロードする関数を用意しましたので、ユーザソースのuses節にdxlib2を追記すればDynamixel Libraryの各APIへ簡便にアクセスできます。 |
| + | |
| + | ***VB [#p8d7963b] |
| + | 'SampleCode\EXCEL'フォルダにサンプルが同梱されます。~ |
| + | ここではVBと似たMicrosoft OfficeのVBAを使用し、マクロの標準モジュールにDXLIB2HEADという名称でDLLに含まれるいくつかのAPIが定義してあります。~ |
| + | Module1にPingTestとMotorTestというマクロが記述されていますので、ワークシートから適宜マクロを呼び出して実行してください。結果がシート上に反映されます。 |
| + | |
| + | ***Ruby [#r63b1e3e] |
| + | 'SampleCode\Ruby'フォルダにサンプルが同梱されます。ポート・ボーレート・ID等は使用する環境に合わせて適宜修正して使用します。~ |
| + | [[Ruby:http://www.ruby-lang.org/ja]]はオープンソースの動的なプログラミング言語で、外部のDLLへ簡易にアクセスすることが出来ます。~ |
| + | require 'dl/import' |
| + | molude dxlib2 |
| + | extend DL::Importer |
| + | dlload "./dxlib_x32.dll" |
| + | extern "int DX2_OpenPort( char *, long )" |
| + | end |
| + | devid = dxlib2.DX2_OpenPort( "ポート名", ボーレート ) |
| + | |
| + | ***Python [#mc6a5a49] |
| + | 'SampleCode\Ruby'フォルダにサンプルが同梱されます。ポート・ボーレート・ID等は使用する環境に合わせて適宜修正して使用します。~ |
| + | [[Python:http://www.python.jp/Zope]]はオープンソースの動的なプログラミング言語で、外部のDLLへ簡易にアクセスすることが出来ます。~ |
| + | LinuxでLoadLibraryを呼び出す際はcdllインスタンスを使用します。 |
| + | dxlib = windll.LoadLibrary( "dxlib2_x32.dll" ) # for window |
| + | dxlib = cdll.LoadLibrary( "./dxlib2.so" ) # for linux |
| + | |
| + | ***Java [#ceff395d] |
| + | |
| + | ***MathWorks MATLAB [#mad2ebed] |
| + | [[MATLAB:http://www.mathworks.co.jp/products/matlab]]からの使用例を紹介します。ポート・ボーレート・ID等は使用する環境に合わせて適宜修正して下さい。また、mex-setupにて[[Cコンパイラを選択>http://www.mathworks.co.jp/jp/help/matlab/matlab_external/building-mex-files.html#f23734]]しておく必要があります。~ |
| + | +事前準備 |
| + | ~まずはMATLAB起動後、「ファイル(F)」→「パス設定(H)」でdxlib_x32.dll(MATALBが64bitの場合はdxlib_x64.dll)とdxlib_matlab.hの格納されたパスを指定します。dxlib2.hはMATLABでは解釈できない記述が多いため使用しないでください。~ |
| + | +DLLのロード |
| + | loadlibrary('dxlib_x32.dll','dxlib_matlab.h','alias','dxlib2') |
| + | +ポートのオープン |
| + | ~ロードされたdxlib2のDX2_OpenPortを呼出します。関数名の後はポートとボーレートです。 |
| + | devid = calllib('dxlib2','DX2_OpenPort','\\.\COM3',1000000) |
| + | +TErrorCodeの取得 |
| + | ~TErrorCodeはポインタで引き渡しているため、事前に型宣言をしておきます。値はErr.Valueで取得可能です。必要なければ0を指定しても構いません。 |
| + | Err = libpointer('uint16Ptr', 0); |
| + | Ret = calllib('dxlib2', 'DX2_Ping', devid, 1, Err); |
| + | fprintf('%x', Err.Value); |
| + | +ポートのクローズ |
| + | ~DX2_OpenPortを行ったので、DX2_ClosePortを使用してポートを閉じます。 |
| + | calllib('dxlib2','DX2_ClosePort',devid) |
| + | +DLLのアンロード |
| + | unloadlibrary('dxlib2') |
| + | |
| + | ***Linux [#w5c096b8] |
| + | Linux上でのdxlib2のコンパイル方法を紹介します。~ |
| + | +コンパイル準備~ |
| + | ダウンロードファイルを解凍~ |
| + | +オブジェクトファイルの作成~ |
| + | gcc dxlib2.c -o dxlib2.o |
| + | +共有ライブラリの作成~ |
| + | RubyやPython等で使用する場合のみ。~ |
| + | gcc -fPIC -shared dxlib2.c -o dxlib2.so |
| + | +コンパイル~ |
| + | dxlib2のオブジェクトファイルとC言語ソースを合わせてコンパイルする。~ |
| + | ポート・ボーレート等は使用する環境に合わせて適宜追加・修正する。~ |
| + | ライブラリは必要に合わせて追加する。~ |
| + | gcc sample.c dxlib2.o -o sample |
| + | |
| + | なお、コンパイルや実行にあたってI/Fやカーネル・ディストリビューションに依存するのがLinuxですので、そのまま使用できない場合は適宜ソースを修正下さい。 |
| + | |
| + | **API [#ybae1454] |
| + | Dynamixel Libraryではシリアル通信を直接意識するコードを記述せずに、対象IDのデバイスのコントロールテーブルへの読み書き行うAPIを用意しています。~ |
| + | C言語のソースにdxlib2.hをインクルードすれば、APIを使用するのに必要なプロトタイプとマクロの定義がなされます。 |
| + | |
| + | ***DX2_OpenPort [#zbd2f6ef] |
| + | ライブラリの内部情報を初期化すると同時に指定されたCOMポートをオープンし、[[DX2_SetBaudrate>#ba9f52b8]]を使用して通信速度を設定した後、ユニークな[[TDeviceID>#TDeviceID]]を返す。以後はこの[[TDeviceID>#TDeviceID]]を使用して各APIを使用する。~ |
| + | 複数のCOMポートを使用する場合は、使用するポート毎にDX2_OpenPortを行い[[TDeviceID>#TDeviceID]]を取得しなくてはならない。~ |
| + | なお、Linuxにおけるボーレートの指定に関しては、[[DX2_SetBaudrate>#ba9f52b8]]の解説に注意の事。 |
| + | TDeviceID DX2_OpenPort (char *name, uint32_t baud); |
| + | -パラメータ |
| + | --char '''*name''' |
| + | ~インターフェースが提供するCOMポート名。~ |
| + | 記述方法は[[こちら:http://support.microsoft.com/default.aspx?scid=kb;ja;JP115831]]の情報に従う。 |
| + | --long '''baud''' |
| + | ~インターフェースとデバイス間の通信速度[bps]。 |
| + | -戻り値 |
| + | --[[TDeviceID>#TDeviceID]]~ |
| + | ~オープンに成功した場合は0以外の値、失敗した場合は0を返す。~ |
| + | -使用例 |
| + | TDeviceID dev; |
| + | // COM10を9600bpsでオープン |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 9600); |
| + | |
| + | ***DX2_ClosePort [#w1ab7cbb] |
| + | DX2_OpenPortで開いたCOMポートを閉じる。~ |
| + | DX2_ClosePortが実行された以後は指定された[[TDeviceID>#TDeviceID]]での通信が行えなくなる。 |
| + | bool DX2_ClosePort (TDeviceID dvid); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | -戻り値 |
| + | --bool |
| + | ~クローズに成功した場合はtrue、失敗した場合はfalseを返す。 |
| + | -使用例 |
| + | TDeviceID dev; |
| + | // オープン |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 9600); |
| + | if (dev) { |
| + | ... (中略) |
| + | // クローズ |
| + | DX2_ClosePort (dev); |
| + | } |
| + | |
| + | ***DX2_SetBaudrate [#ba9f52b8] |
| + | 既にオープンされている[[TDeviceID>#TDeviceID]]の通信速度の変更を行う。~ |
| + | 実行すると強制的に受信バッファがクリアされる。~ |
| + | なお、Linux環境におけるボーレートの設定は、POSIX.1でサポートする値(50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, 3000000, 3500000, 4000000)であればtcsetattrを使用して処理するが、これらの値に当てはまらない場合はioctrlを使用する。その際I/Fがこれらのボーレートに対応していなかったり、ioctrlをサポートしない場合、本APIは失敗する。 |
| + | bool DX2_SetBaudrate (TDeviceID dvid, long baud); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --long '''baud'''~ |
| + | ~新しい通信速度[bps]。 |
| + | -戻り値 |
| + | --bool |
| + | ~通信速度の変更が成功するとtrue、失敗するとfalseを返す。~ |
| + | -使用例 |
| + | TDeviceID dev; |
| + | // オープン |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 9600); |
| + | if (dev) { |
| + | // 通信速度を1M[bps]に変更 |
| + | DX2_SetBaudrate (dev, 1000000); |
| + | ... (中略) |
| + | // クローズ |
| + | DX2_ClosePort (dev); |
| + | } |
| + | |
| + | ***DX2_Active [#ie20e4a7] |
| + | 指定された[[TDeviceID>#TDeviceID]]のポートが開かれており、使用可能であるかを確認する。~ |
| + | USB接続等によりインターフェース自体が取り外し可能な場合に、実際に使用可能であるかを判断するために使用するが、状況によっては正確に判断できない場合もある。 |
| + | bool DX2_Active (TDeviceID dvid); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | -戻り値 |
| + | --bool |
| + | ~指定されたdvidが使用可能な場合はtrue、使用不可の場合はfalseを返す。 |
| + | -使用例 |
| + | TDeviceID dev; |
| + | // オープン |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 9600); |
| + | if (dev) { |
| + | while (DX2_Active (dev)) { |
| + | ... (中略) |
| + | } |
| + | // クローズ |
| + | DX2_ClosePort (dev); |
| + | } |
| + | |
| + | ***DX2_SetTimeOutOffset [#h4a1b54e] |
| + | I/FやOSの都合で生じるであろうタイムラグを予め設定する。~ |
| + | 内部で算出している受信タイムアウト時間とタイムアウトオフセット時間を加算した時間を超えた場合に、タイムアウトエラーとして処理する。~ |
| + | デフォルトは20。 |
| + | void DX2_SetTimeOutOffset (TDeviceID dvid, uint32_t offsettime); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --uint32_t '''offsettime''' |
| + | ~タイムアウトオフセット時間[ms] |
| + | -戻り値 |
| + | --bool |
| + | ~指定されたdvidが使用可能な場合はtrue、使用不可の場合はfalseを返す。 |
| + | |
| + | ***DX2_Ping [#v393b505] |
| + | PINGインストラクションを使用して対象IDからの応答を確認する。 |
| + | bool DX2_Ping (TDeviceID dvid, uint8_t id, TErrorCode *err); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --uint8_t '''id''' |
| + | ~対象とするID (0~254)。 |
| + | --[[TErrorCode>#TErrorCode]] '''*err''' |
| + | ~エラーコード。 |
| + | -戻り値 |
| + | --bool |
| + | ~正常な応答が得られた場合はtrue、それ以外はfalseを返す。~ |
| + | -使用例 |
| + | TDeviceID dev; |
| + | TErrorCode err; |
| + | // オープン |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 57143); |
| + | if (dev) { |
| + | // ID=1にPINGを発行 |
| + | if (DX2_Ping (dev, 1, &err)) |
| + | printf ("Found [%08X]\n", err); |
| + | else |
| + | printf ("Not found [%08X]\n", err); |
| + | // クローズ |
| + | DX2_ClosePort (dev); |
| + | } |
| + | |
| + | ***DX2_Ping2 [#g16504b3] |
| + | PINGインストラクションでBROADCASTING IDを指定して不特定の対象の応答を確認する。~ |
| + | BROADCASTING IDを指定した場合の応答時間がデバイスのシリーズによって差があるため、それらが混在している環境では正確な情報を取得できない場合がある。 |
| + | bool DX2_Ping2 (TDeviceID dvid, uint32_t *num, TDxAlarmStatus *AlarmStatus, TErrorCode *err); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --uint32_t '''*num''' |
| + | ~検索する最大数及び検索で見つかったデバイス数の保存先。 |
| + | --[[TDxAlarmStatus>#TDxAlarmStatus]] '''*AlarmStatus''' |
| + | ~検索で見つかったデバイス情報の保存先。~ |
| + | 少なくともnumで指定したサイズを確保しておく必要がある。 |
| + | --[[TErrorCode>#TErrorCode]] '''*err''' |
| + | ~エラーコード。 |
| + | -戻り値 |
| + | --bool |
| + | ~1台以上のデバイスからの応答が得られた場合はtrue、それ以外はfalseを返す。~ |
| + | -使用例 |
| + | TDeviceID dev; |
| + | TErrorCode err; |
| + | uint8_t id; |
| + | TDxAlarmStatus stat[254]; |
| + | int i; |
| + | uint32_t num = 254; |
| + | // オープン |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 57143); |
| + | if (dev) { |
| + | // 不明な対象に対してPINGを発行 |
| + | if (DX2_Ping2 (dev, &num, stat, &err)) { |
| + | for (i = 0; i < num; i++) |
| + | printf ("Found ID=%d %02X\n", stat[i].id, stat[i].Status); |
| + | } |
| + | // クローズ |
| + | DX2_ClosePort (dev); |
| + | } |
| + | |
| + | ***DX2_ReadByteData [#u7d86432] |
| + | 対象IDのコントロールテーブルから1バイトのデータを読み出す。 |
| + | bool DX2_ReadByteData(TDeviceID dvid, uint8_t id, uint16_t adr, uint8_t *rdata, TErrorCode *err); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --uint8_t '''id''' |
| + | ~対象とするID (0~252)。 |
| + | --uint16_t '''adr''' |
| + | ~コントロールテーブルのアドレス。 |
| + | --uint8_t '''*rdata''' |
| + | ~読み出した値の保存先。 |
| + | --[[TErrorCode>#TErrorCode]] '''*err''' |
| + | ~エラーコード。 |
| + | -戻り値 |
| + | --bool |
| + | ~正常な応答が得られた場合はtrue、それ以外はfalseを返す。~ |
| + | -使用例 |
| + | TDeviceID dev; |
| + | TErrorCode err; |
| + | uint8_t dat; |
| + | // オープン |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 1000000); |
| + | if (dev) { |
| + | // ID=1のXL-320からLEDの状態を取得 |
| + | if (DX2_ReadByteData (dev, 1, 25, &dat, &err)) { |
| + | printf ("LED STAT=%d\n", dat); |
| + | } |
| + | DX2_ClosePort (dev); |
| + | } |
| + | |
| + | ***DX2_WriteByteData [#ma2205f0] |
| + | 対象IDのコントロールテーブルへ1バイトのデータを書き込む。 |
| + | bool DX2_WriteByteData(TDeviceID dvid, uint8_t id, uint16_t adr, uint8_t dat, TErrorCode *err); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --uint8_t '''id''' |
| + | ~対象とするID (0~252, 254)。 |
| + | --uint16_t '''adr''' |
| + | ~コントロールテーブルのアドレス。 |
| + | --uint8_t '''dat'''~ |
| + | ~書き込む値。 |
| + | --[[TErrorCode>#TErrorCode]] '''*err''' |
| + | ~エラーコード。 |
| + | -戻り値 |
| + | --bool |
| + | ~正常な応答が得られた場合はtrue、それ以外はfalseを返す。~ |
| + | BROADCASTING IDを指定した場合は応答待ちを行わない。 |
| + | -使用例 |
| + | TDeviceID dev; |
| + | TErrorCode err; |
| + | uint8_t dat; |
| + | // オープン |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 1000000); |
| + | if (dev) { |
| + | // ID=1のXL-320からLEDの状態を取得 |
| + | if (DX2_ReadByteData (dev, 1, 25, &dat, &err)) { |
| + | dat = (dat + 1) & 7; |
| + | // ID=1のXL-320へLEDの状態を設定 |
| + | DX2_WriteByteData (dev, 1, 25, dat, &err); |
| + | } |
| + | DX2_ClosePort (dev); |
| + | } |
| + | |
| + | ***DX2_ReadWordData [#x202d573] |
| + | 対象IDのコントロールテーブルから1ワード(2バイト)のデータを読み出す。 |
| + | bool DX2_ReadWordData(TDeviceID dvid, uint8_t id, uint16_t adr, uint16_t *rdata, TErrorCode *err); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --uint8_t '''id''' |
| + | ~対象とするID (0~252)。 |
| + | --uint16_t '''adr''' |
| + | ~コントロールテーブルのアドレス。 |
| + | --uint16_t '''*rdata''' |
| + | ~読み出した値の保存先。 |
| + | --[[TErrorCode>#TErrorCode]] '''*err''' |
| + | ~エラーコード。 |
| + | -戻り値 |
| + | --bool |
| + | ~正常な応答が得られた場合はtrue、それ以外はfalseを返す。~ |
| + | -使用例 |
| + | TDeviceID dev; |
| + | TErrorCode err; |
| + | uint16_t dat; |
| + | // オープン |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 1000000); |
| + | if (dev) { |
| + | // ID=1のXL-320から現在位置を取得 |
| + | if (DX2_ReadWordData (dev, 1, 30, &dat, &err)) { |
| + | printf ("PRESENT POS=%d\n", dat); |
| + | } |
| + | DX2_ClosePort (dev); |
| + | } |
| + | |
| + | ***DX2_WriteWordData [#pcc1b3ce] |
| + | 対象IDのコントロールテーブルへ1ワード(2バイト)のデータを書き込む。 |
| + | bool DX2_WriteWordData(TDeviceID dvid, uint8_t id, uint16_t adr, uint16_t dat, TErrorCode *err); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --uint8_t '''id''' |
| + | ~対象とするID (0~252, 254)。 |
| + | --uint16_t '''adr''' |
| + | ~コントロールテーブルのアドレス。 |
| + | --uint16_t '''dat''' |
| + | ~書き込む値。 |
| + | --[[TErrorCode>#TErrorCode]] '''*errcode''' |
| + | ~エラーコード。 |
| + | -戻り値 |
| + | --bool |
| + | ~正常な応答が得られた場合はtrue、それ以外はfalseを返す。~ |
| + | BROADCASTING IDを指定した場合は応答待ちを行わない。 |
| + | -使用例 |
| + | TDeviceID dev; |
| + | TErrorCode err; |
| + | // オープン |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 1000000); |
| + | if (dev) { |
| + | // ID=1のXL-320へ位置(511)を指令 |
| + | DX2_WriteWordData (dev, 1, 30, 511, &err); |
| + | DX2_ClosePort (dev); |
| + | } |
| + | |
| + | ***DX2_ReadBlockData [#b8a1ae1e] |
| + | 対象IDのコントロールテーブルから指定サイズのデータを読み出す。 |
| + | bool DX2_ReadBlockData (TDeviceID dvid, uint8_t id, uint16_t adr, uint8_t *rdata, uint32_t len, TErrorCode *err); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --uint8_t '''id''' |
| + | ~対象とするID (0~252)。 |
| + | --uint16_t '''adr''' |
| + | ~コントロールテーブルのアドレス。 |
| + | --uint8_t '''*rdata''' |
| + | ~読み出したデータの保存先。 |
| + | --uint32_t '''len''' |
| + | ~読み出すデータのサイズ。 |
| + | --[[TErrorCode>#TErrorCode]] '''*err''' |
| + | ~エラーコード。 |
| + | -戻り値 |
| + | --bool |
| + | ~正常な応答が得られた場合はtrue、それ以外はfalseを返す。~ |
| + | -使用例 |
| + | TDeviceID dev; |
| + | TErrorCode err; |
| + | uint8_t gain[3]; |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 1000000); |
| + | if (dev) { |
| + | // ID=1のXL-320からPIDゲインのデータを取得 |
| + | if (DX2_ReadBlockData (dev, 1, 27, comp, 3, &err) { |
| + | printf ( |
| + | "D=%d I=%d P=%d\n", |
| + | gain[0], gain[1], gain[2] |
| + | ); |
| + | } |
| + | DX2_ClosePort (dev); |
| + | } |
| + | |
| + | ***DX2_WriteBlockData [#fcf55aa0] |
| + | 対象IDのコントロールテーブルへ指定サイズのデータを書き込む。 |
| + | bool DX2_WriteBlockData(TDeviceID dvid, uint8_t id, uint16_t adr, uint8_t *dat, uint32_t len, TErrorCode *err); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --uint8_t '''id''' |
| + | ~対象とするID (0~252, 254)。 |
| + | --uint16_t '''adr''' |
| + | ~コントロールテーブルのアドレス。 |
| + | --uint8_t '''*dat''' |
| + | ~書き込むデータの保存先。 |
| + | --uint32_t '''len''' |
| + | ~書き込むデータのサイズ。 |
| + | --[[TErrorCode>#TErrorCode]] '''*err''' |
| + | ~エラーコード。 |
| + | -戻り値 |
| + | --bool |
| + | ~正常な応答が得られた場合はtrue、それ以外はfalseを返す。~ |
| + | BROADCASTING IDを指定した場合は応答待ちを行わない。 |
| + | -使用例 |
| + | TDeviceID dev; |
| + | TErrorCode err; |
| + | uint8_t gain[3] = { 5, 1, 35 }; |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 1000000); |
| + | if (dev) { |
| + | // ID=1のXL-320のPIDゲインを変更 |
| + | DX2_WriteBlockData (dev, 1, 27, gain, 3, &err); |
| + | DX2_ClosePort (dev); |
| + | } |
| + | |
| + | ***DX2_ReadSyncData [#iac9751e] |
| + | SYNC READインストラクションを使用して複数IDからブロック読み出しを行う。~ |
| + | 読み出されるデータの1軸分の構造は[ID(8bit)][ERR(16bit)][DATA1(8bit)]...[DATAn(8bit)]となり、複数軸の場合は本データが軸数分だけスタックする。 |
| + | bool DX2_ReadSyncData (TDeviceID dvid, const TSyncReadParam *param, uint32_t *num, uint8_t *dat, TErrorCode *err); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --[[TSyncReadParam>#TSyncReadParam]] '''*param''' |
| + | ~パラメータデータを指定。 |
| + | --uint32_t '''*num''' |
| + | ~読み出すデバイス数を指定。APIの処理後に応答したデバイス数が入る |
| + | --uint8_t '''dat''' |
| + | ~取得データの保存先。((受信データ長 + 3) * num)バイト以上を確保の事。 |
| + | --[[TErrorCode>#TErrorCode]] '''*err''' |
| + | ~エラーコード。 |
| + | -戻り値 |
| + | --bool |
| + | ~指定したデバイス数が応答したらtrue、それ以外はfalseを返す。~ |
| + | -使用例 |
| + | |
| + | // XM430のRealtimeTick(120番地)を読み出す想定の構造体 |
| + | typedef struct { |
| + | uint8_t id; |
| + | TErrorCode err; |
| + | uint16_t RTTick; |
| + | } __attribute__ ((gcc_struct, __packed__)) TSyncR[3]; |
| + | |
| + | TDeviceID dev; |
| + | TErrorCode err; |
| + | TSyncR rdat; |
| + | |
| + | // 120番地から2バイト分のデータをid=1,2,3より読み出す |
| + | const TSyncReadParam param = { |
| + | addr:120, |
| + | length:2, |
| + | ids:{1,2,3}, |
| + | }; |
| + | |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 1000000); |
| + | if (dev) { |
| + | uint32_t num = 3; // 3軸 |
| + | DX2_ReadSyncData (dev, ¶m, &num, (uint8_t *)&rdat, &err); |
| + | for (int i = 0; i < num; i++) { |
| + | printf("\nID:%d ERR:%04X ", rdat[i].id, rdat[i].err, rdat[i].RTTick); |
| + | } |
| + | DX2_ClosePort (dev); |
| + | }; |
| + | |
| + | ***DX2_WriteSyncData [#v566e16b] |
| + | SYNCインストラクションを使用して複数IDへブロック書き込みを行う。~ |
| + | 書き込まれるデータの構成はユーザに委ねられる。 |
| + | bool DX2_WriteSyncData (TDeviceID dvid, uint8_t *dat, uint32_t size, TErrorCode *err); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --uint8_t '''*dat''' |
| + | ~書き込むパラメータの保存先。 |
| + | --uint32_t '''size''' |
| + | ~パラメータのサイズ。 |
| + | --[[TErrorCode>#TErrorCode]] '''*err''' |
| + | ~エラーコード。 |
| + | -戻り値 |
| + | --bool |
| + | ~インターフェースより送信が行われた場合はtrue、それ以外はfalseを返す。~ |
| + | -使用例 |
| + | #define _POS1 (400) |
| + | #define _POS2 (511) |
| + | |
| + | TDeviceID dev; |
| + | TErrorCode err; |
| + | uint8_t param[8] = { |
| + | 30, // アドレス (Goal Position) |
| + | 2, // データ長 (2 byte) |
| + | // ID=1用データ |
| + | 1, |
| + | _POS1 & 0xff, |
| + | _POS1 >> 8, |
| + | // ID=2用データ |
| + | 2, |
| + | _POS2 & 0xff, |
| + | _POS2 >> 8 |
| + | }; |
| + | |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 1000000); |
| + | if (dev) { |
| + | // 2つのAX-12へ同時に位置を指令 |
| + | DX2_WriteSyncData (dev, param, 8, &err); |
| + | DX2_ClosePort (dev); |
| + | }; |
| + | |
| + | ***DX2_ReadBulkData [#wf7d9427] |
| + | BULK READインストラクションを使用して複数IDの指定アドレス・サイズでブロック読み出しを行う。~ |
| + | 読み出されるデータの1軸分の構造は[SIZE(16bit)][ID(8bit)][ERR(16bit)][PRAM1(8bit)]...[PARAMn(8bit)]の様にデータの前にSIZE(SIZEを含む1軸分のデータ長)・ID・ERRの計5バイトが付与され、複数軸の場合は本データが軸数分だけスタックする。 |
| + | bool DX2_ReadBulkData (TDeviceID dvid, const TBulkReadParam *param, uint32_t *num, uint8_t *dat, TErrorCode *err); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --[[TBulkReadParam>#TBulkReadParam]] '''*param''' |
| + | ~パラメータデータを指定。 |
| + | --uint32_t '''*num''' |
| + | ~読み出すデバイス数を指定。APIの処理後に応答したデバイス数が入る |
| + | --uint8_t '''dat''' |
| + | ~取得データの保存先。十分な領域を確保の事。 |
| + | --[[TErrorCode>#TErrorCode]] '''*err''' |
| + | ~エラーコード。 |
| + | -戻り値 |
| + | --bool |
| + | ~指定したデバイス数が応答したらtrue、それ以外はfalseを返す。~ |
| + | -使用例 |
| + | |
| + | TDeviceID dev; |
| + | TErrorCode err; |
| + | uint8_t rdat[1000]; |
| + | |
| + | const TBulkReadParam param[3] = { |
| + | {1,124,4}, // ID=1の124番地から4バイト分 |
| + | {2,128,8}, // ID=2の128番地から8バイト分 |
| + | {3,136,8} // ID=3の136番地から8バイト分 |
| + | }; |
| + | |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 1000000); |
| + | if (dev) { |
| + | uint32_t num = 3; // 3軸 |
| + | PBulkReadResult pr = (void *)rdat; |
| + | |
| + | DX2_ReadBulkData (dev, ¶m, &num, (uint8_t *)&rdat, &err); |
| + | for (int i = 0; i < num; i++) { |
| + | if (pr->size > 0) { |
| + | printf("\nID:%d ERR:%04X ",pr->id, pr->err); |
| + | for (int j = 0; j < pr->size - 5; j++) printf("[%02X]",pr->dat[j]); |
| + | pr = (void *)pr + pr->size; // 次のデータ用にポインタを更新 |
| + | } |
| + | DX2_ClosePort (dev); |
| + | }; |
| + | |
| + | ***DX2_TxPacket [#r051327a] |
| + | 任意のインストラクションパケットを送信する。 |
| + | bool DX2_TxPacket (TDeviceID dvid, uint8_t id, TInstruction inst, uint8_t *param, uint32_t len, TErrorCode *err); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --uint8_t '''id''' |
| + | ~対象とするID (0~254)。 |
| + | --[[TInstruction>#TInstruction]] '''inst''' |
| + | ~使用するインストラクション。 |
| + | --uint8_t '''*param''' |
| + | ~送信するパラメータの保存先。 |
| + | --uint32_t '''len'''~ |
| + | ~送信するパラメータのサイズ。 |
| + | --[[TErrorCode>#TErrorCode]] '''*err''' |
| + | ~エラーコード。 |
| + | -戻り値 |
| + | ~インターフェースより送信が行われた場合はtrue、それ以外はfalseを返す。 |
| + | -使用例 |
| + | TDeviceID dev; |
| + | TErrorCode err; |
| + | uint8_t param[2] = { |
| + | 25, // アドレス (LED) |
| + | 0, // データ |
| + | }; |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 1000000); |
| + | if (dev) { |
| + | // ID=1のAX-12+のLEDを消灯 |
| + | DX2_TxPacket (dev, 1, INST_WRITE, param, 2, &err); |
| + | DX2_ClosePort (dev); |
| + | }; |
| + | |
| + | ***DX2_RxPacket [#r280cda8] |
| + | ステータスパケットを受信する。~ |
| + | 基本的にDX2_TxPacketとペアで使用する。ステータスパケットが得られない状況で使用するとタイムアウトするまで返らない。~ |
| + | なお、本APIは[[DX2_SetTimeOutOffset>#h4a1b54e]]で設定されたオフセット値は使用せず、引数で指定された受信タイムアウトのみが適用される。 |
| + | bool DX2_RxPacket (TDeviceID dvid, uint8_t *rdata, uint32_t rdatasize, uint32_t *rlen, uint32_t timeout, TErrorCode *err); |
| + | -パラメータ |
| + | --[[TDeviceID>#TDeviceID]] '''dvid''' |
| + | ~DX2_OpenPortで開いた際の[[TDeviceID>#TDeviceID]]。 |
| + | --uint8_t '''*rdata''' |
| + | ~受信バッファ。~ |
| + | ステータスパケットを受信するのに十分なサイズを確保しておく必要がある。 |
| + | --uint32_t '''readsize''' |
| + | ~rdataのサイズ。~ |
| + | --uint32_t '''*rlen''' |
| + | ~実際に受信されたステータスパケットのサイズ。 |
| + | --int '''timeout''' |
| + | ~受信タイムアウト[ms]。 |
| + | --[[TErrorCode>#TErrorCode]] '''*err''' |
| + | ~エラーコード。 |
| + | -戻り値 |
| + | ~受信成功時はtrue、それ以外はfalseを返す。 |
| + | -使用例 |
| + | int i; |
| + | uint32_t len; |
| + | TDeviceID dev; |
| + | TErrorCode err; |
| + | uint8_t param[2] = { |
| + | 25, // アドレス (LED) |
| + | 1, // サイズ |
| + | }; |
| + | |
| + | dev = DX2_OpenPort ("\\\\.\\COM10", 1000000); |
| + | if (dev) { |
| + | // ID=1のAX-12+からLEDの状態を読み出す要求 |
| + | if (DX2_TxPacket (dev, 1, INST_READ, param, 2, &err)) { |
| + | // ステータスパケットを受信 |
| + | DX2_RxPacket (dev, dat, sizeof (dat), &len, 100, &err); |
| + | for (i = 0; i < len; i++) { |
| + | printf ("[%02X]", dat[i]); |
| + | } |
| + | } |
| + | DX2_ClosePort (dev); |
| + | }; |
| + | |
| + | ***DXLIB2のオリジナルな定義 [#af9ca340] |
| + | &aname(TDeviceID); |
| + | :TDeviceID | ''(uint32_t|uint64_t)''~ |
| + | インターフェース毎に割り当てられるユニークな値。DX2_OpenPortにて自動的に生成される。 |
| + | &aname(TInstruction);~ |
| + | :TInstruction | ''(uint8_t)''~ |
| + | DX2_TxPacketにてインストラクションパケットを送信する場合に使用される。~ |
| + | 使用可能なマクロは以下の通り。~ |
| + | ~INST_PING~ |
| + | INST_READ~ |
| + | INST_WRITE~ |
| + | INST_REG_WRITE~ |
| + | INST_ACTION~ |
| + | INST_RESET~ |
| + | INST_REBOOT~ |
| + | INST_STATUS~ |
| + | INST_SYNC_READ~ |
| + | INST_SYNC_WRITE~ |
| + | INST_BULK_READ~ |
| + | INST_BULK_WRITE |
| + | &aname(TDxAlarmStatus); |
| + | :TDxAlarmStatus | struct {&br; uint8_t id;&br; TErrorCode Status;&br; |
| + | }~ |
| + | idとTErrorCodeを対にした構造体でアライメントは1バイト。 |
| + | &aname(TSyncReadParam); |
| + | :TSyncReadParam | struct {&br; uint16_t addr;&br; uint16_t length;&br; uint8_t ids[254];&br;}~ |
| + | addr, length, idsをまとめた構造体でアライメントは1バイト。 |
| + | &aname(TBulkReadParam); |
| + | :TBulkReadParam | struct {&br; uint8_t id;&br; uint16_t addr;&br; uint16_t length;&br;}~ |
| + | id, addr, lengthをまとめた構造体でアライメントは1バイト。 |
| + | &aname(TErrorCode); |
| + | :TErrorCode | ''(uint16_t)''~ |
| + | API内で検出される16ビットのエラーコード。上位8ビットはAPI内部で検出したエラー、下位8ビットはステータスパケットに含まれるエラーフラグが割り当てられている。~ |
| + | |CENTER:|LEFT:|LEFT:|c |
| + | |bit|macro name| |h |
| + | |15|ERR_INVALID_DEVID|使用できないTDeviceID | |
| + | |14|ERR_INVALID_ID|指定できないID | |
| + | |13|ERR_DIFF_ID|異なるIDからの応答 | |
| + | |12|ERR_ILLEGAL_SIZE|異常なデータサイズ | |
| + | |11|ERR_INVALID_PARAM|異常なパラメータ | |
| + | |10|ERR_COMM|シリアルポートエラー | |
| + | |9|ERR_CHECKSUM|異常なチェックサム | |
| + | |8|ERR_TIMEOUT|受信タイムアウト | |
| + | |7|ERR_DX2_ALERT|ハード的な異常検出 | |
| + | |6|ERR_DX2_LENGTHLONG=6&br;ERR_DX2_LENGTHSHORT=5&br;ERR_DX2_OVERRANGE=4&br;ERR_DX2_CRCERROR=3&br;ERR_DX2_UNDEFINST=2&br;ERR_DX2_INSTERROR=1|パケットの処理や数値範囲に関するエラー | |
| + | |5|~|~| |
| + | |4|~|~| |
| + | |3|~|~| |
| + | |2|~|~| |
| + | |1|~|~| |
| + | |0|~|~| |