SPIアブソリュートエンコーダ用Arduinoサンプルコード

著者 Damon Tarry, Design Applications Engineer, Same Sky

このArduinoサンプルコードチュートリアルは、Same SkyAMT22アブソリュートエンコーダの構成とデータの読み取りをシリアルペリフェラルインターフェース(SPI)通信で行うための確かな出発点をユーザーに提供することを目的としています。このチュートリアルでは、必要なハードウェアとソフトウェア、主なセットアップ要件、シングルターンとマルチターンの出力オプションのサンプルコードパッケージと説明を提供します。開始に必要なもののリストは以下の通りです。

AMT22アブソリュートエンコーダの概要

Same Sky(前CUI Devices)のAMT22は、12ビットまたは14ビットの分解能で提供されるアブソリュートエンコーダです。1回転あたりの正確な位置数を提供します。12ビットバージョンの場合、これは4,096ポジションに相当し、14ビットモデルの場合は1回転あたり16,384ポジションとなります。デバイスが何度回転しても、その絶対位置を継続的に報告するため、デバイスの正確な角度を正確にフィードバックすることができます。

このエンコーダには、シングルターンモデルとマルチターンモデルがあります。シングルターンバージョンは360度の1回転内の位置を測定し、マルチターンバージョンは1回転内の位置だけでなく、完全な回転の総数も追跡します。さらに、シングルターンバージョンはプログラマブルゼロ点を備えており、ユーザーはエンコーダの原点に対するカスタムリファレンスを定義することができます。

はじめに

エンコーダの背面にあるスイッチを適切な位置に調整し、デバイスがRUNモードになっていることを確認します(図1)。AMT22エンコーダをモータまたはアセンブリに正しく取り付けるには、AMTの取り付け手順を使用してください。AMT22は、2mmから8mmまでの9種類のシャフトサイズをサポートしています。

図:RUNモードに切り替えたSame Sky AMT22エンコーダ図1:AMT22エンコーダ背面のスイッチをRUNモードにします。(画像提供:Same Sky)

図2と表1に示した接続はArduino Unoボード用のものですが、提供されたコードはほとんどのArduinoボードに対応しているはずです。ただし、Arduinoのモデルによってピン構成が異なる場合があることに注意してください。他のボードの正確な接続の詳細については、対応するArduinoのドキュメントを参照することを推奨します。

図:AMT22エンコーダとArduino Unoの配線接続図2:AMT22エンコーダとArduino Unoの配線接続。(画像提供:Same Sky)

機能 エンコーダピン番号 Arduino Unoピン AMT-DBC-1-036
+5V 1 5V 白/緑
SCLK 2 13 青/白
MOSI 3 11 白/青
GND 4 GND 緑/白
MISO 5 12 橙/白
CS 6 2 白/橙

表1:Arduino Unoの配線接続の詳細。(画像提供:Same Sky)

AMT22エンコーダは、SPI通信が開始されると直ちに絶対位置データの送信を開始するため、従来のコマンド応答構造が不要になります。SPI転送の最初のバイトで、ホストは0x00を送信し、AMT22は有効な位置データで同時に応答します。

ホストがゼロ設定コマンドのようなコマンド(表2)を発行する必要がある場合、そのコマンドは送信の2バイト目で送信されます。これは拡張コマンドと呼ばれます。詳細な技術仕様については、AMT22のデータシートを参照してください。

コマンド バイト 備考
Get Position 0x00 0x00
Set Zero 0x00 0x70 シングルターンのみ
Get Turns 0x00 0xA0 マルチターンのみ

表2:AMT22コマンドの定義。(画像提供:Same Sky)

コードチュートリアル - インクルードと定義

AMT22エンコーダとのインターフェースにArduinoのSPIバスを使用するため、SPIライブラリをコードに含める必要があります。Arduinoからコンピュータに位置データを送信するには、Arduino IDEに内蔵されたUSBシリアル接続を利用し、ボーレート115200で構成します。

