AN-2634: EnDat 2.1/2.2 アブソリュート・エンコーダ・プロトコルの TMC8100 向けサポート
説明
TMC8100 は、最大16Mbit/s のシリアル同期/非同期アブソリュート・エンコーダ・プロトコル用に最適化された、プログラマブル・マイクロコントローラを内蔵しています。このデバイスは、専用のエンコーダ・プロトコル・インターフェースIC やフィールド・プログラマブル・ゲート・アレイ(FPGA)の実装に代えて使用できる一方で、様々なエンコーダ機能のシステム内アップデートや、他のエンコーダ・プロトコルへの切り替えもサポートしています。TMC8100 は小型でコスト効率に優れた柔軟な通信ソリューションであり、工業用ドライブにアブソリュート・エンコーダのサポート機能を付加します。
このアプリケーション・ノートでは、EnDat 2.1/2.2 インターフェースを備えたアブソリュート・ポジション・エンコーダをサポートするTMC8100 ソフトウェアの実装例を詳細に示します。
システムの説明
工業用アプリケーションなどで用いられるサーボ・モータ・ドライブには、通常、正確で信頼性が高く、しかも低レイテンシの位置フィードバックが必要です。これまで長期にわたり、インクリメンタル A/B/N出力の光エンコーダが業界標準として使われています。それにもかかわらず、アブソリュート・ポジション・エンコーダが次第に多用されるようになっており、その多くは機能を追加し、異なるインターフェース・プロトコル(ほとんどがベンダー固有)を使っています。その一例がHeidenhainのEnDat 2.1/2.2プロトコルで、これはエンコーダとコントローラ間でのデジタル・データのシリアル転送に使われます。これは 2 つの別々の接続を使用します。1 つは、コントローラによって生成されたクロック用(単方向)、もう 1つは、コントローラとエンコーダの間で転送されるデータ用(双方向、半二重)です。物理層はRS-485規格に基づいており、位置の値と診断情報をエンコーダからコントローラへ送信できます。また、エンコーダ内のレジスタとの間で読書きもできます(エンコーダ構成など)。
この例では、エンコーダの位置とステータスの情報の読出しと抽出に焦点を合わせます。半二重シリアル通信を使用します。
EnDat 2.2インターフェース付きエンコーダは、6本のワイヤで構成される接続ケーブル1本でTMC8100-EVAL-KITに接続します(図1)。6 本のワイヤの内訳は次のとおりです。
- +5VおよびGND:エンコーダ電源とグラウンド接続。
- CLOCK+およびCLOCK−:コントローラからエンコーダにクロックを送信するための差動RS-485信号。
- DATA+およびDATA−:エンコーダとコントローラの間でデータを送信するための差動RS-485信号。
リファレンス実装は次のような機能を備えています。
- 必要な周波数、パルス数、極性を持つクロック信号を出力。
- ライン遅延の補償のために受信データのヘッダに同期する受信データの内部サンプル・クロックを分離。
- 受信データのCRCチェックサムをその場で計算。
- EnDatエンコーダによってサポートされる1MHzのクロック信号の例。
- データのパッキングとパッキング解除。
リファレンス実装はソース・コードで提供されます。ユーザはこれを出発点として使用し、アプリケーションの必要に応じて変更を加えることができます。
システムの概要
提供されるソフトウェアは、TMC8100-EVAL-KIT と共に使用して動作するよう設計されており、EnDat 2.2 インターフェースを使用してHEIDENHAIN製のEQN 1135 512エンコーダでテストされています。
TM8100-EVAL-KIT に必要なコア・ハードウェア・コンポーネントは TMC8100 と 2 個の RS485 トランシーバです。RS485 トランシーバは、TMC8100 の信号とコネクタ部での差動 RS485信号(CLOCK+/CLOCK−信号および DATA+/DATA−信号)との間の変換に使用します(図 2)。このプロトコルでは、CLOCK 信号用の RS485 トランシーバは恒久的に出力/トランスミッタとして構成されるのに対し、DATA信号用のRS485トランシーバは切り替えられます。
ソフトウェアには、EnDat 2.x プロトコルをサポートするためのコントローラ機能を実装した TMC8100 のファームウェアが含まれています。このファームウェアは、パワーアップ後に TMC8100 にダウンロードする必要があります。ファームウェアの選択とダウンロード、およびその後にエンコーダと共にファームウェア機能のデモとテストを行うための追加 GUI(グラフィカル・ユーザ・インターフェース)は、Pythonスクリプトとして提供されます。
ENDAT 2.1/2.2 プロトコル
EnDat 2.1/2.2 は、エンコーダの値を送信するためのデジタルのシリアル・インターフェース・プロトコルです。EnDatプロトコルは、位置情報およびステータス情報の他、レジスタの読出しおよび書込み動作もサポートします。このアプリケーション・ノートでは、コントローラとしてのTMC8100とEnDat 2.1/2.2インターフェースを備えるエンコーダとの間のポイントtoポイント通信と、エンコーダの位置データおよびステータス情報の読出しに焦点を合わせます。
エンコーダ・データの読出しは、CLOCKライン(コネクタ・ピンCLK+/CLK−)上にクロック・パルスを出力し、それと並行して、エンコーダへのDATA接続(コネクタ・ピンDATA+およびDATA−)にModeコマンドを出力することにより、TMC8100が制御します。その後、エンコーダは、その応答を DATA ライン(DATA+ / DATA−ピンに接続)に生成し、シリアル化された情報を受信クロック信号の立上がりエッジでシフト・アウトします。
このアプリケーションでは、EnDat 2.1およびEnDat 2.2のコマンド・セットで使用できるエンコーダ位置の値を読み出すために、ModeコマンドNo.1(M2/M1/M0 = "000")を用います。
アイドル状態時には、データ伝送は行われず、CLOCKラインはハイ(「1」)、DATAラインはロー(「0」)のままになります。
新しいエンコーダ値を取り出すために、TMC8100は、CLOCKラインにクロック・パルスを発生します。TMC8100は、送信の最初の部分で、DATA ラインの RS-485 トランシーバをオンにし、Modeコマンドを送信します。3つの Modeコマンド・ビットが非反転で送信され(ModeコマンドNo.1の場合、M2/M1/M0はすべて「0」)、その後は反転されます(ModeコマンドNo.1の場合すべて「1」)。
モード・コマンドが送信されると、DATAラインの RS-485トランシーバは再度オフに切り替わり、TMC8100はエンコーダから送られるデータグラムのStartビットの立上がりエッジを待ちます。TMC8100は、送信されたCLOCK信号のサイクル時間に基づき、受信ビットの中間をサンプリングします。
エンコーダから送られるデータグラムには、エラー・ビット(F1)、シングル・ターン・エンコーダの位置(この例で用いられるエンコーダでは23ビットで、LSBを最初に送信)、マルチ・ターン・エンコーダの位置(12ビット)、そして最後に6ビットのCRCチェックサムが含まれています。CRCチェックサムは、CRC多項式P(x) = x^5 + x^3 + x^1 + x^0を用いて、エラー・ビットとエンコーダ位置情報をカバーします。
ソフトウェアの概要
TMC8100用EnDat 2.xプロトコルのファームウェア実装は、ソース・コード「tmc8100-eval_endat_v10.asm」と、Intel 16進ファイル・フォーマットの機械語コード「tmc8100-eval_endat_v10.hex」で提供されます。機能の最初のテスト/評価用には、「tmc8100- eval_endat_v10.py」というPythonスクリプトを使用できます。これは、エンコーダを取り付けたTM8100-EVAL-KITにUSBを介して接続されたPC上で実行できます(図1)。
Pythonスクリプト・プログラムを実行するには、まずPythonインタープリタをPCにインストールして、「intelhex」および「pySerial」という Python ライブラリを使用する必要があります。このスクリプトは、グラフィカル・ユーザ・インターフェース用の「tkinter」を使用します。
TM8100-EVAL-KIT にエンコーダを接続して USBで PCに接続し、TM8100-EVAL-KITに+5Vを加えると、コマンドラインから Pythonスクリプトを実行できます(図4)。
最初に、TM8100-EVAL-KITへのUSB接続に使用する仮想COMポートを選ぶ必要があります。この例では「COM4」です。ターミナル・ウィンドウに表示された出力行には、既に Landungsbruecke(LB)への接続に成功して TMC8100 を検出したこと、およびそのチップ IDとリビジョン番号が示されます。
その後、別ウィンドウにグラフィカル・ユーザ・インターフェースが自動的に起動します(図5)。
まず、[Select Input File]フレームの[…]ボタン(1)を使い、TMC8100 用のサンプル・コードが格納された 16 進ファイル 「tmc8100-eval_endat_v10.hex」を選択します。次のステップとして[Load + Execute](2)を押すと、ブートローダによりファイルの内容がUSBとLandungsbruecke(LB)を用いて TMC8100 の SRAMプログラム・メモリに書き込まれて、プログラムが実行されます。このプログラムは、固有の通信プロトコルを使用してエンコーダにアクセスします。例えば異なるプログラムをダウンロードするためにTMC8100 を再びブートローダ・モードにするには、TMC8100 のリセットまたは電源サイクルを行う必要があります。Python GUI で[Load + Execute]を押すと、自動的にTMC8100をリセットしてから、新しいファームウェアをダウンロードします。
ウィンドウの中段にある[EnDat 2.2]フレームには、エンコーダの読出しを開始するためのコマンド・ボタンが 2 つあります。[Get Encoder position value]を押すと、それぞれの数のクロック・サイクルとMode No.1コマンド(エンコーダが位置の値を送信)を送信し、シングル・ターン(ST)位置、マルチ・ターン(MT)位置、エラー・フラグ、CRC チェックサムをエンコーダからリード・バックするよう求める命令が、TMC8100 にロードされたファームウェアに対し発せられます。両方の CRC チェックサム(エンコーダから受信したチェックサムと TMC8100 ファームウェアから計算したチェックサム)がここに表示され、情報として利用したり、デバッグのために使用したりできます。
[Read Encoder continuously]を押すと、エンコーダ値の読出しが Python プログラムによって連続的にトリガされます(図 7)。それによって、エンコーダ位置のST値(バイナリのみ)と、現在の360º絶対角位置を示す赤色の位置マーカーを備えたアナログ・ダイヤルホイールが更新されます。その他の値は、性能上の理由で更新されません。連続エンコーダ読出しを停止する場合、およびこれ以外のボタンを押す場合には、Pythonプログラムの実行を終了するか再起動する必要があります。
抽出/関連データの表示されるGUIと並行して、コマンド・ライン・ウィンドウには生の通信データといくつかの追加情報が表示されます。これらのデータと情報は、TMC8100のサンプル・プログラムを変更/拡張する際に役立ちます(図8)。
注:性能上の理由から、連続エンコーダ読出し中はコマンド・ライン・ウィンドウへの出力が省略されます。
ファームウェアの実装
サンプル・ソース・コード「tmc8100-eval_endat_v10.asm」は出発点として使用できる他、アプリケーションの要件に応じて変更できます。
ソース・コードの変換には、アセンブラを使用できます。サンプル・コードの概要をフロー・チャート(図10)に示します。
ファームウェアのソース・コードは、可読性向上のために、いくつかの定数値(例えばソフトウェア・バージョンやサポートされているプロトコル)の定義と、TMC8100内にある周辺装置のレジスタ・アドレスの定義から始まります(図9)。
設定を簡略化するために、EnDat のクロック周波数(ENDAT_CLOCK_TOGGLE_DIV)は、ここで設定できます。定数「ENDAT_CLOCK_TOGGLE_DIV」は、システム・クロック周波数(このファームウェア・プログラム例では 128MHz に設定)用の分周比であり、CLOCKラインのトグル・レートを定めます。このトグル・レートはクロック周波数の2倍です。例えば、EnDatのクロック周波数を1MHz、クロック・トグル・レートを2MHzとするには、分周比を64 − 1(クロック分周器は0からENDAT_CLOCK_TOGGLE_DIVで定まる制限値までカウントするため)に設定する必要があります。
SPI の設定
TMC8100 で使用できる標準的な SPI信号(SPI_CSN、SPI_SCLK、SPI_SDI、SPI_SDO)は設定不要です。これらの機能とパッケージのピン割り当ては固定されています。ただし、追加信号SPI_DATA_AVAILABLEを使用できます。この追加信号はGPIO(6)ピンの代替機能として設定でき、TMC8100 内のファームウェアによって SPI 出力バッファ(ハイ「1」を出力)にデータが書き込まれたことを表示できます。これにより、接続したマイクロコントローラへフィードバックを行い、更にマイクロコントローラが SPI データグラム/トランザクションを開始して、それ以前に TMC8100 から送信されたコマンドへの応答データをフェッチできます。この機能は起動後にブートローダによって既に設定されていますが、ここには確実を期すために示しています(図11)。
クロックの選択と初期化
TMC8100 は常に内部発振器で起動し、パワーオン/リセット後にブートローダが75MHzのシステム・クロック周波数でPLLを設定します。この例では、TM8100-EVAL-KITに搭載された16MHzの水晶を使用する発振器が使われています。PLL出力およびシステム周波数は128MHz に設定されています。通信用のリファレンス・クロックは TMC8100 自体に内蔵されているので、EnDat インターフェース使用時に必ずしも水晶クロックが必要なわけではありません。したがって、ここでは、PLLと共にTMC8100の内部クロックを使用することも1つの選択肢です。
最初のステップとして、GPIO0ピンおよびGPIO1ピンを内部水晶発振器との併用で外部水晶用に設定します(図12)。
次のステップでは、128MHzのPLL出力周波数(PLL_FB_128)と水晶発振器(XTAL)クロック回路に合わせてPLL帰還分周器が設定され、PLL入力におけるクロック周波数が1MHzとなるようにPLL入力分周器が設定されます(図13)。クロック・ブロックのアドレスはレジスタ書込みアクセスごとに間接的に指定されるので、4 つのコマンドが必要です。まずロード命令(LDI)とストア命令(ST)のペアでレジスタ・アドレスを設定し、その後にもう1つのロード命令とストア命令のペアで新しいレジスタ値を設定します。
最後の書込みアクセスは、すべての変更を適用するためにクロック・ブロックの内部ステート・マシンもトリガします。これには水晶発振器の起動とPLLロックが含まれるので、クロック・ブロックのステータス・レジスタをチェックして、新しい128MHzのシステム・クロックを使用できるようになるまで待つ必要があります(図 14)。したがって、クロック・ブロックの設定レジスタ(CLK_CTRL_PLL_CFG)のアドレスが選択され、ビット 7(TEST1 $7, r0)がクリアされるまでプログラム・ループがこのレジスタを読み出してから、初めてその後のプログラム実行へ移行します。
DIRECT_IN/DIRECT_OUT ピンの設定
図2に示すように、エンコーダとの通信にはTM8100-EVAL-KIT上にある両方のRS485トランシーバが使われます。ブロック図の上側にあるRS485トランシーバは、TMC8100からエンコーダへのCLOCK信号の送信に使用します。このRS485トランシーバの送信イネーブルは常時オンになっています。ここでは、DIRECT_OUT(0)は、クロック出力のために使用します。
ブロック図の下側にあるRS485トランシーバは、エンコーダにMODEコマンドを送信し、エンコーダからデータを受信するために使用します。DIRECT_IN(1)はシリアル・データ入力(常時イネーブル)に使用し、DIRECT_OUT(1)は TMC8100からエンコーダへのシリアル・データ出力に使用します。DIRECT_OUT(3)は、DATA 接続用 RS-485 トランシーバ内部のトランスミッタをイネーブル/ディスエーブルするために使用します。
アイドル状態ではCLOCKラインがハイ・レベル「1」なので、対応する出力のDIRECT_OUT(0)は反転されます。
SPI コマンド・ループ
TMC8100の設定後、プログラムはSPIを通じたコマンド受信をエンドレス・ループで待機します。すべてのSPIトランザクションは32ビットのデータグラムで行われます。このサンプル・コードのすべてのコマンドは、1つのデータグラム内に収まります。単純化のために、コマンドの選択と実行用に上位 8ビット(MSB、SPIを通じて最初に受信)だけがテストされます。コマンド・ループは SPIペリフェラル・ブロックのステータス・レジスタ(LDI SPI_STATUS, r0)の読出しから始まり、ステータス・レジスタのビット0が1に変わるまで待機状態となります(WAIT1 $0, r0)。このビット0の変化は、SPIデータグラムが受信されてSPI入力バッファ(SPI_BUFFER)に格納されたことを示します。
32ビットSPI入力バッファの内容の上位8ビットは、$80と比較されます。$80は、フラグと位置の値を含むエンコーダ・データを読み出すためにこのサンプル・コードで定義された SPI コマンドです。この比較が正常に終了すると、プログラムの実行はアドレス「EnDAT_encoder」にジャンプします。次いで、このアドレスのプログラム・コード(詳細は以下に記述)が DIRECT_OUT(0)を通じてCLOCK 信号を送信し、DIRECT_OUT(1)を通じて MODE コマンドを送信します。そして、DIRECT_IN(1)を通じてエンコーダから返される応答データを収集します。受信データは 32 ビットの SPIデータグラムにまとめられて、SPI出力バッファに格納されます。同時に、SPI_BUFFER_AVAILABLE/GPIO6 がロー「0」からハイ「1」に変化して、SPI トランザクションの新しいデータが使用可能になったことを示します。次の SPI トランザクションで、このデータを読み出すことができます。前のコマンドのすべてのデータが読み出される前に、新しいコマンドを送信してはいけません。1つのSPIトランザクションは常に両方向にデータを転送し、通常はすべての応答データを読み出すのに複数のトランザクションが必要になるので、「ダミー」コマンド(例えば0x00 0x00 0x00 0x00)を使用することが推奨されます。コマンド・ループは、追加読出しのためにこのダミー・コマンドを解釈することはありません。読出しの最後のトランザクションには、次のコマンドを含めることができます。
TMC8100によるエンコーダ実装「tmc8100-eval_endat.py」のテストに使用できるPythonスクリプトは、GUIに[Read Encoder]プッシュ・ボタンを提供します。このボタンを押すと、SPIデータグラム0x80 0x00 0x00 0x00の送信を指示する命令がLandungsbruecke(LB)に送られます(「tmc8100-eval_endat.py」から抜粋)。
その後、プログラムはエンコーダからの応答を待ちます。SPI_DATA_AVAILABLE/GPIO6 ピンは、TMC8100 の SPI 出力バッファに応答データが格納されると、すぐにロー「0」からハイ「1」に切り替わります。このデータの読出し時、Python プログラムは、TMC8100 のファームウェアによって解釈されないSPI「ダミー」コマンド0x00 0x00 0x00 0x00を使用します。
# get ST value
value = SpiWriteCommand([0x00, 0x00, 0x00, 0x00])
print(f"Encoder - ST: {value[0]:02x} {value[1]:02x} {value[2]:02x} {value[3]:02x}")
....
# get MT value
value = SpiWriteCommand([0x00, 0x00, 0x00, 0x00])
print(f"Encoder - MT: {value[0]:02x} {value[1]:02x} {value[2]:02x} {value[3]:02x}")
....
# get Flags + CRC
value = SpiWriteCommand([0x00, 0x00, 0x00, 0x00])
print(f"Encoder - Flags: {value[0]:02x} {value[1]:02x} {value[2]:02x} {value[3]:02x}")
....
サンプル・プログラムに使われているすべてのSPIコマンドと、それらに対する応答データの概要を以下の表に示します。SPIコマンドは常に1個の32ビット・データグラム内に収められますが、この例では応答には最大3個の32ビット・データグラムを使用できます。SPI 32 ビット・データグラムは4個の連続した16進数として与えられます。これは1バイトあたり1個の16進数で、可読性向上のためMSBファーストになっています。応答の場合、32 ビット・データグラムは SPI バッファに格納された順番(読み出せる順番)で示されます(先入れ/先出し(FIFO))。
| SPI-COMMAND (32-BIT) | SPI-REPLY (32-BIT) |
| 0x80 0x00 0x00 0x00 Read encoder position value/status/information. |
|
| 0xff 0x00 0x00 0x00 Get firmware version. |
|
| 0xfe 0x00 0x00 0x00 Get encoder protocol. |
|
[Read Encoder continuously]を押すと、Pythonプログラムが表の最初のコマンド0x80 0x00 0x00 0x00を繰り返し送信します。
エンコーダ・データの読出し
TMC8100ソフトウェアがSPIコマンドの受信を待っている状態で新しいSPIコマンドが受信され、なおかつ上位バイト/32ビット・データグラムから最初に受信したバイトが$80に等しい場合は、直ちにプログラム実行がプログラム・コード内の「EnDAT_encoder」アドレスにジャンプしてCRCブロックが初期化され、シリアル・データをシフト・インしながらオンザフライのCRC計算が開始されます。CRC多項式はP(x) = x^5 + x^3 + x^1 + x^0に設定され、開始値はゼロに設定されます。
EnDAT_encoder: .... ; initialize CRC for 5bit CRC LDI $0, r0 STS r0, SYSTEM_CRC, SYSTEM_CRC_CTRL_W ; reset CRC block LDI $ff, r0 STS r0, SYSTEM_CRC, SYSTEM_CRC_START_W ; LSB STS r0, SYSTEM_CRC, SYSTEM_CRC_START_W STS r0, SYSTEM_CRC, SYSTEM_CRC_START_W STS r0, SYSTEM_CRC, SYSTEM_CRC_START_W ; MSB LDI %0010_1011, r0 ; CRC polynomial: x^5 + x^3 + x^1 + x^0 STS r0, SYSTEM_CRC, SYSTEM_CRC_POLYNOM_W ; LSB LDI %0000_0000, r0 STS r0, SYSTEM_CRC, SYSTEM_CRC_POLYNOM_W STS r0, SYSTEM_CRC, SYSTEM_CRC_POLYNOM_W STS r0, SYSTEM_CRC, SYSTEM_CRC_POLYNOM_W ; MSB
次ステップとして、DIRECT_OUT(0)が CLOCKラインのクロック出力に設定されます。DIRECT_OUT(3)は「1」に設定されてDATAラインのRS-485トランシーバがイネーブルされ、DIRECT_OUT(1)は「0」に設定されてDATAラインの初期値が「0」に設定されます。
プログラム・ソース・ファイルの開始時の ENDAT_CLOCK_TOGGLE_DIV 定数設定値を用いると、DIRECT_OUT(0)の EnDat クロック出力信号は 1MHz に設定されます。システム・カウンタがオーバーフローすると、その都度クロック出力がトグルします。したがって、システム・カウンタは、ENDAT_CLOCK_TOGGLE_DIVの分周値を用いて出力クロック周波数の2倍に設定されます(この場合は2MHzのトグル・レート)。
システム・タイマー・カウンタの初期化の最終ステップとして、クロック生成がイネーブルされます。
; configure DIRECT_OUT(0) as clock output LDI $01, r0 ST DIRECT_ALT_FUNCTION, r0 ; set DATA = '0' - DIRECT_OUT(1) = '0', idle (non-inverted) SFCLR WAIT1SF NO_WAIT, 0, 1 ; configure DATA as output - DIRECT_OUT(3) = '1' SFSET WAIT1SF NO_WAIT, 0, 3 ; init counter LDI ENDAT_CLOCK_TOGGLE_DIV, r0 ; set toggle rate for EnDAT clock generator STS r0, SYSTEM_TIMER, SYSTEM_TIMER_COUNTER_LIMIT_W LDI 1, r0 ; enable counter STS r0, SYSTEM_TIMER, SYSTEM_TIMER_CTRL_W
エンコーダとの通信は、2つのフェーズに分離できます。つまり、TMC8100からエンコーダへMODEコマンドを送信するフェーズと、エンコーダからの応答を受信するフェーズです(図18)。
クロックがイネーブルされ、データ・ラインのRS-485トランスミッタがオンになると、MODEコマンド(非反転「000」と反転「111」)が送信されるまでプログラムは2 クロック・サイクルの間待機します。MODE コマンドは、「SHRO WAIT1SF WAIT_OVERFLOW_COUNTER, r0, FLAG_OUT1」という命令を用いて、クロック信号の立下がりエッジあるいはタイマーのオーバーフロー時にDIRECT_OUT(1)を通じてシフト・アウトされます。
MODEコマンドの後、DATAラインのRS-485トランスミッタがオフになってエンコーダからの応答を受信する準備ができるまで、プログラムは更に2クロック・サイクル待機します。
; 2T clock cycles WAIT0SF WAIT_IN0, WAIT_NO_ACTION ; wait for falling edge of clock signal WAIT1SF WAIT_IN0, WAIT_NO_ACTION ; wait for rising edge of clock signal WAIT0SF WAIT_IN0, WAIT_NO_ACTION ; wait for falling edge of clock signal WAIT1SF WAIT_IN0, WAIT_NO_ACTION ; wait for rising edge of clock signal ; shift out MODE command LDI %0011_1000, r0 REP 7, 2 SHRO WAIT1SF WAIT_OVERFLOW_COUNTER, r0, FLAG_OUT1 WAIT1SF WAIT_OVERFLOW_COUNTER, WAIT_NO_ACTION ; 2T clock cycles WAIT1SF WAIT_IN0, WAIT_NO_ACTION ; wait for rising edge of clock signal WAIT0SF WAIT_IN0, WAIT_NO_ACTION ; wait for falling edge of clock signal WAIT1SF WAIT_IN0, WAIT_NO_ACTION ; wait for rising edge of clock signal WAIT0SF WAIT_IN0, WAIT_NO_ACTION ; wait for falling edge of clock signal ; configure DATA as input - DIRECT_OUT(3) = '0' SFCLR WAIT1SF NO_WAIT, 0, 3
プログラムはシステム・タイマーの制限値をクロック・サイクルの半分に設定し、DATA ラインの立上がりエッジまで待機してからタイマーを自動的に始動します(WAIT1SF WAIT_IN1, WAITSTART_TIMER)。開始ビットの途中でタイマーがオーバーフローすると、タイマーの制限値は 1 クロック・サイクルに増加され、タイマー・オーバーフロー/データ・サンプリングと受信エンコーダ・データを同期させます。
次のタイマー・オーバーフローでシフト・インされる最初のビットはエラー・フラグ F1 です。これは、レジスタ r1 に格納されます(SHLI WAIT1SF WAIT_OVERFLOW_TIMER, r1, FLAG_IN1_CRC)。その後、23 ビットのシングル・ターン・エンコーダ位置(LSBファースト)がレジスタr2、r3、r4にシフト・インされ、12ビットのマルチ・ターン位置がレジスタr5、r6にシフト・インされます。エラー・ビットおよび位置ビットもCRCユニットにシフト・インされ、CRCチェックサム計算がオンザフライで実行されます。
最後に、エンコーダから受信した5ビット CRCチェックサムがレジスタr7にシフト・インされます。クロック信号の次の立上がりエッジでデータ転送が終了し、クロック生成がオフになります。
; prepare for encoder reply / data LDI ENDAT_CLOCK_TOGGLE_DIV - 1, r0 ; from rising edge of start bit to middle of start bit / ; half a clock cycle STS r0, SYSTEM_TIMER, SYSTEM_TIMER_LIMIT_W WAIT1SF WAIT_IN1, WAIT_START_TIMER ; wait for rising edge of start DATA bit '1' and start timer ; wait until middle of start bit WAIT1SF WAIT_OVERFLOW_TIMER, WAIT_NO_ACTION LDI ENDAT_BIT_LENGTH_DIV, r0 ; set sample point to middle of next bit / 1 clock cycle STS r0, SYSTEM_TIMER, SYSTEM_TIMER_LIMIT_NO_RESET_W ; middle of F1 error bit - shift in F1 error bit LDI 0, r1 SHLI WAIT1SF WAIT_OVERFLOW_TIMER, r1, FLAG_IN1_CRC ; encoder single turn data 23bits, LSB first LDI 0, r2 REP 8, 1 SHRI WAIT1SF WAIT_OVERFLOW_TIMER, FLAG_IN1_CRC, r2 LDI 0, r3 REP 8, 1 SHRI WAIT1SF WAIT_OVERFLOW_TIMER, FLAG_IN1_CRC, r3 LDI 0, r4 REP 7, 1 SHRI WAIT1SF WAIT_OVERFLOW_TIMER, FLAG_IN1_CRC, r4 ; encoder multi turn data 12bits LDI 0, r5 REP 8, 1 SHRI WAIT1SF WAIT_OVERFLOW_TIMER, FLAG_IN1_CRC, r5 LDI 0, r6 REP 4, 1 SHRI WAIT1SF WAIT_OVERFLOW_TIMER, FLAG_IN1_CRC, r6 ; encoder CRC checksum 5bits LDI 0, r7 REP 5, 1 SHLI WAIT1SF WAIT_OVERFLOW_TIMER, r7, FLAG_IN1 ; wait for rising edge of clock signal WAIT1SF WAIT_IN0, WAIT_NO_ACTION ; disable (and reset) counter & timer LDI 0, r0 ; disable counter & timer STS r0, SYSTEM_TIMER, SYSTEM_TIMER_CTRL_W
SPI 応答データグラム
以上で、受信データと計算データのすべてが使用可能になりました。次のステップとして、接続したマイクロコントローラ/モーション・コントローラによって読み出すことができるように、これらの値がSPIバッファにコピーされます。
最初の32ビットSPIデータグラムは、下位24ビットにコピーされたシングル・ターン位置情報と、MSBとしての固定値0x10で構成されます。SPIデータグラムの読出し時は、この固定MSB値を使ってこのデータグラムの内容を明確に識別できます。
; ST / Single turn value LDI %0001_0000, r0 ST SPI_BUFFER_3, r0 ; shift right MSB of ST 1x SHRI WAIT1SF NO_WAIT, FLAG_IN3, r4 LDI %0111_1111, r0 AND r4, r0, r4 ST SPI_BUFFER_2, r4 ST SPI_BUFFER_1, r3 ST SPI_BUFFER_0, r
2 番目の32ビットSPIデータグラムは、下位16ビットにコピーされたマルチ・ターン位置情報と、MSBとしての固定値0x20で構成されます。12ビット・マルチ・ターン位置のMSB値は、4ビット右にシフトされ、コピー動作の前のデータグラムのバイト境界に揃えられます。
; MT / Multiturn value LDI %0010_0000, r0 ST SPI_BUFFER_3, r0 LDI $0, r0 ST SPI_BUFFER_2, r0 ; shift right MSB of MT 4x SHRI WAIT1SF NO_WAIT, FLAG_IN3, r6 SHRI WAIT1SF NO_WAIT, FLAG_IN3, r6 SHRI WAIT1SF NO_WAIT, FLAG_IN3, r6 SHRI WAIT1SF NO_WAIT, FLAG_IN3, r6 LDI %0000_1111, r0 AND r6, r0, r6 ST SPI_BUFFER_1, r6 ST SPI_BUFFER_0, r5
3 番目および最後の32ビットSPIデータグラムは、エラー・フラグF1の値、内部CRCユニットで計算したCRC、データグラムのLSBにあるエンコーダから受信したCRCで構成されます。このデータグラムのMSBは、0x70の固定値に設定されています。
; CRC values + flags LDI %0111_0000, r0 ST SPI_BUFFER_3, r0 ; status flags SF ST SPI_BUFFER_2, r1 ; status flags ; load calculated CRC LDS SYSTEM_CRC, SYSTEM_CRC_RESULT0_R, r0 ST SPI_BUFFER_1, r7 ; received CRC LDI %0001_1111, r1 XOR r1, r0, r0 ST SPI_BUFFER_0, r0 ; calculated CRC
コードを実行し、次のSPIコマンドを実行するためにSPIコマンド・ループにジャンプして戻ります。
付録
アセンブラ
TMC8100 用サンプル/リファレンス・プログラムの開発および変更には、アセンブラを使用できます。PC ベースのコマンド・ライン・ツールには、パラメータとして、アセンブラ・ソース・コードのファイル名とパス(オプション)を入力する必要があります。入力されたファイル名のファイルが見つからない場合は、入力されたファイル名の末尾に自動的に「.asm」が追加されます。アセンブラは、この入力ファイルから出力ファイルを生成します。
この例では、アセンブリ・ソース・コードが保存された入力ファイルの名前は「tmc8100-eval_abn_demo.asm」です。このファイルから生成される機械語命令(16ビット)の数は83で、これは使用可能なプログラム・メモリ(SRAM)の4.1%に相当します。TMC8100のプログラム・メモリのサイズは2K × 16ビット(最大で2048個の命令)です。
生成されるデフォルトの出力ファイルは1つで、ファイル名は入力ファイルと同じですが、拡張子は「*.asm」ではなく「*.hex」になります。この出力ファイルはIntel標準16進ファイル・フォーマットのプログラム・コードが保存されたテキスト・ファイルであり、例えば、TMCL-IDEまたはサンプル・コードで使用できるPythonスクリプトの1つを使い、TMC8100-EVAL-KITにあるTMC8100のプログラム・メモリにプログラム・コードをロードします。
アセンブラは、異なるフォーマットと追加的な情報で構成されるその他の出力ファイルを生成するためのフラグも、いくつかサポートしています。
- オプション「-i」:「<filename>.i」というファイルがプリプロセッサからの出力として生成されます。このファイルはアッセンブリ・ソース・ファイルの内容で構成され、すべてのプリプロセッサ・コマンド(# ...)が処理されて(例えば「#include」ファイルの内容がソース・ファイルにマージされる)、すべてのコメント(例えば/* .. */ | // | ;)が削除されます。
- オプション「-l」:アッセンブリ・ソース・コード(コメントとプリプロセッサ・コマンドは含まれない)、追加の命令エンコーディング、およびそれらの命令のプログラム・メモリ・アドレスで構成される「<filename>.log」というファイルが生成されます。
- オプション「-c」:TMC8100 をブートストラップするコントローラ用プログラムの開発を支援するために、Cコードで構成される「<filename.c>」というファイルが生成されます。このファイルは、整数の配列と、生成された機械語コードで構成されます。
- オプション「-m」:機械語フラグ。様々な実装/命令セットをサポートします(デフォルトはTMC8100)。
- オプション「-h」:スクリーンショットに示すようなヘルプの表示テスト。
サポートされている構文/コマンド
TMC8100のデータシートに示すように、現在のところアセンブラはTMC8100のすべての命令をサポートしています。
プリプロセッサ・コマンド
コメント
プリプロセッサは、その後の処理のために入力ファイルからすべてのコメントを削除します。現在は以下のオプションがサポートされています。
- /* <comment> */ - ブロック・コメント - 複数の行を含めることができます(C言語型)
- // <comment> - 行の最後までのコメント(C/C++言語型)
- ; <comment> - 行の最後までのコメント
#include
#include "<filename>" — その後の処理のために、<filename>にファイル名とオプション・パスで示されたファイルの内容が、#include プリプロセッサ・ディレクティブの位置に挿入されます。#includeディレクティブがあった行は削除されます。
注:<filename>の周囲に記述できるのは引用符だけです。その他のコマンド/アサインを含めても、それらはその後の処理で削除/無視されるので含めることはできません。
#define
#define <label> [<replacement text>]
プリプロセッサは、ソース・ファイル(および#include ステートメントによってインクルードされたファイル)内にあるこのコマンド後の<label>を、<replacement text>に置き換えます。置き換えテキストとして使われるのは、<label>の後に置かれて 1 つ以上のスペースによって分離された、<label>からその行の最後までの文字シーケンスです。コメントと引用符内のテキストだけは自動置き換えから除外されます。ラベルはその後、別の#defineを同じ<label>と共に使ってソース・ファイル内で定義し直すことができます。
置き換え文字を指定せずに<label>を定義することも可能です。この場合、置き換え文字はありません。これは、例えば条件付きの#ifdefプリプロセッサ・コマンドや#ifndefプリプロセッサ・コマンドと組み合わせて使用すると便利です。
#ifdef, #ifndef, #else, #endif
#ifdef <label> <code block 1> #else <code block 2> #endif
それ以前に<label>が定義されている場合は<code block 1>の内容が解釈されてアセンブラ出力が生成され、<code block 2>は無視されます。中間ファイル(<filename>.i)には<code block 1>だけが表示され、<code block 2>は表示されません。コード・ブロックには複数行のアセンブラ命令などを含めることができます。<label>が定義されていない場合は、<code block 2>が解釈されます。
#ifndef <label> <code block 1> #else <code block 2> #endif
それ以前に<label>が定義されている場合は<code block 2>の内容が解釈されてアセンブラ出力が生成され、<code block 1>は無視されます。中間ファイル(<filename>.i)には<code block 2>だけが表示され、<code block 1>は表示されません。コード・ブロックには複数行のアセンブラ命令などを含めることができます。<label>が定義されていない場合は、<code block 1>が解釈されます。
注:#define <label>の使用前に<label>名を挙げるだけでも十分です。置き換え用のテキスト/値を示す必要はありません。
#else 部分はオプションです。#ifdefブロックまたは#ifndefブロックはネストできます。
アセンブラ・コマンド
数値
2 進数、10進数、16進数での数値指定を容易にするために、様々なフォーマットがサポートされています。これらを以下に示します。
「0x123..」または「$123..」は16進数として解釈されます(使用文字:0~9、a~fまたはA~F)。
「%1010..」は 2進数として解釈されます(使用文字:0および1)。可読性向上のために文字「_」を挿入できます(例えば8ビット数の場合は%1010_0011)。
「123..」は10進数として解釈されます(使用文字:0~9)。
識別子
可読性向上のため、数値に代えて識別子を使用できます。識別子は、文字(a~z/A~Z)または「_」から始める必要があります。その後は数値(0~9)を使用することも可能です。文字名の大文字と小文字は区別されません。
識別子には等号を使って値を割り当てることができます。例えば、
SPI_BUFFER_0 = $30
識別子に使うべきではない名前も以下のようにいくつかあります。
アセンブラ命令。これには、TMC8100 のデータシートにリストされたすべての名前と、「C」(コマンドの条件付き実行を示す)から始まる命令名が含まれます。
識別子r0~r7は、使用可能なすべての汎用レジスタを指定するためにレジスタ0~7として予め定義されています。
ラベルと識別子を同じ名前にすることはできません。
ラベル
ラベルは、プログラム・メモリ・アドレスのプレースホルダとして使われます。ラベルに値を割り当てる必要はありません。ラベルは、アセンブラがアッセンブリ・ソース・コードを機械語コードに変換する際に最新のプログラム・メモリ・アドレスで初期化されます。
エンドレス・ループの例:
WAIT:
JA WAIT
ラベル名の後の「:」文字に注意してください。アセンブラは、次の命令(この場合は JA WAIT コマンド)のプログラム・メモリ・アドレスで、ラベル WAIT を自動的に初期化します。実際に命令の一部として使われる前にラベルが初期化されるジャンプ・バック(上の例)と、ジャンプ・フォワード(命令によって参照された後にラベルが初期化される)の両方がサポートされています。



















