AN-2614: SSI アブソリュート・エンコーダ・プロトコルのTMC8100 向けサポート

説明

TMC8100 は、最大16Mbit/s のシリアル同期/非同期アブソリュート・エンコーダ・プロトコル用に最適化された、プログラマブル・マイクロコントローラを内蔵しています。このデバイスは、専用のプロトコル・インターフェースIC やフィールド・プログラマブル・ゲート・アレイ/FPGA 実装に代えて使用できる一方で、様々なエンコーダ機能のシステム内アップデートや、他のエンコーダ・プロトコルへの切り替えもサポートしています。TMC8100 は小型でコスト効果に優れ、なおかつ柔軟な通信ソリューションであり、工業用ドライブにアブソリュート・エンコーダのサポート機能を追加します。

このアプリケーション・ノートでは、同期シリアル・インターフェース(SSI)を備えたアブソリュート・エンコーダをサポートするTMC8100 ソフトウェアのリファレンス実装の詳細を示します。

特長

  • SSI の実装
  • 例では1MHz のクロック周波数を使用
  • グレイ・コードからバイナリ・コードへの変換
  • 入力フィルタ
  • 最大16MHz のSSI クロック周波数が可能

アプリケーション

  • モータの位置フィードバック
図1. TMC8100-EVAL-KIT とSSI アブソリュート・ポジション・エンコーダの接続
図1. TMC8100-EVAL-KIT とSSI アブソリュート・ポジション・エンコーダの接続

システムの説明

例えば工業用アプリケーション用サーボ・モータ・ドライブには、正確で信頼性が高く、しかも低レイテンシの位置フィードバックが必要です。これまで長期にわたり、インクリメンタルA/B/N 出力の光エンコーダが業界標準として使われてきました。その一方でアブソリュート・ポジション・エンコーダも多用されるようになっていますが、その多くは機能を追加したり、異なるインターフェース・プロトコル(ほとんどがベンダー固有)を使ったりしています。その一例が、エンコーダとコントローラの間でデジタル・データの同期シリアル転送に使われるSSI プロトコルです。このプロトコルは2 つの独立した接続を使用します。1 つはコントローラによって生成されたクロック用で、もう1 つはエンコーダからシフト・アウトされるエンコーダ・データ用です。物理層はRS422 規格に基づいており、位置情報と診断情報をエンコーダからコントローラへ送信できます。

この例で使用するSSI 付きエンコーダは、6 本のワイヤで構成される接続ケーブル1 本でTMC8100-EVAL-KIT に接続します(図1)。6 本のワイヤの内訳は次のとおりです。

  • +24V とGND:エンコーダの電源とグラウンド接続
  • DATA+とDATA−:エンコーダからコントローラへデータを送信するための差動RS-422 信号
  • CLK+とCLK−:コントローラからエンコーダへクロックを送信するための差動RS-422 信号

リファレンス実装は次のような機能を備えています。

  • 必要な周波数、パルス数、および極性のクロック信号の生成
  • SSI エンコーダと同じ1Mbps のデータ・レートをサポート
  • データのパッキングとパッキング解除
  • 16 ビットのエンコーダ位置データをグレイ・コードから16 ビット・バイナリ・コードに変換

リファレンス実装はソース・コードで提供されます。ユーザはこれを出発点として使用し、アプリケーションの必要に応じて変更を加えることができます。

システムの概要

提供されるソフトウェアは、TMC8100-EVAL-KIT と共に使用するように設計されており、SSI を備えたRi360P0-QR14-ESG25X2 エンコーダ(Turck)を使ってテストされています。

図2. コア・ハードウェア・コンポーネントと接続
図2. コア・ハードウェア・コンポーネントと接続

TMC8100-EVAL-KIT に必要なコア・ハードウェア・コンポーネントはTMC8100 と2 個のRS485 トランシーバです。RS485 トランシーバは、DATA+/DATA−信号およびCLK+/CLK−信号としてコネクタに送られてくる差動RS485 信号とTMC8100 の信号間の変換に使用します(図2)。

ソフトウェアには、SSI プロトコルをサポートするためのコントローラ機能を実装したTMC8100 のファームウェアが含まれています。このファームウェアは、パワーアップ後にTMC8100 にダウンロードする必要があります。ファームウェアの選択とダウンロード、およびその後にエンコーダと共にファームウェア機能のデモとテストを行うための追加GUI は、Python スクリプトとして提供されます。

同期シリアル・インターフェース・プロトコル