さらに、AMT22が使用するコマンドを定義する必要があります。エンコーダは1バイト目の内容を処理しないので、通信処理を簡略化するためにNOP(no-operation)が割り当てられています(リスト1)。

コピー
/* Include the SPI library for the arduino boards */
#include <SPI.h>
 
/* Serial rates for UART */
#define BAUDRATE      115200
 
/* SPI commands */
#define AMT22_NOP     0x00
#define AMT22_ZERO    0x70
#define AMT22_TURNS   0xA0

リスト1:SPIインターフェースのセットアップ。

初期化

setup()関数(リスト2)で、必要なSPIピンをすべて初期化し、通信用のシリアルインターフェースを構成することから始めます。

シリアルポートは、ホストコンピュータへのデータ伝送を可能にするために初期化されなければなりません。これは、定義されたBAUDRATEをSerial.begin()関数に渡すことによって行われます。

SPIを有効にする前に、チップセレクト(CS)ラインが適切な状態に設定され、エンコーダが通信できる状態になっていることを確認してください。

AMT22と通信するSPIバスのクロックレートを選択します。AMT22は最大2MHzのクロックレートをサポートしますが、プロトタイピング目的では500kHzのクロックレートが適しています。500kHzを達成するには、SPI_CLOCK_DIV32設定を使用します。Arduino Unoの16MHzクロックを考えると、この分割は500kHzのSPIクロックレートになります。SPIクロック構成の詳細については、Arduinoのドキュメントを参照してください。

すべてを構成した後、SPI.begin()を使用してSPIバスを初期化します。これにより、3つの専用SPIピン(MISO、MOSI、SCLK)がセットアップされ、システムでエンコーダとの通信の準備が整います。

コピー
void setup()
{
  uint8_t cs_pin = 2;
 
  //Set the modes for the SPI CS
  pinMode(cs_pin, OUTPUT);
  //Get the CS line high which is the default inactive state
  digitalWrite(cs_pin, HIGH);
 
  //Initialize the UART serial connection for debugging
  Serial.begin(BAUDRATE);
 
  //set the clockrate. Uno clock rate is 16Mhz, divider of 32 gives 500 kHz.
  //500 kHz is a good speed for our test environment
  //SPI.setClockDivider(SPI_CLOCK_DIV2);   // 8 MHz
  //SPI.setClockDivider(SPI_CLOCK_DIV4);   // 4 MHz
  //SPI.setClockDivider(SPI_CLOCK_DIV8);   // 2 MHz
  //SPI.setClockDivider(SPI_CLOCK_DIV16);  // 1 MHz
  SPI.setClockDivider(SPI_CLOCK_DIV32);    // 500 kHz
  //SPI.setClockDivider(SPI_CLOCK_DIV64);  // 250 kHz
  //SPI.setClockDivider(SPI_CLOCK_DIV128); // 125 kHz
 
  //start SPI bus
  SPI.begin();
}

リスト2:すべてのSPIピンを初期化するsetup()関数。

SPI通信

AMT22とのSPI通信はArduinoのSPIライブラリで処理され、チップセレクト(CS)制御はデジタルI/Oピンを使用してコードで管理されます。digitalWrite()関数は、CSラインをアサートまたはデアサートするために使用されます(リスト3)。

AMT22は2バイトの0x00が送信されることを予期しており、このバイトを受信すると直ちにデータを返します。この高速応答のため、特定の最小タイミング要件に従う必要があり、その概要はAMT22のデータシートに記載されています。

エンコーダが12ビットバージョンか14ビットバージョンかに関わらず、常に2バイト(16ビット)のデータで応答します。上位2ビットはチェックビットで、データの完全性を検証するために使用されます。12ビットバージョンの場合、下位2ビットはともに0であり、適切に使用するためには、返される値を2ビット右にシフトする(または4で割る)必要があります。

