18. SPI - シリアル・ペリフェラル・インターフェース

18.1 機能

18.2 概要

 シリアル・ペリフェラル・インターフェース(SPI)を使用して、ATmega48/88/168と周辺デバイス間、または複数のAVRデバイス間での高速同期データ通信を行うことができます。

 USARTをマスターSPIモードで使用することもできます(「USARTのSPIモード」(p.198)を参照)。SPIモジュールを有効にするには、「消費電力を最小にするために」(p.40)に記載されているPRSPIビットをゼロにしておく必要があります。

Figure 18-1. SPI ブロック図(1)

注: 1. SPI各端子の配置については、Figure 1-1 (p.2)Table 13-3 (p.77)を参照

 Figure18-2に、マスターCPUとスレーブCPUをSPIで接続したものを記します。このシステムは、二つのシフトレジスタとマスター・クロック・ジェネレータにより構成されています。SPIマスターは、対象のSPIスレーブのスレーブ・セレクト(/SS)ピンをLowにすることにより通信サイクルを開始します。マスターとスレーブは、各々のシフトレジスタ上に転送データを準備し、そして、マスターがSCKライン上にデータ交換に必要なクロック・パルスを生成します。データは、マスター・アウト・スレーブ・イン(MOSI)ラインでは、常にマスター側からスレーブ側へと、そして、マスター・イン・スレーブ・アウト(MISO)ライン上では、スレーブ側からマスター側へと、シフトされます。それぞれのデータ・パケット終了後、マスターは、スレーブ・セレクト(/SS)ラインをHighにして、スレーブと同期をとります。

 マスターとして設定した場合、SPIインターフェースが/SSラインを自動的に制御することはありません。この制御はユーザー・ソフトウェアにより、通信を開始する前に行わなければなりません。この制御が完了してから、SPIデータ・レジスタへ1バイトの書き込みを行うことで、SPIクロック・ジェネレータが動作開始し、ハードウェアによる8ビット分のデータのシフトがスレーブ側へと行われます。1バイトのシフトが完了すると、SPIクロック・ジェネレータは停止し、転送終了フラグ(SPIF)がセットされます。SPCRレジスタのSPI割込み有効ビット(SPIE)がセットされている場合、割込み要求が発生します。マスターは、SPDRレジスタに次のバイトを書き込んで転送を継続するか、あるいは、スレーブ・セレクト(/SS)ラインをHighにしてパケット終了の合図を送ることができます。最後に受信したバイトは、後で利用できるように、バッファ・レジスタに保存されています。

 スレーブとして設定した場合、SPIインターフェースは/SS端子がHighになっている間、MISOをハイ・インピーダンスにしてスリープ状態のまま待機します。この状態では、ソフトウェアによりSPIデータ・レジスタ(SPDR)の内容を更新することができますが、/SS端子がLowにならない限り、SCK端子のクロック・パルスによってデータが外部へシフトされることはありません。1バイトのシフトが完了すると、転送終了フラグ(SPIF)がセットされます。SPCRレジスタのSPI割込み有効ビット(SPIE)がセットされている場合、割込み要求が発生します。スレーブは、受信したデータを読み取る前に、転送する次のデータをSPDRレジスタに書き込むことができます。最後に受信したバイトは、後で利用できるように、バッファ・レジスタに保存されています。

Figure 18-2. SPI マスター・スレーブ接続

 SPIのシステム構成は、送信方向では単一バッファになっており、受信方向では二重バッファになっています。送信については、シフト・サイクルが完全に完了するまでは、SPIデータ・レジスタに次に送信するバイト・データを書き込んではいけません。一方、データ受信の際は、受信したバイト・データを、次のデータが完全にシフトされてくる前に、SPIデータ・レジスタから読み取らなければならない、ということです。そうしないと、最初のバイト・データは失われてしまいます。

 SPIスレーブ・モードでは、制御ロジックにより、SCK端子への入力クロック信号をサンプリングします。クロック信号を正しくサンプリングできるように、信号のLow期間、High期間は以下のように設定しなければなりません。

 SPIが有効になっているとき、MOSI、MISO、SCK、/SS各端子の入出力方向は、Table18-1 (p.162)のように切り換わります。ポート端子の自動的な機能切り換えについての詳細は、「ポートの機能切り換え」(p.75)を参照してください。