SSI はコントローラによって生成されるクロック信号を使用し、エンコーダが位置データや追加的情報(例えばステータスやエラー)をシフト・アウトするためのトリガ/リファレンスとして使われます。アイドル状態では両方(コントローラからエンコーダへのクロック信号とエンコーダからコントローラへ戻るデータ信号)ともハイ・レベル/ロジック「1」になります。

図3. SSI データグラム:最上位ビット/MSB から最下位ビット/LSB への順番によるエンコーダ・データのシリアル送信
図3. SSI データグラム:最上位ビット/MSB から最下位ビット/LSB への順番によるエンコーダ・データのシリアル送信

クロック信号の最初の立下がりエッジで、エンコーダ内の出力シフト・レジスタにエンコーダ・データが転送されます。クロック信号のそれ以後の立上がりエッジにおいて、MSB ファースト/LSB ラストでこのデータがシフト・アウトされます。この例では、25 ビットのデータが転送されます。上位2 ビット(23、22)は診断ビットで、下位16 ビットはグレイ・コードでコード化されたエンコーダ位置情報です(表1)。データ・ラインはデータの転送後に一定時間ロー・レベルに維持され、その後にハイ・レベル/アイドル状態に戻ります。更にその後、クロック・ラインの立下がりレベルで次のエンコーダ位置の読出しを開始できます。

表1. SSI データグラムのビット構成
データ・ビット 説明
Bit 24 (MSB)) 転送される最初のビット - 使用しません
Bit 23 ポジショニング・エレメントが測定範囲外
Bit 22 ポジショニング・エレメントが測定範囲内、低信号品質(例えば距離が大きすぎる)
Bit 21 .. Bit 16 使用しません
Bit 15 .. Bit 0 エンコーダの位置情報(グレイ・コード)

LSB の送信後にデータ・ラインがまだローの状態で次のSSI データグラムを先に開始し、クロックの立下がりエッジで同じエンコーダ位置を2 回読み出すことができます。この例ではデータグラムにチェックサムが含まれていないので、これはエンコーダ・データの完全性をチェックするのに有効です。

データ・ビットの数は25 個ですが、クロック・サイクル数は実際には26 個で、データ・ビットより1 個多くなっています。これは、最初のデータ・ビットがクロックの最初の立下がりエッジではなく立上がりエッジでシフト・アウトされ、最後のデータ・ビットが最後の立上がりエッジではなくその1 つ前の立上がりエッジでシフト・アウトされるからです。

SSI プロトコルをサポートしているエンコーダとメーカーは数多くあり、多くの場合、そのデータグラム・ビットの数、エンコーダ位置情報に使用するビットの数、データのエンコード方法(例えばグレイ・コードではなくバイナリ・コード)、追加的な情報(例えばステータス/エラー・ビット、マルチターン・データ)、およびチェックサム(例えばパリティ/CRC)がそれぞれ異なります。具体的なプロトコル実装の詳細については、エンコーダ・メーカーが提供するドキュメントを参照してください。

ソフトウェアの概要

TMC8100 用SSIプロトコルのファームウェア実装は、ソース・コード「tmc8100-eval_ssi_encoder_demo_v10.asm」と、Intel 16 進ファイル・フォーマットの機械語コード「tmc8100-eval_ssi_encoder_demo_v10.hex」で提供されます。機能の最初のテスト/評価用には、「tmc8100-eval_ssi_encoder_demo_v10.py」というPython スクリプトを使用できます。これは、図1 に示す要領でエンコーダを取り付けたTMC8100-EVAL-KIT にUSB を介して接続されたPC 上で実行できます。

Python スクリプト・プログラムを実行するには、まずPython インタープリタをPC にインストールして、「intelhex」および「pySerial」というPython ライブラリを使用する必要があります。このスクリプトは、グラフィカル・ユーザ・インターフェース用の「tkinter」を使用します。

TMC8100-EVAL-KIT にエンコーダを接続してUSB でPC に接続し、TMC8100-EVAL-KIT に+5V を加えると、以下のようにコマンドラインからPython スクリプトを実行できます。

command line 1

最初に、TMC8100-EVAL-KIT へのUSB 接続に使用する仮想COM ポートを選ぶ必要があります。この例では「COM4」です。ターミナル・ウィンドウに表示された出力行には、既にLandungsbruecke(LB)への接続に成功してTMC8100 を検出したこと、およびそのチップID とリビジョン番号が示されます。

その後、別のウィンドウでGUI が自動的に起動します。

gui 1

