SPI
名称
SPIライブラリ
/icons/hr.icon
このページでは、内蔵SPIポートの使用方法について説明します。 SPIプロトコル自体については説明しません。 SPIの詳細については、SPIリファレンスを参照してください。 (注意)Arduino STM32のSPIライブラリは、Leaf版とは異なる実装となっています。原文のHardwareSPIクラスは利用出来ません。仕様も異なります。 入門
利用ポートの選択
SPIポートを使って制御するためは、使用するSPIポートを選択する必要があります。
Arduino STM32対応ボード(STM32F103C8T6の場合)には、SPIポートが2つ(SPI1、SPI2)あります。
SPI1ポート :利用ピン MOSI=PA7、MISO=PA6、SCK=PA5、NSS=PA4
SPI2ポート :利用ピン MOSI=PB15、MISO=PB14、SCK=PB13、NSS=PB12
Arduinoのスケッチの流用や互換性を意識するのであれば、SPI1を利用しましょう。
SPI1ポートは定義済のSPIオブジェクトを利用して制御することが出来ます。
SPI2用の定義済オブジェクトは用意されていません。
どうしても、SPI2ポートを使いたい場合、2つの方法があります。
SPI2を利用するオブジェクトを作成して利用する :SPIClass SPI_2(2);
定義済みSPIオブジェクトの利用ポートを切り替える :SPI.setModule(2);
SPIポートを同時に2つ利用したい場合は、SPI2用のオブジェクトを作成しましょう。
(※SPIオブジェクトはSPI1、SPI2の状態を別々に保持しているようなので、SPI.setModule()で動的に切り替えて利用できるかもしれません。)
SPIポートをオンにする
ここでは、SPI1を使うことにします。SPI1ポートの利用は定義済SPIオブジェクトを使います。
SPIポートを使う上で最初にやることは、SPIポートをオンにし、通信条件を設定することです。
次のサンプルスケッチがその例です:
code:sample1.ino
// SPIポート1を利用
void setup() {
// SPIポートをオン(有効)にし、通信条件を設定します
SPI.begin(); // SPI1ポート オン
SPI.setBitOrder(MSBFIRST); // ビットオーダーの設定
SPI.setDataMode(SPI_MODE0); // データモードの設定
SPI.setDataSize(DATA_SIZE_8BIT); // データサイズの設定
SPI.setClockDivider(SPI_CLOCK_DIV4); // クロック速度 72 / 4 = 18MHz
}
void loop() {
// ここにSPIを使った通信処理を実装します
}
SPIポートをオンにするにはbegin()関数を使います。
また通信条件の設定には、setBitOrder()、setDataMode()、setDataSize()、setClockDivider()を使用します。
通信条件の設定を行わない場合の設定(デフォルト)は次の通りです。
ビットオーダー :MSBFIRST
データモード :SPI_MODE0
データサイズ :DATA_SIZE_8BIT
クロック分周 :SPI_CLOCK_DIV32
通常の利用では、setClockDivider()で通信速度の指定のみでも良いでしょう。
書式
■ void begin(void)
SPIポートをオンにして、GPIOピンモードをマスタとして使用するように設定します。
■ void setBitOrder(BitOrder bitOrder)
SPIバスの入出力に使用するビットオーダーを設定します。
LSBFIRST(最下位ビットから最上位ビット)またはMSBFIRST(最上位ビットから先に)のいずれかのSPIバスからシフトアウトされたビットの順序を設定します。
引数
bitOrder :ビットオーダー (BitOrder型 定数)LSBFIRST または MSBFIRST
BitOrder型は次のように定義されています。
code:c
enum BitOrder {
LSBFIRST = 0,
MSBFIRST = 1
};
指定しない場合のデフォルト値はMSBFIRSTです。
■ void setDataMode(uint8_t dataMode)
SPIの転送モードを設定します。
引数
dataMode:転送モード、次の定数が設定可能です。
table:転送モード
転送モード クロック極性 クロック位相 CLKエッジ シフトCLKエッジ
SPI_MODE0 アイドル時CLK LOW 立ち上がり ↗ ↘
SPI_MODE1 アイドル時CLK LOW 立ち下がり ↘ ↗
SPI_MODE2 アイドル時CLK HIGH 立ち下がり ↘ ↗
SPI_MODE3 アイドル時CLK HIGH 立ち上がり ↗ ↘
指定しない場合のデフォルト値はSPI_MODE0です。
■ void setClockDivider(uint32_t clockDivider)
SPIクロック分周器を設定します。
引数
clockDivider:システムクロックの分周値、次の定数が設定可能です。
SPI_CLOCK_DIV2 :1/2
SPI_CLOCK_DIV4 :1/4
SPI_CLOCK_DIV8 :1/8
SPI_CLOCK_DIV16 :1/16
SPI_CLOCK_DIV32 :1/32
SPI_CLOCK_DIV64 :1/64
SPI_CLOCK_DIV128:1/128
SPI_CLOCK_DIV256:1/256
指定しない場合、4MHzに近いクロックかつ、4MHz以下の条件の定数が設定されます。
システムクロックが72MHzの場合、SPI_CLOCK_DIV32、SPIのクロックは2.25MHzとなります。
(注意)SPI2では、SPI_CLOCK_DIV2の指定は出来ません。
■ void setDataSize(uint32 ds)
データサイズを指定します。
引数
ds :データサイズ、次の定数の指定が可能です。
DATA_SIZE_8BIT :8ビット
DATA_SIZE_16BIT :16ビット
指定しない場合のデフォルト値はDATA_SIZE_8BITです。
SPIでの通信
SPIポートの設定が完了したら、通信を開始しましょう。
SPI.write()を使用してデータを送信し、SPI.read()を使用してデータを受信します。
送信、受信の両方をSPI.transfe()を使用して実行できます。
■ void write(uint16 data)
データを送信します。
引数
data :送信データ(バイト/ワード)
送信するデータのサイズ(バイト/ワード)はデータサイズの設定に依存します。
■ uint16 read(void)
未読データ(バイト/ワード)を取得します。
未読のバイト/ワード待ちがなければ、この関数は受信されるまでブロックされます。
戻り値
受信データ(バイト/ワード)
受信するデータサイズ(バイト/ワード)はデータサイズの設定に依存します。
■ uint8 transfer(uint8 data)
■ uint16_t transfer16(uint16_t data)
データ(バイト/ワード)を送信し、次に未読のバイトを返します。
この関数は受信する前に送信します。ただし、送信前に未読データがある場合、そのデータを破棄してから送信します。
transfer()では、バイトデータの送受信、transfer16()ではワードデータの送受信を行います。
引数
data :送信データ(バイト/ワード)
戻り値
受信データ(バイト/ワード)
これまで解説した関数を使い、SPIを介して数値を送信して、戻ってくる数値をシリアル出力しましょう。
code:sample2.ino
// SPIポート1を利用
void setup() {
// SPIポートをオン(有効)にし、通信条件を設定します
SPI.begin(); // SPI1ポート オン
SPI.setBitOrder(MSBFIRST); // ビットオーダーの設定
SPI.setDataMode(SPI_MODE0); // データモードの設定
SPI.setDataSize(DATA_SIZE_8BIT); // データサイズの設定
SPI.setClockDivider(SPI_CLOCK_DIV4); // クロック速度 72 / 4 = 18MHz
}
void loop() {
// 数値データ245を送信し、応答を待ちます
spi.write(245);
byte response = spi.read();
// 受信した応答を表示します。
Serial.print("response: ");
Serial.println(response, DEC);
}
新しいインタフェース
Arduino IDE 1.6以降では、1つのSPIバスに複数のデバイスが接続されている場合に、効率よく通信相手を切り替えて通信するためのインタフェースが追加されました。Arduino STM32でも利用可能です。
追加されたインタフェース
class SPISettings :SPIポート設定値。デバイス別に設定値を用意して通信時に指定します。
beginTransaction() :引数のSPIポート設定値で通信条件設定を行い、SPIポートをオンにします。
beginTransactionSlave() :上記と同じですが、SPIポートをスレーブモードでオンにします。
endTransaction() :beginTransaction()、beginTransactionSlave()を終了します(*1)。
方針としては上記のインタフェースを使い、
SPIバスに接続するデバイス毎にSPISettings型のSPIポート設定値を用意し、個々のデバイスの通信はトランザクションの単位でで行います。その際、個々のデバイス用のSPIポート設定値を使います。これにより、各トランザクションは、異なる通信条件で通信することが出来ます。
(現状、デバイスをCSにてディスエイブルにしても、デバイスのバスがハイインピータンスにならず、バスの共有出来ないデバイスがあります。幸いSPIが2本あるので、そのような場合はSPIを2系統使うのも良いでしょう)
脚注
(*1):endTransaction()は、現時点のバージョンでは何も行いません。ソースコード上でトランザクションの区切りとして明確化するために利用します。
DMA転送
Arduino STM32では、DMA(Direct Memory Access)を使った非同期のSPI通信が可能です。
DMA転送が利用可能な関数
uint8 dmaTransfer()
void dmaTransferSet()
uint8 dmaTransferRepeat()
uint8 dmaSend()
void dmaSendSet()
uint8 dmaSendRepeat()
uint8 dmaSendAsync()
非同期のSPI通信利用で、転送完了時に何らかの処理を行いたい場合は、コールバック関数を利用することが可能です。
次の関数で登録出来ます。
void onReceive(void(*)(void))
void onTransmit(void(*)(void))
例えばデータ送信完了後に、コールバック関数にて次のデータを送信する等の処理をバックグランドで行うことも可能です。
ライブラリリファレンス
/icons/hr.icon
SPISettingsクラス class SPISettings ヘッダーファイル SPI.h SPI通信の通信条件の設定値を保持します。SPIクラスを使ってSPI通信を行う際の通信条件とて利用します。
コンストラクタ
■ SPISettings(uint32_t clock, BitOrder bitOrder, uint8_t dataMode)
■ SPISettings(uint32_t clock, BitOrder bitOrder, uint8_t dataMode, uint32_t dataSize)
■ SPISettings(uint32_t clock)
■ SPISettings()
SPISettingsクラスのインスタンスを生成します。
引数
clock :システムクロックの分周値、次の定数が設定可能です。
SPI_CLOCK_DIV2 :1/2
SPI_CLOCK_DIV4 :1/4
SPI_CLOCK_DIV8 :1/8
SPI_CLOCK_DIV16 :1/16
SPI_CLOCK_DIV32 :1/32
SPI_CLOCK_DIV64 :1/64
SPI_CLOCK_DIV128:1/128
SPI_CLOCK_DIV256:1/256
bitOrder :ビットオーダー (BitOrder型 定数)LSBFIRST または MSBFIRST
dataMode :転送モード、次の定数が設定可能です。
table:転送モード
転送モード クロック極性 クロック位相 CLKエッジ シフトCLKエッジ
SPI_MODE0 アイドル時CLK LOW 立ち上がり ↗ ↘
SPI_MODE1 アイドル時CLK LOW 立ち下がり ↘ ↗
SPI_MODE2 アイドル時CLK HIGH 立ち下がり ↘ ↗
SPI_MODE3 アイドル時CLK HIGH 立ち上がり ↗ ↘
dataSize :データサイズ、次の定数の指定が可能です。
DATA_SIZE_8BIT :8ビット
DATA_SIZE_16BIT :16ビット
設定しない場合、下記のデフォルトの通信条件が設定されます。
ビットオーダー :MSBFIRST
データモード :SPI_MODE0
データサイズ :DATA_SIZE_8BIT
クロック分周 :SPI_CLOCK_DIV32
クロック分周は、4MHzに近いクロックかつ、4MHz以下となる定数が設定されます。
システムクロックが72MHzの場合、SPI_CLOCK_DIV32、SPIのクロックは2.25MHzとなります。
SPIクラス class SPIClass ヘッダーファイル SPI.h コンストラクタ
■ SPIClass(uint32 spiPortNumber)
SPIClassクラスのインスタンスを生成します。
引数
spiPortNumber :SPIポート番号、1 または 2(大容量ボードでは3の指定も可能)
SPIバスの利用開始
■ void begin(void)
SPIポートをオンにして、GPIOピンモードをマスタとして使用するように設定します。
デフォルトの通信条件は次の通りです。
ビットオーダー :MSBFIRST
データモード :SPI_MODE0
データサイズ :DATA_SIZE_8BIT
クロック分周 :SPI_CLOCK_DIV32
クロック分周は、4MHzに近いクロックかつ、4MHz以下となる定数が設定されます。
システムクロックが72MHzの場合、SPI_CLOCK_DIV32、SPIのクロックは2.25MHzとなります。
スレーブモード SPIバスの利用開始
■ void beginSlave(void)
SPIポートをオンにし、GPIOピン・モードをスレーブとして使用するように設定します。
SPIポートは、ソフトウェアスレーブ管理を使用して全二重モードで有効になります。
(注意)SPI.hにはvoid beginSlave(uint32 bitOrder, uint32 mode)の宣言がありますが、現時点では実装されていません。
SPIバスの利用終了
■ void end(void)
SPIポートをディセーブルにしますが、GPIOピンモードは変更しません。
SPIバスの利用トランザクション開始
■ void beginTransaction(SPISettings settings)
■ void beginTransaction(uint8_t pin, SPISettings settings)
指定したsettingsにて通信条件を設定し、
SPIポートをオンにして、GPIOピンモードをマスタとして使用するように設定します。
機能としては、begin(void)と同等です。
引数
settings :SPIポート設定値
pin :SSピン ※現時点ではこの指定は利用されません
スレーブモード SPIバスの利用トランザクション開始
■ void beginTransactionSlave(SPISettings settings)
指定したsettingsにて通信条件を設定し、
SPIポートをオンにし、GPIOピン・モードをスレーブとして使用するように設定します。
SPIポートは、ソフトウェアスレーブ管理を使用して全二重モードで有効になります。
機能としては、beginSlave(void)と同等です。
トランザクション終了
■ void endTransaction(void)
beginTransaction()、 beginTransactionSlave()で開始したトランザクションを終了します。
本関数は、現時点のバージョンでは何も行いません。ソースコード上でトランザクションの区切りとして明確化するために利用します。
通信条件の設定
■ void setDataMode(uint8_t dataMode)
SPIの転送モードを設定します。
引数
dataMode:転送モード、次の定数が設定可能です。
table:転送モード
転送モード クロック極性 クロック位相 CLKエッジ シフトCLKエッジ
SPI_MODE0 アイドル時CLK LOW 立ち上がり ↗ ↘
SPI_MODE1 アイドル時CLK LOW 立ち下がり ↘ ↗
SPI_MODE2 アイドル時CLK HIGH 立ち下がり ↘ ↗
SPI_MODE3 アイドル時CLK HIGH 立ち上がり ↗ ↘
指定しない場合のデフォルト値はSPI_MODE0です。
■ void setClockDivider(uint32_t clockDivider)
SPIクロック分周器を設定します。
引数
clockDivider:システムクロックの分周値、次の定数が設定可能です。
SPI_CLOCK_DIV2 :1/2
SPI_CLOCK_DIV4 :1/4
SPI_CLOCK_DIV8 :1/8
SPI_CLOCK_DIV16 :1/16
SPI_CLOCK_DIV32 :1/32
SPI_CLOCK_DIV64 :1/64
SPI_CLOCK_DIV128 :1/128
SPI_CLOCK_DIV256 :1/256
指定しない場合、4MHzに近いクロックかつ、4MHz以下の条件の定数が設定されます。
システムクロックが72MHzの場合、SPI_CLOCK_DIV32、SPIのクロックは2.25MHzとなります。
(注意)SPI2では、SPI_CLOCK_DIV2の指定は出来ません。
■ void setBitOrder(BitOrder bitOrder)
SPIバスの入出力に使用するビットオーダーを設定します。
LSBFIRST(最下位ビットから最上位ビット)またはMSBFIRST(最上位ビットから先に)のいずれかのSPIバスからシフトアウトされたビットの順序を設定します。
引数
bitOrder :ビットオーダー (BitOrder型 定数)LSBFIRST または MSBFIRST
BitOrder型は次のように定義されています。
code:c
enum BitOrder {
LSBFIRST = 0,
MSBFIRST = 1
};
指定しない場合のデフォルト値はMSBFIRSTです。
■ void setDataSize(uint32 ds)
データサイズを指定します。
引数
ds :データサイズ、次の定数の指定が可能です。
DATA_SIZE_8BIT :8ビット
DATA_SIZE_16BIT :16ビット
指定しない場合のデフォルト値はDATA_SIZE_8BITです。
データ受信
■ uint16 read(void)
未読データ(バイト/ワード)を取得します。
未読のバイト/ワード待ちがなければ、この関数は受信されるまでブロックされます。
戻り値
受信データ(バイト/ワード)
受信するデータサイズ(バイト/ワード)はデータサイズの設定に依存します。
■ void read(uint8 *buffer, uint32 length)
指定したバイト数のデータを読み込み、バッファに格納します。
引数
buffer :受信したバイトを格納するバッファ
length :バッファに格納するバイト数
この関数は、必要なバイト数が読み取られるまでブロックします。
データ送信
■ void write(uint16 data)
データを送信します。
引数
data :送信データ(バイト/ワード)
送信するデータのサイズ(バイト/ワード)はデータサイズの設定に依存します。
■ void write16(uint16 data)
8ビットデータ長で2バイトのデータを送信します。
引数
data :送信データ(ワード)
■ void write(uint16 data, uint32 n)
指定された回数だけ1バイト/ワードを送信します。
引数
data :送信データ(バイト/ワード)
n :送信回数
送信するデータのサイズ(バイト/ワード)はデータサイズの設定に依存します。
■ void write(const void * buffer, uint32 length)
複数のバイト/ワードを送信します。
引数
buffer :送信するバイト/ワードのバッファ
length :送信するバッファ内のバイト数/ワード数
データ送受信
■ uint8 transfer(uint8 data) const
■ uint16_t transfer16(uint16_t data) const
データ(バイト/ワード)を送信し、次に未読のバイトを返します。
この関数は受信する前に送信します。ただし、送信前に未読データがある場合、そのデータを破棄してから送信します。
transfer()では、バイトデータの送受信、transfer16()ではワードデータの送受信を行います。
引数
data :送信データ(バイト/ワード)
戻り値
受信データ(バイト/ワード)
DMA連携データ送受信
■ void dmaTransferSet(const void *transmitBuf, void *receiveBuf)
■ uint8 dmaTransferRepeat(uint16 length)
■ uint8 dmaTransfer(const void * transmitBuf, void * receiveBuf, uint16 length)
DMA転送を利用してデータの送受信を行います
送信するデータのサイズ(バイト/ワード)はデータサイズの設定に依存します。
引数
transmitBuf :送信するバイト格納バッファ. NULLを指定した場合、lengthバイト分0xFFを繰り返し送信します。
receiveBuf :受信データ格納バッファ
length :送信データ長
dmaTransferSet()は、送信するバイト格納バッファ、受信データ格納バッファの指定のみ行います。
dmaTransferRepeat()は、dmaTransferSet()で指定したバッファを利用して指定した送信データ長のデータ送信後、デバイスからの応答データを受信します。
dmaTransfer()は、dmaTransferRepeat()、dmaTransferSet()を呼び出してデータの送受信を行います。
onReceive()関数にて受信完了通知コールバック関数を登録している場合、dmaTransferRepeat()、dmaTransfer()はデバイスからの応答データの受信待ちを行わずに、即時復帰し、0(正常終了)を返します。
コールバック関数を登録していない場合、デバイスからの応答を待ちを行います。
デバイスから100ミリ秒たっても応答が無い場合はタイムアウトエラーを返します。
戻り値
dmaTransferRepeat()、dmaTransfer()は次の実行処理結果を返します。
0:正常終了
2:エラー(タイムアウト)
■ void dmaSendSet(const void * transmitBuf, bool minc)
■ uint8 dmaSendRepeat(uint16 length)
■ uint8 dmaSend(const void * transmitBuf, uint16 length, bool minc = 1)
DMA転送を利用してデータの送信を行います
送信するデータのサイズ(バイト/ワード)はデータサイズの設定に依存します。
引数
transmitBuf :送信するバイト格納バッファ. NULLを指定した場合、lengthバイト分0xFFを繰り返し送信します。
minc :メモリインクリメントモード指定.1:インクリメントモード、0:サイクリックモード
length :送信データ長
dmaSendSet()は、送信するバイト格納バッファ、メモリインクリメントモード指定の指定のみ行います。
dmaSendRepeat()は、dmaSendSet()で指定したバッファを利用して指定した送信データ長のデータ送信後、デバイスからの応答データを受信します。
dmaSend()は、dmaSendSet()、dmaSendRepeat()を呼び出してデータの送信を行います。
transmitCallback()関数にて送信完了通知コールバック関数を登録している場合、dmaSendRepeat()、dmaSend()はデバイスへのデータ送信完了待ちを行わずに即時復帰し、0(正常終了)を返します。
コールバック関数を登録していない場合、デバイスへのデータ送信完了待ちを行います。
デバイスから100ミリ秒たっても送信が完了しない場合はタイムアウトエラーを返します。
戻り値
dmaSendRepeat()、dmaSend()は次の実行処理結果を返します。
0:正常終了
2:エラー(タイムアウト)
■ uint8 dmaSendAsync(const void * transmitBuf, uint16 length, bool minc = 1)
DMA転送を利用してデータの非ブロック送信を行います。
送信するデータのサイズ(バイト/ワード)はデータサイズの設定に依存します。
dmaSend()との違いは、データ送信中にdmaSendAsync()が呼び出された場合、直前の送信が完了するまでブロックしてからら、次のDMA転送のための初期化をして通信を再確立します。
引数
transmitBuf :送信するバイト格納バッファ. NULLを指定した場合、lengthバイト分0xFFを繰り返し送信します。
minc :メモリインクリメントモード指定.1:インクリメントモード、0:サイクリックモード
length :送信データ長
本関数は、コールバック関数の利用は出来ません。
戻り値
0:正常終了
2:エラー(タイムアウト)
コールバック関数の登録
■ void onReceive(void(*callback)(void))
DMA転送の受信完了時のコールバック関数を登録を行います。
引数
callback:コールバック関数
コールバック関数登録をクリアしたい場合は、NULLを指定します。
beginTransaction()、beginTransactionSlave()を使用したトランザクション中に本関数にてコールバック関数の登録を行った場合、そのトランザクションのSPISettingsに登録されます。異なるトランザクション(異なるSPISettingsを利用)では別途、コールバック関数を登録する必要があります。
■ void onTransmit(void(*callback)(void))
DMA転送の送信完了時のコールバック関数を登録を行います。
引数
callback:コールバック関数
コールバック関数登録をクリアしたい場合は、NULLを指定します。
beginTransaction()、beginTransactionSlave()を使用したトランザクション中に本関数にてコールバック関数の登録を行った場合、そのトランザクションのSPISettingsに登録されます。異なるトランザクション(異なるSPISettingsを利用)では別途、コールバック関数を登録する必要があります。
■ void attachInterrupt(void)
現行バージョンでは、未実装のため利用出来ません。
■ void detachInterrupt(void)
現行バージョンでは、未実装のため利用出来ません。
SPI利用ピンの参照
■ uint8 misoPin(void)
MISOに利用しているピン番号を返します。
■ uint8 mosiPin(void)
MOSIに利用しているピン番号を返します。
■ uint8 sckPin(void)
SCKに利用しているピン番号を返します。
■ uint8 nssPin(void)
NSSに利用しているピン番号を返します。
SPIデバイスの参照
■ spi_dev* c_dev(void)
■ spi_dev* dev(void)
このSPIインスタンスの基礎となるspi_devへのポインタを取得します。
非推奨API
■ uint8 send(uint8 data)
write()と同機能です。write()を使用して下さい。
■ uint8 send(uint8 *data, uint32 length)
write()と同機能です。write()を使用して下さい。
■ uint8 recv(void)
read()と同機能です。read()を使用して下さい。
関連項目
/icons/hr.icon
このドキュメントはleafLabs, LLC.が執筆し、たま吉が翻訳・加筆修正したものです。