Table 18-1. SPI端子の入出力方向切り換え(注)

端子 マスターSPI側の入出力方向 スレーブSPI側の入出力方向
MOSI ユーザー定義 入力
MISO 入力 ユーザー定義
SCK ユーザー定義 入力
/SS ユーザー定義 入力

注: ユーザー定義のSPI端子の入出力方向を設定する方法の詳細については、「13.3.1 ポートBの機能切り替え」(p.77)を参照してください。

 下記のコーディング例では、SPIをマスターとして初期化し、単純なデータ転送を行う方法を表しています。コーディング例中のDDR_SPIは、実際にSPI端子に関係するデータ入出力方向レジスタに置き換える必要があります。DD_MOSI、DD_MISO、DD_SCKは、各端子に相当する入出力方向レジスタのビットに置き換えてください。例えば、MOSIがPB3端子に配置されている場合は、DD_MOSIをDDB3に、DDR_SPIをDDRBに置き換えてください。

アセンブリ言語での例(1)
	SPI_MasterInit:
		; MOSIとSCKを出力に、他のすべてを入力に設定
		ldi	r17,(1<<DD_MOSI)|(1<<DD_SCK)
		out	DDR_SPI,r17
		; SPIをマスターとして有効に設定、クロック・レートをfck/16に設定
		ldi	r17,(1<<SPE)|(1<<MSTR)|(1<<SPR0)
		out	SPCR,r17
		ret
	SPI_MasterTransmit:
		; データ(r16)の転送を開始
		out	SPDR,r16
	Wait_Transmit:
		; 転送完了を待つ
		in	r16, SPSR
		sbrs	r16, SPIF
		rjmp	Wait_Transmit
		ret
C言語での例(1)
	void SPI_MasterInit(void)
	{
		/* MOSIとSCKを出力に、他のすべてを入力に設定 */
		DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK);
		/* SPIをマスターとして有効に設定、クロック・レートをfck/16に設定 */
		SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
	}

	void SPI_MasterTransmit(char cData)
	{
		/* 転送を開始 */
		SPDR = cData;
		/* 転送完了を待つ */
		while(!(SPSR & (1<<SPIF)))
			;
	}

注: 1. 「サンプル・コードについて」(p.7)を参照

 下記の例は、SPIをスレーブとして設定し、単純なデータ受信を行う方法について示したものです。

アセンブリ言語での例(1)
	SPI_SlaveInit:
		; MISOを出力に、他のすべてを入力に設定
		ldi	r17,(1<<DD_MISO)
		out	DDR_SPI,r17
		; SPIを有効にする
		ldi	r17,(1<<SPE)
		out	SPCR,r17
		ret
	SPI_SlaveReceive:
		; 受信が完了するまで待つ
		sbis SPSR,SPIF
		rjmp SPI_SlaveReceive
		; 受信データを読み取り、リターン
		in	r16,SPDR
		ret
C言語での例(1)
	void SPI_SlaveInit(void)
	{
		/* MISOを出力に、他のすべてを入力に設定 */
		DDR_SPI = (1<<DD_MISO);
		/* SPIを有効にする */
		SPCR = (1<<SPE);
	}

	char SPI_SlaveReceive(void)
	{
		/* 受信が完了するまで待つ */
		while(!(SPSR & (1<<SPIF)))
			;
		/* 受信データを読み取り、リターン */
		return SPDR;
	}

注: 1. 「サンプル・コードについて」(p.7)を参照

18.3 /SS 端子の機能

18.3.1 スレーブ・モード

 SPIがスレーブに設定されている場合、スレーブ・セレクト(/SS)端子は常に入力となります。/SSがLowになっているときSPIが動作状態になり、ユーザー・プログラムで出力になるように設定されていれば、MISOが出力となります。他のすべての端子は入力です。/SSがHighになると、すべての端子は入力となり、SPIは待機状態になり、データ受信を行いません。SPIの回路は、/SSをHighにすると一旦リセットされることに注意してください。

 /SS端子は、スレーブのビット・カウンターをマスターのクロック・ジェネレータと同期させる、パケット/バイトの同期をとるのに便利です。/SS端子がHighになると、SPIスレーブは即座に送受信回路をリセットし、シフト・レジスタ中にある受信途中のデータをすべて破棄します。