まず、[Select Input File]フレームの「…」ボタン(1)を使い、TMC8100 用のサンプル・コードが格納された16 進ファイル「tmc8100-eval_ssi_encoder_demo_v10.hex」を選択します。次のステップとして「Load + Execute」ボタン(2)を押すと、ブートローダによりファイルの設定内容がUSB とLandungsbruecke/LB を介してTMC8100 のSRAM プログラム・メモリに書き込まれて、プログラムが実行されます。このプログラムは、固有の通信プロトコルを使用してエンコーダにアクセスします。例えば異なるプログラムをダウンロードするためにTMC8100 を再びブートローダ・モードにするには、リセットまたは電源サイクルを行う必要があります。

gui 2

ウィンドウの中段にある[SSI]フレームには、エンコーダの読出しを開始するためのコマンド・ボタンが2 つあります。[Read Encoder]ボタンを押すと、SSI を通じてそれぞれの数のクロック・サイクルを送信して1 回転内の絶対位置をリード・バックするよう求める命令が、TMC8100 のファームウェアに送られます(「ST」ラベルの隣に表示)。ここではこれが、グレイ・コード・フォーマット[ST (raw /gray code / 16bit)]と、変換後の16 ビット値[ST (converted / binary / 16bit)]で表示されます。変換はTMC8100 のファームウェア内で行われ、オリジナルのグレイ・コード数値が送信されて、参考用としてここに表示されます。

command line 2

Read Encoder continuously]ボタンを押すと、Python プログラムによる一定レートでのエンコーダ値読出しがトリガされます。位置を示すデジタル数値に加えて、赤い位置マークの付いたアナログ・ダイヤルホイールも、最新の360º 絶対角度位置を示すエンコーダ値で更新されます。連続エンコーダ読出しを停止するには、Python プログラムの実行を終了するか、再起動します。

図4. ファームウェア実装テスト用Python のGUI
図4. ファームウェア実装テスト用Python のGUI

抽出/関連データが表示されるGUI と並行して、コマンド・ライン・ウィンドウには未加工通信データといくつかの追加情報が表示されます。これらのデータと情報は、TMC8100 のサンプル・プログラムを変更/拡張する際に役立ちます。

command line 3

性能上の理由から、連続エンコーダ読出し中はコマンド・ライン・ウィンドウへの出力が省略されます。

ファームウェア実装

サンプル・ソース・コード「tmc8100-eval_ssi_encoder_demo_v10.asm」は出発点として使用できるほか、アプリケーションの要求に応じて変更を加えることができます。アセンブラ(Assembler)は、ソース・コードの変換に使用できます。

サンプル・コードの概要をフローチャート(図5)に示します。

ファームウェアのソース・コードは、可読性向上のために、いくつかの値(例えばソフトウェア・バージョンとサポートされているプロトコル)の定義と、TMC8100 内にある周辺装置のレジスタ・アドレスの定義から始まります。

    SOFTWARE_VERSION_MAJOR = $01
    SOFTWARE_VERSION_MINOR = $00
    PROTOCOL_3 = $53 ; "S"
    PROTOCOL_2 = $53 ; "S"
    PROTOCOL_1 = $49 ; "I"
    PROTOCOL_0 = $20 ; " "
    ; system register
    SYSTEM_CORE = $0
    SYSTEM_TIMER = $1
    SYSTEM_CRC = $2
図5. ファームウェア実装の概要
図5. ファームウェア実装の概要

SPI の設定

TMC8100 で使用できる標準SPI 信号(SPI_CSN、SPI_SCLK、SPI_SDI、SPI_SDO)は設定不要です。これらの機能とパッケージのピン割り当ては固定されていますが、追加的な信号SPI_DATA_AVAILABLE を使用することができます。この追加信号はGPIO(6)の代わりに設定して、TMC8100 内のファームウェアがSPI 出力バッファ(ハイ「1」を出力)に書き込むデータを表示することができます。これにより、接続したマイクロコントローラへフィードバックを行い、更にマイクロコントローラがSPI データグラム/トランザクションを開始して、それ以前にTMC8100 から送信されたコマンドへの応答データをフェッチすることができます。この機能は起動後にブートローダによって既に設定されていますが、ここには確実を期すために示しました。

    LD GPIO0_ALT1_FUNCTION, r0
    LD GPIO_OUT_ENABLE, r1
    SET $4, r0, r0 ; GPIO_ALT1_FUNCTION(5 downto 4) = "01" -> connect spi_data_available to GPIO(6)
    CLR $5, r0, r0
    ST GPIO0_ALT1_FUNCTION, r0
    SET $6, r1, r1 ; GPIO(6) / SPI_DATA_AVAILABLE -> output
    ST GPIO_OUT_ENABLE, r1

