シリアル通信I2C【STM32のI2C詳細】

STM32に内蔵のペリフェラルI2Cの使い方を解説しています。この章ではI2C仕様のEEPROMへのデータの読み込および書き込を例に解説しています。

シリアル通信I2Cとは

めかのとろ

シリアル通信I2C同期型の双方向通信です。Philips社が提唱したシリアル通信方式です。信号線が2本だけでそのうちの一本がデータを双方向でやり取りする信号線SDA(Serial Data Line)で、他方が同期用クロック信号線SCL(Serial Clock Line)です。

めかのとろ

特徴としてはマスタ側とスレーブ側に分かれ、マスタ側が通信をすべてコントロールしています。I2C では一つのバスに複数のスレーブを接続することができます。バスはオープンドレインで接続されているためプルアップ抵抗(電源電圧が3.3Vのとき、2.2k程度)が必要です。

シリアルI2C通信
めかのとろ

I2Cでは1文字(1バイト)ずつ送受信し,1バイト転送毎に受信側からACK(アクノリッジ)信号を返送し、互いに確認を取りながら(ハンドシェイク)転送を行います。マスタが受信を終了させたい時点でACKを返さないNACK(ノットアクノリッジ)とします。

めかのとろ

その後ストップコンディションを生成して通信を終了させます。I2Cデバイスは個々にスレーブアドレスを持っていて、スレーブデバイス内の指定アドレスのレジスタやメモリとアクセスしてデータのやり取りをします。

めかのとろ

ここではI2C通信仕様のEEPROMを使用したデータの送受信を実際のプログラムで解説していきます。
I2C通信を使った送受信はルールが規格できまったものであるためメーカーなどが作成したサンプルプログラムなどを参考に、ある程度決まった形式のものを使用するのが無難です。

めかのとろ

まず、他のペリフェラルと同様に初期化を解説します。

I2C通信初期化手順

目的:使用するI2CはI2C1でGPIOはPB6とPB7
■ I2CモードでACK有効
■ ACKを返すアドレス長は7ビット
■ クロックスピードは200000でデューティ比は1:1

 ① I2C1にクロック供給 

めかのとろ

使用するI2C1にクロックを供給します。APB1バスに接続していますのでAPB1に供給します。

 ② I2C1に使用するGPIO設定 

めかのとろ

I2C1に使用するIOは送受信にPB6とPB7をオルタネートオープンドレイン出力に指定します。

 ③ I2C1の初期設定 

めかのとろ

I2C通信の初期設定を実行します。初期化はI2C_Init関数を実行して上記のパラメータを設定します。

めかのとろ

関数の第1引数は設定対象のI2C(I2C1,I2C2)、第2引数は構造体メンバになっていて以下に示すとおりです。

I2C初期化関数実行例: I2C_Init(I2C1, &I2C_InitStructure);

めかのとろ

I2C_ModeメンバにはI2C通信ではI2Cモードを指定します。

めかのとろ

I2C_DutyCycleメンバには通常クロックパルスデューティ比で1:1にしておきます。このメンバは高速クロックの波形整形のためのものでクロック周波数がファストモードの場合にのみ有効なメンバです。

めかのとろ

I2C_AckメンバはACK(アクノリッジ)を返す指定にするためI2C_Ack_Enableとします。

めかのとろ

I2C_AcknowledgeAddressメンバはスレーブアドレス幅を指定します。スレーブデバイスの仕様に従い、ここでは7ビットを指定します。

めかのとろ

I2C_ClockSpeedメンバはクロック周波数を指定します。ここではI2C_CLOCK(200000)を指定します。

めかのとろ

クロック周波数が100kHz以下であれば標準モードそれ以外ではファストモードが自動的に選択されるようになっています。200000の場合はファストモードとなります。

 ④ I2C1を有効化する 

めかのとろ

これまででI2C通信の初期化ができましたのでI2C_Cmd関数を実行してI2Cポートを有効にします。

I2C通信有効化関数実行例: I2C_Cmd(I2C1, ENABLE);

めかのとろ

関数の第1引数には設定対象のI2C1かI2C2を指定し、第2引数はENABLEで有効DISABLEで無効となります。

I2C仕様EEPROM書き込み


I2C型EEPROM接続回路
めかのとろ

I2C書き込みは下記のフォーマット手順に従った書き込み関数I2C_EEPROM_Write()を作成し実行します。関数内ではコマンド時系列で実行していきます。

データ書き込み通信フォーマット
めかのとろ

STM32のI2C通信ではデータ転送の合間にさまざまなイベントが発生しています。実際のプログラムにおいてはイベントの状態を確認しながら手順を進めていくことになります。

 ① スタートコンディションの生成 

めかのとろ

スタートコンディションはI2C_GenerateSTART関数を実行して生成すると自動的にマスターモードとなり、スレーブ側との通信を開始します。

I2C_GenerateSTART関数の実行例: I2C_GenerateSTART(I2C1, ENABLE);

めかのとろ

関数の第1引数は設定対象のI2C(I2C1,I2C2)、第2引数はENABLEで有効、DISABLEで無効となります。

I2C_CheckEvent関数の実行例: while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

めかのとろ

I2C_CheckEvent関数を実行して、ここではマスターモードになったかどうかを確認しています。

I2Cイベントコード

 ② I2C通信のモード指定

めかのとろ

ここではI2C通信のモードを設定します。I2C_Send7bitAddress関数を実行して、スレーブ側(EEPROM)のアドレスとデータ方向(受信か送信)を指定します。