18.3.2 マスター・モード

 SPIがマスターに設定されている(SPCRレジスタのMSTRビットがセットされている)場合、ユーザーにより/SS端子の入出力方向を決定することができます。

 /SS端子を出力として設定すると、同端子はSPIシステムに影響をおよぼさない汎用の出力端子となります。一般的な例では、この出力端子をSPIスレーブの/SS端子を制御するために使います。

 /SS端子を入力として設定した場合、SPIマスターとして動作させるためには、この端子をHighに保持しなければなりません。/SS端子を入力としてSPIマスターに設定している時に/SS端子が周辺回路によりLowになった場合、他のマスターがSPIをスレーブとして選択してデータを送信しようとしている、とSPIシステムは解釈します。バスの競合を回避するために、SPIシステムは以下のような動作を行います。

  1.  SPCRレジスタのMSTRビットがクリアされ、SPIシステムをスレーブ・モードにする。その結果、SPIはスレーブとなり、MOSI端子とSCK端子は入力になる。
  2.  SPSRレジスタのSPIFフラグがセットされ、もしSPI割込みが有効で、SREGのIビットがセットされていれば、割込み処理が実行される。

 したがって、割込みによって処理されているSPIデータ転送がマスター・モードで行われており、/SS端子がLowになる可能性がある場合、その割込み処理では常にMSTRビットがセットされているかどうかをチェックする必要があります。MSRTビットがスレーブ・セレクト信号によりクリアされた場合、ユーザー・プログラムによって再びSPIマスター・モードを有効にしなければなりません。

18.4 データ・モード

 シリアル・データに対するSCKの位相と極性には4種類の組み合わせがあり、これはCPHAとCPOLの制御ビットにより決定されます。Figure18-3Figure18-4に、SPIデータ転送フォーマットを示します。SCK信号の逆のエッジにおいて、データ・ビットのシフト出力とラッチ入力がそれぞれ行われており、データ信号が安定するまでに十分な時間を確保しています。この様子は、下記のTable18-3Table18-4に図示したものを見ると明確にわかります。

Table 18-2. CPOLビットの機能

前方エッジ 後方エッジ SPIモード
CPOL=0, CPHA=0 ラッチ動作(立ち上がりエッジ) シフト動作(立ち下がりエッジ) 0
CPOL=0, CPHA=1 シフト動作(立ち上がりエッジ) ラッチ動作(立ち下がりエッジ) 1
CPOL=1, CPHA=0 ラッチ動作(立ち下がりエッジ) シフト動作(立ち上がりエッジ) 2
CPOL=1, CPHA=1 シフト動作(立ち下がりエッジ) ラッチ動作(立ち上がりエッジ) 3

Figure 18-3. SPI転送フォーマット (CPHA = 0)

Figure 18-4. SPI転送フォーマット (CPHA = 1)

18.5 レジスタ詳細

18.5.1 SPCR – SPI コントロール・レジスタ

ビット 7 6 5 4 3 2 1 0
0x2C (0x4C) SPIE SPE DORD MSTR CPOL CPHA SPR1 SPR0 SPCR
Read/Write R/W R/W R/W R/W R/W R/W R/W R/W
初期値 0 0 0 0 0 0 0 0

18.5.2 SPSR – SPI ステータス・レジスタ

ビット 7 6 5 4 3 2 1 0
0x2D (0x4D) SPIF WCOL - - - - - SPI2X SPSR
Read/Write R R R R R R R R/W
初期値 0 0 0 0 0 0 0 0

 ATmega48/88/168のSPIインターフェースは、プログラム・メモリおよびEEPROMのダウンロード、アップロードにも利用されています。「シリアル・プログラミング」(p.297)を参照してください。

18.5.3 SPDR – SPI データ・レジスタ

ビット 7 6 5 4 3 2 1 0
0x2E (0x4E) MSB LSB SPDR
Read/Write R/W R/W R/W R/W R/W R/W R/W R/W
初期値 X X X X X X X X 不定

 SPIデータ・レジスタは汎用レジスタ・ファイルとSPIシフト・レジスタ間でデータのやりとりをするための読み書き可能なレジスタです。このレジスタに書き込みをすることで、データ転送が開始されます。このレジスタを読み取ることにより、シフト・レジスタの受信バッファを読み取ります。


目次に戻る