クロックの選択と初期化

TMC8100 は常に内部発振器で起動し、パワーオン/リセット後にブートローダが75MHz のシステム・クロック周波数でPLL を設定します。この例では、TMC8100-EVAL-KIT に搭載された16MHz の水晶を使用する発振器が使われています。PLL 出力とシステム周波数は、その後のクロック計算/分周設定を容易にするために、100MHz に設定されています。通信用のリファレンス・クロックはTMC8100 自体に内蔵されているので、SSI 使用時に必ずしも水晶クロックが必要なわけではありません。したがって、この場合は、PLL と共にTMC8100 の内部クロックを使用することも1 つの選択肢です。

最初のステップとして、GPIO0 とGPIO1 を内部水晶発振器との併用で外部水晶用に設定します。

    LDI $03, r0 ; enable input for GPIO0/GPIO1
    ST GPIO_IN, r0
    
    LDI $03, r0 ; disable pull-up for GPIO0/GPIO1
    ST GPIO_PU, r0

次のステップでは、100MHz のPLL 出力周波数(PLL_FB_100)と水晶発振器(XTAL)クロック回路に合わせてPLL フィードバック分周器が設定され、PLL 入力におけるクロック周波数が1MHz となるようにPLL 入力分周器が設定されます。クロック・ブロックのアドレスはレジスタごとに間接的に指定されるので、書込みアクセスには4 つのコマンドが必要です。まずロード命令(LDI)とストア命令(ST)のペアでレジスタ・アドレスを設定し、その後にもう1 つのLDI 命令とST 命令のペアで新しいレジスタ値を設定します。

    LDI PLL_FB_CFG, r0 ; set pll feedback divider
    ST CLK_ADDR, r0
    LDI PLL_FB_100, r0
    ST CLK_DOUT, r0 ; will trigger write access to clk register
     
    LDI CLK_CTRL_SOURCE, r0
    ST CLK_ADDR, r0
    LDI $26, r0 ; use XTAL
    ST CLK_DOUT, r0 ; will trigger write access to clk register
    
    LDI CLK_CTRL_OPT, r0 ; enable clk fsm
    ST CLK_ADDR, r0
    LDI $40, r0
    ST CLK_DOUT, r0 ; will trigger write access to clk register
    
    LDI CLK_CTRL_PLL_CFG, r0
    ST CLK_ADDR, r0
    LDI %1011_1101, r0 ; RDIV = 15 (assuming 16MHz external / XTAL clock)
        ; and select PLL output, start FSM (commit = 1)
    ST CLK_DOUT, r0 ; will trigger write access to clk register

最後の書込みアクセスは、すべての変更を適用するためにクロック・ブロックの内部ステート・マシンもトリガします。これには水晶発振器の起動とPLL ロックが含まれるので、クロック・ブロックのステータス・レジスタをチェックして、新しい100MHz システム・クロックを使用できるようになるまで待つ必要があります。したがって、クロック・ブロックの設定レジスタ(CLK_CTRL_PLL_CFG)のアドレスが選択され、ビット7(TEST1 $7、r0)がクリアされるまでプログラム・ループがこのレジスタを読み出してから、初めてその後のプログラム実行へ移行します。

    LDI CLK_CTRL_PLL_CFG, r0
    ST CLK_ADDR, r0
    NOP
    NOP
WAIT_FOR_PLL:
    LD CLK_DIN, r0
    NOP
    TEST1 $7, r0
    JC WAIT_FOR_PLL

DIRECT_IN/DIRECT_OUT ピンの設定

図2 に示すように、エンコーダとの通信にはTMC8100-EVAL-KIT 上にある両方のRS485 トランシーバが使われます。ブロック図の上側にあるRS485 トランシーバは、TMC8100 からエンコーダへのクロック信号の送信に使用します。このRS485 トランシーバの送信イネーブルは常時オンになっています。ここではDIRECT_OUT(0)をクロック出力に使用し(「ST DIRECT_ALT_FUNCITON, r0」コマンドで選択する、この出力の代替機能)、DIRECT_IN(0)はドライバ遅延を補償するためにクロック信号をRS485 トランシーバ出力からTMC8100へループバックするのに使用します。クロックのループバックは内部的に行うこともできるので、これはオプションであり、必ずしも必要なわけではありません。