位置データを取得するために、SPI.transfer()関数が呼び出され、AMT22_NOPコマンドが送信されます。このプロセス中はCSラインはローのままです。AMT22は上位バイトを最初に送信するため、受信したバイトは8ビット左シフトされ、uint16_t変数の上半分に整列されます。この値は、1つの操作でencoderPosition変数に割り当てられます。タイミング要件を満たすために短時間遅延した後、2回目のSPI.transfer()呼び出しが実行され、別のAMT22_NOPコマンドが送信されます。その結果は、encoderPositionの現在値とOR演算され、受信した2バイトを1つのuint16_t変数にまとめます。最後にCSラインがリリースされ、通信が完了します。

コピー
uint8_t cs_pin = 2;
 
//set the CS signal to low
digitalWrite(cs_pin, LOW);
delayMicroseconds(3);
 
//read the two bytes for position from the encoder, starting with the high byte
uint16_t encoderPosition = SPI.transfer(AMT22_NOP) << 8; //shift up 8 bits because this is the high byte
delayMicroseconds(3);
encoderPosition |= SPI.transfer(AMT22_NOP); //we do not need a specific command to get the encoder position, just no-op
 
//set the CS signal to high
digitalWrite(cs_pin, HIGH);

リスト3:SPI通信のセットアップ。

チェックサム検証

SPI転送が完了したら、チェックサムを用いて受信データを検証することが不可欠です(リスト4)。

この検証を実施するには、データシートに記載されている方程式に基づいて関数を作成します。チェックサムは受信値の上位2ビットに含まれ、位置応答の奇数ビットと偶数ビットにまたがる奇数パリティを利用します。

この関数は以下のステップを実行します。

  1. 奇数ビット(ビット1、3、5、7、9、11、13)のパリティを計算します。
  2. 偶数ビット(ビット0、2、4、6、8、10、12、14)のパリティを計算します。
  3. 計算されたパリティをチェックサムビットが示す値と比較します。

この関数は、チェックサムが有効であればtrueを返し、データの完全性が確認されたことを示します。チェックサムが無効な場合、この関数はfalseを返し、受信データにエラーがある可能性を示します。

コピー
/*
 * Using the equation on the datasheet we can calculate the checksums and then make sure they match what the encoder sent.
 */
bool verifyChecksumSPI(uint16_t message)
{
  //checksum is invert of XOR of bits, so start with 0b11, so things end up inverted
  uint16_t checksum = 0x3;
  for(int i = 0; i < 14; i += 2)
  {
    checksum ^= (message >> i) & 0x3;
  }
  return checksum == (message >> 14);
}

リスト4:チェックサム検証。

データフォーマット

チェックサム検証でデータの完全性が確認された場合、次のステップは、上位2ビットを削除してencoderPosition変数を更新することです(リスト5)。これは、0x3FFF(または0b0011111111111111)を使用してビットごとのAND演算を適用することで達成できます。この演算は、位置データの下位14ビットすべてを効果的に保持します。

さらに、エンコーダの分解能(12ビットか14ビットか)を考慮する必要があります。分解能が12ビットの場合、encoderPositionの値は、低い分解能を調整するために2ビット右にシフトされなければなりません。これにより、encoderPosition変数に、指定された分解能に基づくエンコーダの実際の位置が反映され、位置データが正確に表現されます。

コピー
if (verifyChecksumSPI(encoderPosition)) //position was good
{
  encoderPosition &= 0x3FFF; //discard upper two checksum bits
  if (RESOLUTION == 12) encoderPosition = encoderPosition >> 2; //on a 12-bit encoder, the lower two bits will always be zero
 
  Serial.print(encoderPosition, DEC); //print the position in decimal format
  Serial.write('\n');
}
else //position is bad
{
  Serial.print("Encoder position error.\n");
}

リスト5:encoderPositionの更新。

ゼロ位置の設定(シングルターンのみ)

AMT22エンコーダの特定のバージョンには、プログラマブルなゼロ位置機能があります。このゼロ位置を設定するには、特定の2バイトコマンドシーケンスを送信しなければなりません。このプロセスでは、まずAMT22_NOPコマンドを送信し、その後、AMT22が指定する最小タイミング要件を満たすために短時間待機します。この待機の後、チップセレクト(CS)ラインを確実にリリースしながらAMT22_ZEROコマンドを送信します。エンコーダがこのコマンドを受け取ると、リセット動作を実行します(リスト6)。