I2C_Send7bitAddress関数実行例:
 I2C_Send7bitAddress(I2C1, EEPROM_SLAVE_ADDRESS, I2C_Direction_Transmitter);

めかのとろ

関数の第1引数は設定対象のI2C(I2C1,I2C2)、第2引数は8ビットアドレスなのでスレーブアドレス7ビットを左詰めにして指定します

めかのとろ

採用している回路ではEEPROMアドレスは上位4ビットが1010の固定で、下位3ビットがA0/A1/A2の指定で000の1010000(7ビット)です。

めかのとろ

これを左詰めにして10100000(0xA0)(EEPROM_SLAVE_ADDRESS)で指定します。第3引数には書き込みの場合ですのでデータ方向I2C_Direction送信(Transmitter)を指定します。ここでもI2C_CheckEvent関数を実行して、ここでは送信(トランスミッタ)モードになったかどうかを確認しています。

 ③ EEPROMデータを格納するアドレス2バイト分を指定

めかのとろ

I2Cスレーブ側(EEPROM)に書き込むデータを格納するアドレス2バイト分を指定します。I2C_SendData関数を実行して上位、下位に分けて指定します。

I2C_SendData関数実行例:
 I2C_SendData(I2C1, (uint8_t)((Address & 0xFF00) >> 8)); //上位アドレス
 I2C_SendData(I2C1, (uint8_t)(Address & 0x00FF)); //下位アドレス

めかのとろ

関数の第1引数は設定対象のI2C(I2C1,I2C2)、第2引数はデータを格納する2バイトアドレスを指定します。アドレス指定の範囲は各EEPROMの容量によりますのでデータシートで確認して使用できる範囲で指定します。

I2C_CheckEvent関数の実行例:
 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED);

 ④ EEPROMに書き込みデータ(2バイト)を送信 

めかのとろ

EEPRONに書き込みたいデータをデータレジスタに転送します。③のアドレス転送時と同様でデータの上位と下位に分けて転送します。

 ⑤ ストップコンディション生成 

めかのとろ

ストップコンディションはI2C_GenerateSTOP関数を実行して生成します。関数の第1引数は設定対象のI2C(I2C1,I2C2)、第2引数はENABLEで有効、DISABLEで無効となります。

 I2C_EEPROM_Poll関数 

めかのとろ

EEPROMへの書き込みは前述した書き込み関数を実行してストップコンディションを生成してから開始します。EEPROMへの書き込みにはある程度時間がかかるため、連続してデータを書き込むにはデータ転送が完了したことを確認してから次のデータを書き込まなければなりません。

めかのとろ

この処理に関してはSTM32メーカーにより作成したI2C_EEPROM_Poll関数がありますのでそのまま使用します。EEPROM書き込みの際にはI2C_EEPROM_Write関数とセットで実行します。

DATA(16ビット)をEEPROMアドレスADDRESS(2バイト)に送信する実行例:
 I2C_EEPROM_Write(DATA, ADDRESS);
 I2C_EEPROM_Poll();

I2C仕様EEPROM読み込み

めかのとろ

I2C読み込みは下記のフォーマット手順に従った読み込み関数I2C_EEPROM_Read()を作成し実行します。この関数ではEEPROMの特定のデータが格納しているアドレスを指定するとデータを返します。

データ読み込み通信フォーマット
めかのとろ

読み込みの場合はI2Cバスが事前に空いているかどうかを確認してから手順を始めます
①から③までは書き込み関数の場合と同じです。ここで読み込みたいデータが格納しているアドレスを指定します。ここまではアドレスを指定するために送信モードで行っていました。

 ④ スタートコンディション生成(再スタート)

めかのとろ

ここから指定したアドレスのデータを受信するための切替スタートコンディションを再度生成します。

 ⑤ I2C通信のモード指定 

めかのとろ

I2C通信モードを受信にするためにI2C_Send7bitAddress関数を実行して、②と同様に第2引数はスレーブアドレスを、第3引数には読み込みの場合ですのでデータ方向I2C_Directionに受信(Receiver)を指定します。ここでもI2C_CheckEvent関数を実行して、ここでは受信(レシーバ)モードになったかどうかを確認しています。

 ⑥  読み込みデータを受信 

めかのとろ

データ受信の準備ができたところでI2C_CheckEvent関数を実行して、受信レジスタにデータ転送完了の確認に続いて2バイトデータを上位1バイト分、下位1バイト分の順に受信します。

 ⑦  ACK無効指定 

めかのとろ

この時点で受信を終了させたいのでACKを返さずにNACK(ノットアクノリッジ)とするために、I2C_AcknowledgeConfig関数でACKを無効にしています。この例では受信する2バイトデータのうち最後の1バイト(下位バイト分)を受信する前に実行してNACKを返しています。

 ⑧  ストップコンディション生成 

めかのとろ

ストップコンディションを生成します。

 ⑨  ACK有効指定 

めかのとろ

次回の通信に備えてACKを有効に戻します

 ⑩  読み出しデータ処理 

めかのとろ

受信した上位、下位データから2バイトデータに処理し、読み込み関数I2C_EEPROM_Readの返り値とします。

ポイント

I2Cはペリフェラルのなかでも繊細なもので、ライブラリの関数をブラックボックスとして使用するだけでは行き詰まるかもしれません。STM32レファレンスマニュアルとライブラリ関数の内容を照らし合わせて動作を確認することが理解への早道でつぶしが効くようになります。