ブロック図の下側にあるRS485 トランシーバは、エンコーダからデータを受信するために使用します。この場合、トランスミッタはディスエーブルされます。DIRECT_IN(1)はシリアル・データ入力に使用しますが、このアプリケーションではDIRECT_OUT(1)とDIRECT_OUT(3)は不要なので、固定レベルに設定されます(両方ともロー「0」)。

この状態ではクロック・ライン(CLK+)がハイ・レベル「1」なので、対応する出力のDIRECT_OUT(0)は反転されます。データとクロック・ループバックの入力用には、ハードウェアのフィルタがイネーブルされます。

    ; configure DIRECT_OUT(0) as inverted
    LDI $10, r0
    ST DIRECT_POLARITY, r0
    ; set DIRECT_OUT(1) = 0 -> always low
    SFCLR WAIT1SF NO_WAIT, 0, 1
    ; set DIRECT_OUT(3) = 0 -> always low
    SFCLR WAIT1SF NO_WAIT, 0, 3
    ; configure DIRECT_OUT(0) as clock output of the system timer and DIRECT_OUT(3) as output
    LDI $01, r0
    ST DIRECT_ALT_FUNCTION, r0
    ; enable input filter for DIRECT_IN
    LDI %0000_0001, r0
    STS r0, SYSTEM_CORE, SYSTEM_CORE_INPUT_FILTER_W

SPI コマンド・ループ

TMC8100 の設定後、プログラムはSPI を通じたコマンド受信をエンドレス・ループで待機します。すべての SPI トランザクションは32ビットのデータグラムで行われます。このサンプル・コードのすべてのコマンドは、1 つのデータグラム内に収まります。

単純化のために、コマンドの選択と実行用に上位8 ビット(MSB、SPI を通じて最初に受信)だけがテストされます。コマンド・ループはSPI ペリフェラル・ブロックのステータス・レジスタ(SPI_STATUS)読出しから始まり、ステータス・レジスタのビット0 が1 に変わるまで待機状態となります(WAIT1 $0、r0)。このビット0 の変化は、SPI データグラムが受信されてSPI 入力バッファ(SPI_BUFFER)に格納されたことを示します。

CMD_LOOP:
    ; wait for SPI command
    LDI SPI_STATUS, r0
    WAIT1 $0, r0
    LD SPI_BUFFER_3, r0
    ; read encoder position data
    LDI $80, r1
    COMP EQ r0, r1
    JC read_encoder_data
    ; get software version
    . . . .
JA CMD_LOOP

32 ビットSPI 入力バッファの内容の上位8 ビットは、$80 と比較されます。$80 は、フラグと位置の値を含むエンコーダ・データを読み出すためにこのサンプル・コードで定義されたSPI コマンドです。この比較が正常に終了すると、プログラムの実行はアドレス「read_encoder_data 」にジャンプします。次いで、このアドレスのプログラム・コード( 詳細は以下のイメージに記述)がDIRECT_OUT(0)を通じて予め定められた数のクロック・サイクルを送信し、DIRECT_IN(1)を通じてエンコーダから返される応答データを収集します。受信データは32 ビットのSPI データグラムにまとめられて、SPI 出力バッファに格納されます。同時に、SPI_BUFFER_FULL / GPIO6 がロー「0」からハイ「1」に変化して、SPI トランザクションの新しいデータが使用可能になったことを示します。次のSPI トランザクションでは、このデータを読み出すことができます。前のコマンドのすべてのデータが読み出される前に、新しいコマンドが送信されることはありません。1 つのSPI トランザクションは常に両方向にデータを転送します。通常はすべての応答データを読み出すのに複数のトランザクションが必要になるので、ダミー・コマンド、例えば0x00 0x00 0x00 0x00 を使用することが推奨されます。コマンド・ループはこのダミー・コマンドを解釈しません。読出しの最後のトランザクションには、次のコマンドを含めることができます。

TMC8100-EVAL-KIT によるエンコーダ実装「tmc8100-eval_ssi_encoder_demo.py」のテストに使用できるPython スクリプトは、Read Encoder プッシュ・ボタンを含むGUI を提供します(図4)。このボタンを押すと、SPI データグラム0x80 0x00 0x00 0x00 の送信を指示する命令がLandungsbruecke/LB に送られます(「tmc8100- eval_ssi_encoder_demo.py」から抜粋)。

def spi_get_encoder_value():
    # get encoder value command
    value = SpiWriteCommand([0x80, 0x00, 0x00, 0x00])
    WaitForGPIO6()
    . . . .