このリセット期間中のエンコーダとの通信を避けるため、250msの遅延が実装され、電源投入時にエンコーダにコマンドが送信されないようになっています。

動作開始時にエンコーダのゼロ位置をコードで設定することも可能ですが、一般的なアプリケーションでは、システム内で使用するデバイスの初期構成時にゼロ位置を一度だけ設定するのが一般的です。この方法は、エンコーダの動作寿命を通じて位置フィードバックの完全性を維持するのに役立ちます。

コピー
/*
 * The AMT22 bus allows for extended commands. The first byte is 0x00 like a normal position transfer,
 * but the second byte is the command.
 * This function takes the pin number of the desired device as an input
 */
void setZeroSPI(uint8_t cs_pin)
{
  //set CS to low
  digitalWrite(cs_pin, LOW);
  delayMicroseconds(3);
 
  //send the first byte of the command
  SPI.transfer(AMT22_NOP);
  delayMicroseconds(3);
 
  //send the second byte of the command
  SPI.transfer(AMT22_ZERO);
  delayMicroseconds(3);
 
  //set CS to high
  digitalWrite(cs_pin, HIGH);
 
  delay(250); //250 millisecond delay to allow the encoder to reset
}

リスト6:シングルターンAMT22エンコーダのゼロ位置の設定。

ターンカウンタの読み取り(マルチターンのみ)

AMT22エンコーダの特定のバージョンでは、マルチターンカウンタがサポートされており、ユーザーは1つのデータ取得シーケンスで位置とターンカウントの両方を読み取ることができます。

受信した位置データが無効な場合、システムはユーザーにエラーを通知しなければなりません。一方、位置が有効な場合、プログラムは位置を10進数形式で報告する必要があります(リスト7)。この機能により、絶対位置と完全な回転数の両方について包括的なフィードバックを提供することでエンコーダの機能が強化され、精密な回転データを必要とするアプリケーションにおいて、より正確な監視と制御が可能になります。

コピー
uint8_t cs_pin = 2;
 
//set the CS signal to low
digitalWrite(cs_pin, LOW);
delayMicroseconds(3);
 
//read the two bytes for position from the encoder, starting with the high byte
uint16_t encoderPosition = SPI.transfer(AMT22_NOP) << 8; //shift up 8 bits because this is the high byte
delayMicroseconds(3);
encoderPosition |= SPI.transfer(AMT22_TURNS); //we send the turns command (0xA0) here, to tell the encoder to send us the turns count after the position
 
//wait 40us before reading the turns counter
delayMicroseconds(40);
 
//read the two bytes for turns from the encoder, starting with the high byte
uint16_t encoderTurns = SPI.transfer(AMT22_NOP) << 8; //shift up 8 bits because this is the high byte
delayMicroseconds(3);
encoderTurns |= SPI.transfer(AMT22_NOP);
delayMicroseconds(3);
 
//set the CS signal to high
digitalWrite(cs_pin, HIGH);

リスト7:マルチターンAMT22エンコーダのencoderPositionとターンカウンタの読み取り。

コードの実行

コードの作成に成功したら、それをArduinoにアップロードして、AMT22エンコーダとの通信を確立しましょう。

出力をモニタするには、Arduino IDEでシリアルモニタを開き、データレートが115200ボーに設定されていることを確認します。これにより、ユーザーはエンコーダの動作を観察し、報告された位置データをリアルタイムで見ることができます。シリアルモニタがアクティブになると、エンコーダは位置情報の送信を開始し、システム内でその機能を発揮するはずです(図3)。

画像:エンコーダからの報告位置図3:Arduinoが受信した、エンコーダからの報告位置(画像提供:Same Sky)

複数のエンコーダ