その後、プログラムはエンコーダからの応答を待ちます。SPI_DATA_AVAILABLE/GPIO6 ピンは、TMC8100 のSPI 出力バッファに応答データが格納されると、すぐにロー「0」からハイ「1」に切り替わります。このデータの読出し時、Python プログラムは、TMC8100 のファームウェアによって解釈されないSPI「ダミー」コマンド0x00 0x00 0x00 0x00 を使用します。

    # get ST value (raw / gray code value)
    value = SpiWriteCommand([0x00, 0x00, 0x00, 0x00])
    print(f"ST (raw): {value[0]:02x} {value[1]:02x} {value[2]:02x} {value[3]:02x}")
    . . . .
    # get ST value (binary value)
    value = SpiWriteCommand([0x00, 0x00, 0x00, 0x00])
    print(f"ST (binary): {value[0]:02x} {value[1]:02x} {value[2]:02x} {value[3]:02x}")
    . . . .
    # get error flags
    value = SpiWriteCommand([0x00, 0x00, 0x00, 0x00])
    print(f"ERR: {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 error flags and encoder absolute
position value within one rotation (ST)
  1. 0x10 0x00 ST (MSB) ST (LSB)
    (raw position value in gray code from encoder)
  2. 0x20 0x00 ST (MSB) ST (LSB)
    (converted binary position value)
  3. 0x70 Flags 0x00 0x00
0xff 0x00 0x00 0x00
get firmware version
  1. 0xff 0x00 VERSION_MAJOR VERSION_MINOR
0xfe 0x00 0x00 0x00
get encoder protocol
  1. 0x53 (“S”) 0x53 (“S”) 0x49 (“I”) 0x20 (“ “)

Read Encoder continuously]ボタンを押すと、Python プログラムが表の最初のコマンド0x80 0x00 0x00 0x00 を繰り返し送信します。

エンコーダ・データの読出し

TMC8100 ソフトウェアがSPI コマンドの受信を待っている状態でSPI コマンドが受信され、なおかつ上位バイト/32 ビット・データグラムから最初に受信したバイトが$80 に等しかった場合は、直ちにプログラムの実行がプログラム・コード内の「read_encoder_data」アドレスへジャンプして、クロック・パルスの準備と、DIRECT_OUT(0)および取り付けられたRS485 トランシーバを通じたエンコーダへの送信が開始されます。

    ; set timer to 100MHz / 50 = 2MHz clock toggle rate -> 1MHz SSI clock
    LDI 49, r0
    STS r0, SYSTEM_TIMER, SYSTEM_TIMER_COUNTER_LIMIT_W
    ; number of clock edges: 52 (25 x 2 + falling edge at the beginning and rising edge at the end)
    LDI 52, r0
    STS r0, SYSTEM_TIMER, SYSTEM_TIMER_PULS_COUNTER_LIMIT_W
    LDI 1, r0 ; enable counter
    STS r0, SYSTEM_TIMER, SYSTEM_TIMER_CTRL_W
    . . . .

クロック信号は、この例で使用するエンコーダの最大値である1MHz に設定されます。ステム・カウンタがオーバーフローすると、その都度クロック出力がトグルします。したがって、システム・カウンタは出力クロック周波数の2 倍、この場合は2MHz に設定されます。クロック・サイクルの数は、エンコーダからシフト・アウトされるビットの数(この場合は25)に開始時の立下がりエッジと終了時の立上がりエッジを加えた値と同じです。結果としてクロック・サイクルの立上がりエッジと立下がりエッジは52 個で、クロック・サイクル数は全部で26 になります。カウンタ・システムの最終ステップとして、タイマー初期化クロックの生成が開始されます。

次のステップでは、DIRECT_IN(1)入力でエンコーダからの応答データがキャプチャされます。クロック信号はリファレンスとして、およびある種の遅延補償用としてRS485 トランシーバを通じてループバックされます。使われるのは、DIRECT_IN(0)ピンへの入力です。注:ループバックを使用できない場合(例えばRS422 トランスミッタを使用する場合)は、内部クロックを直接使用できます。

ファームウェアは、クロック信号CLK+がハイ・レベル「1」からロー・レベル「0」に変化して送信開始を示すまで待機します(WAIT0SF WAIT_IN0, WAIT_NO_ACTION)。エンコーダからのシリアル・データはクロック信号の次の立上がりエッジでシフト・アウトされ、クロック信号の立下がりエッジでTMC8100 のファームウェアによりDIRECT_IN(1)入力でキャプチャされます。最初にキャプチャされるビットは、25 ビット・データグラムのMSB/ビット24 です(図6)。

図6. TMC8100 のエンコーダ・クロック出力(CLK+)/シリアル・データ受信入力(DATA+)
図6. TMC8100 のエンコーダ・クロック出力(CLK+)/シリアル・データ受信入力(DATA+)
    ; wait for clock signal low / start of transmission
    WAIT0SF WAIT_IN0, WAIT_NO_ACTION ; wait for CLK signal low
    
    ; shift in error flags on falling edge of clock
    REP 3, 2
    WAIT1SF WAIT_IN0, WAIT_NO_ACTION ; wait for CLK signal high
    SHLI WAIT0SF WAIT_IN0, r2, FLAG_IN1 ; shift DATA in MSB first on falling edge of clock signal
    
    ; shift in absolute multi turn counter value (not used)
    REP 6, 2
    WAIT1SF WAIT_IN0, WAIT_NO_ACTION ; wait for CLK signal high
    SHLI WAIT0SF WAIT_IN0, r7, FLAG_IN1 ; shift DATA in MSB first
    
    ; shift in single turn counter value - MSB
    REP 8, 2
    WAIT1SF WAIT_IN0, WAIT_NO_ACTION ; wait for CLK signal high
    SHLI WAIT0SF WAIT_IN0, r3, FLAG_IN1 ; shift DATA in MSB first
    OR r3, r3, r5 ; copy original value to r5
    
    ; shift in single turn counter value - LSB
    REP 8, 2
    WAIT1SF WAIT_IN0, WAIT_NO_ACTION ; wait for CLK signal high
    SHLI WAIT0SF WAIT_IN0, r4, FLAG_IN1 ; shift DATA in MSB first
    OR r4, r4, r6 ; copy original value to r6

最初に受信される3 ビットは、「REP 3, 2」の後の2 つの命令が1 行につき3 回繰り返されることにより、クロック信号のその後の立下がりエッジでレジスタr2 にシフト・アウトされます。2 つの命令のうち、最初の命令は、常にクロック信号がハイ「1」になるまで待機し(WAIT1SF …)、2 番目の命令は、クロックの立下がりエッジでシリアル・データを、MSB ファーストで左から右へレジスタr2 にシフト・インします(SHLI ..r2..)。

「REP 6, 2」で始まる次の3 つの命令は6 個のビットをレジスタr7 へシフト・インしますが、この例では使用しません。他のSSI エンコーダには、マルチターン・カウンタ情報を含むものもあります。

最後に、「REP 8, 2」から始まり、3 つの命令で構成される2 つのブロックがあります。これらは、クロック信号の立下がりエッジで、エンコーダ・カウンタの値をレジスタr3(MSB)とレジスタr4(LSB)にシフト・インします。この受信データは、グレイ・コードでエンコーダから送られてくるオリジナル・データを保存するために、レジスタr5(MSB)とr6(LSB)にもコピーされます。

グレイ・コードからバイナリ・コードへの変換

次のステップでは、r3 とr4 のエンコーダ・カウンタ値がグレイ・コードからバイナリ・コードに変換されます。変換はr3 のMSB から開始してビットごとにインプレースで行われ、その結果でレジスタr3 とr4 の内容が上書きされます。このビットごとの変換は図7 に示すアルゴリズムを使用します。このアルゴリズムは高い柔軟性を備えており、ビット長が異なる場合でも容易に調整できます。

図7. グレイ・コードからバイナリ・コードへの変換アルゴリズム
図7. グレイ・コードからバイナリ・コードへの変換アルゴリズム

1 ビットを変換するためのソース・コードの例:

    TEST1 7, r3
    MOVF 0, r0
    TEST1 6, r3
    MOVF 0, r1
    XOR r0, r1, r0
    TEST1 0, r0
    MOVF 6, r3
    . . .

r3 のMSB がテストされ(TEST1 7, r3)、そのビットの値に従ってプロセッサ・フラグがセットされます。フラグはその後、補助レジスタr0 のビット0 にコピーされます(MOVF 0, r0)。更に次の2 つの命令により、同じ要領でレジスタr3 のビット6 がレジスタr1 のビット0にコピーされます。次いで、r0 とr1 に対してビットごとにXOR 演算が行われて、その結果が再びr0 に書き込まれます。更にr0 のビット0 の値が再度システム・フラグに書き込まれて(TEST1 0, r0)、最後にレジスタr3 のビット6 にコピーされます。これらのステップがr3の残りのビットにも繰り返されて、その後r4 に実行が移ります。