SPIデバイスを使用することの大きな利点は、同じバス上の複数のエンコーダと通信できることです。これを実現するには、個々のチップセレクト(CS)制御用に各エンコーダに追加のデジタルI/Oピンを割り当てる必要があります。

コード例(リスト8)では、CSピンの配列を利用して、任意の数のエンコーダをサポートしています。この設計により、スケーラブルな通信が可能になり、ユーザーは必要に応じて簡単にエンコーダを追加することができます。目的のデバイスに対応するピン番号を受け入れるように関数を変更することで、コードはSPIバス上でどのエンコーダをアクティブにするかを動的に制御することができ、各デバイスが独立してアクセスおよび操作できるようになります。

コピー
uint8_t cs_pins[] = {2}; //only one encoder connected, using pin 2 on arduino for CS
//uint8_t cs_pins[] = {2, 3}; //two encoders connected, using pins 2 & 3 on arduino for CS

リスト8:複数のエンコーダを読み取るための配列の設定。

次のステップは、配列の各CSピンをループし、接続された各エンコーダから位置を読み取ることです。これにより、システムはチップセレクトラインをアサートして各エンコーダをアクティブにし、SPI転送を実行し、位置データを取得することができます。コードは各エンコーダを順次選択し、SPI通信を実行し、CSラインをリリースします。これにより、接続されたすべてのデバイスの位置情報が確実に返されます(リスト9)。

コピー
void loop()
{
  for(int encoder = 0; encoder < sizeof(cs_pins); ++encoder)
  {
    uint8_t cs_pin = cs_pins[encoder];
 
    //set the CS signal to low
    digitalWrite(cs_pin, LOW);
    delayMicroseconds(3);
 
    //read the two bytes for position from the encoder, starting with the high byte
    uint16_t encoderPosition = SPI.transfer(AMT22_NOP) << 8; //shift up 8 bits because this is the high byte
    delayMicroseconds(3);
    encoderPosition |= SPI.transfer(AMT22_NOP); //we do not need a specific command to get the encoder position, just no-op
 
    //set the CS signal to high
    digitalWrite(cs_pin, HIGH);
 
    if (verifyChecksumSPI(encoderPosition)) //position was good, print to serial stream
    {
      encoderPosition &= 0x3FFF; //discard upper two checksum bits
      if (RESOLUTION == 12) encoderPosition = encoderPosition >> 2; //on a 12-bit encoder, the lower two bits will always be zero
 
      Serial.print("Encoder #");
      Serial.print(encoder, DEC);
      Serial.print(" position: ");
      Serial.print(encoderPosition, DEC); //print the position in decimal format
      Serial.write('\n');
    }
    else //position is bad, let the user know how many times we tried
    {
      Serial.print("Encoder #");
      Serial.print(encoder, DEC);
      Serial.print(" position error.\n");
    }
  }
 
  //For the purpose of this demo we don't need the position returned that quickly so let's wait a half second between reads
  //delay() is in milliseconds
  delay(500);
}

リスト9:複数のエンコーダからのencoderPosition変数の読み取り。

データ転送後、チップセレクトラインをリリースする前に、最小限の待ち時間が必要です。データシートによれば、この最小時間は3マイクロ秒です。この遅延は通常、より低速のデータレートで自然に発生しますが、適切な動作とタイミング仕様の遵守を確実にするために、コードに明示的に実装することが推奨されます。これにより、AMT22エンコーダとの信頼性の高い通信が確保されます。

まとめ

これで、Same SkyのAMT22アブソリュートエンコーダの構成とデータの読み取りの基本をご理解いただけたと思います。この記事では、AMT22アブソリュートエンコーダに焦点を当てました。Same Skyは、インクリメンタル、アブソリュート、整流の各バージョンを提供するAMTモジュラーエンコーダのラインもご用意しています。

免責条項:このウェブサイト上で、さまざまな著者および/またはフォーラム参加者によって表明された意見、信念や視点は、DigiKeyの意見、信念および視点またはDigiKeyの公式な方針を必ずしも反映するものではありません。

著者について

Damon Tarry, Design Applications Engineer, Same Sky