SPI 応答データグラム

以上で、受信したすべてのデータがシステム・レジスタr2~r6 に格納されます。次のステップとして、接続したマイクロコントローラ/モーション・コントローラによって読み出すことができるように、これらの値がSPI バッファにコピーされます。

    ; copy single turn 16bit raw value from encoder to SPI transmit buffer
    LDI %0001_0000, r0 ; ST value
    ST SPI_BUFFER_3, r0 ; 1 -> ST (raw)
    LDI $0, r0
    ST SPI_BUFFER_2, r0
    ST SPI_BUFFER_1, r5 ; ST - MSB
    ST SPI_BUFFER_0, r6 ; ST – LSB

最初の32 ビットSPI データグラムは、下位16 ビットにコピーされたグレイ・コード(エンコーダから受信した値)の絶対位置情報と、MSB としての固定値0x10 で構成されます。SPI データグラムの読出し時は、この固定MSB 値を使ってこのデータグラムの内容を明確に識別できます。

    LDI %0010_0000, r0 ;
    ST SPI_BUFFER_3, r0 ; 2 -> ST (binary)
    LDI $0, r0
    ST SPI_BUFFER_2, r0
    ST SPI_BUFFER_1, r3
    ST SPI_BUFFER_0, r4

2 番目の32 ビットSPI データグラムは、バイナリ・コードに変換された下位16 ビットの絶対位置情報と、MSB としての固定値0x20 で構成されます。

    LDI %0111_0000, r0 ; flag value
    ST SPI_BUFFER_3, r0
    ST SPI_BUFFER_2, r2 ; flags
    LDI $0, r0
    ST SPI_BUFFER_1, r0 ; no CRC
    ST SPI_BUFFER_0, r0 ; no CRC

3 番目となる最後の32 ビットSPI データグラムは、エンコーダから受信したフラグと、MSB としての固定値0x70 で構成されます。

付録

アセンブラ

TMC8100 用サンプル/リファレンス・プログラムの開発および変更には、アセンブラを使用できます。PC ベースのコマンド・ライン・ツールには、パラメータとして、アセンブラ・ソース・コードのファイル名とパス(オプション)を入力する必要があります。入力されたファイル名のファイルが見つからない場合は、入力されたファイル名の末尾に自動的に「.asm」が追加されます。アセンブラは、この入力ファイルから出力ファイルを生成します。

command line 4

この例では、アセンブリ・ソース・コードが保存された入力ファイルの名前は「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 のプログラム・メモリにプログラム・コードをロードします。

アセンブラは、異なるフォーマットと追加的な情報で構成されるその他の出力ファイルを生成するためのフラグも、いくつかサポートしています。

command line 4
  • オプション「-i」:「<filename>.i」というファイルがプリプロセッサからの出力として生成されます。このファイルはアッセンブリ・ソース・ファイルの内容で構成され、すべてのプリプロセッサ・コマンド(# ...)が処理されて(例えば「#include」ファイルの内容がソース・ファイルにマージされる)、すべてのコメント(例えば/* .. */ | // | ;)が削除されます。
  • オプション「-l」:アッセンブリ・ソース・コード(コメントとプリプロセッサ・コマンドは含まれない)、追加の命令エンコーディング、およびそれらの命令のプログラム・メモリ・アドレスで構成される「<filename>.log」というファイルが生成されます。
  • オプション「-c」:TMC8100 をブートストラップするコントローラ用プログラムの開発を支援するために、C コードで構成される「<filename.c>」というファイルが生成されます。このファイルは、整数の配列と、生成された機械語コードで構成されます。
  • オプション「-rom」:3 つの追加ファイルが生成されます。機械語命令が16 進/2 進数値として1 行につき1 つずつリストされた「pelican_bootloader.hex」と「pelican_bootloader.bin」、およびvhdl エンティティ宣言の一部として機械語命令を論理コードの形で示した「pelican_bootloader.vhd」です。
  • オプション「-m」:機械語フラグ。様々な実装/命令セットをサポートします(TM02 は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>が定義されていない場合は、もう1 つのコード・ブロックが解釈されます。

#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>が定義されていない場合は、もう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 を自動的に初期化します。実際に命令の一部として使われる前にラベルが初期化されるジャンプ・バック(上の例)と、命令によって参照された後にラベルが初期化されるジャンプ・フォワードの両方がサポートされています。