I2C通信のハードウェアと通信手順の概略を説明します。
I2C通信の基本
前回の記事で、I2C通信では1本のクロック信号と1本のデータ信号のみの構成で、ホストと複数のデバイス間でデータ通信を行うことを説明しました。
クロック信号はあくまでデータ読み取りタイミングを伝えるものなので、つまるところ「1本のデータ信号で、複数のデバイスの中の特定の1つのデバイス」とデータのやり取りを行なっているわけです。
SPI通信の場合は、通信相手を選択するための専用の信号(チップセレクト信号)がありましたが、I2Cではその信号はありません。
今回の記事では、I2C通信がどのように複数デバイスの中の特定の1つのデバイスとデータをやりとりするのか、そのハードウェアと通信方法について説明します。
最初に、例のリビングと勉強部屋間の通信システムで、このような通信を行うためにはどのようなハードウェア構成にすれば実現できるか考えてみます。
検討するポイントは、以下の2点です。
- 1本の信号線で、クロック信号をホストから複数のデバイスに伝える方法
- 1本の信号線で、データ信号をホストからデバイス、デバイスからホストの双方向に伝える方法
❶ クロック信号の伝え方
最初はクロック信号の伝え方を検討します。
クロック信号はSPI通信の時と同様に、次のように構成すれば問題ありません。
ホストがスイッチを操作すると、すべてのデバイスにクロック信号を伝えることができます。
また、クロック信号はホストしか操作できない点もSPI通信と同じです。

❷ データ信号の伝え方
次はデータ信号の伝え方です。
データ信号線は1本しかありません。
つまり、1本の信号線でホストからデバイス、デバイスからホストの双方向に信号を送る必要があります。
一見難しそうですが、回路は次のように構成すれば実現できるんです。

なんだか、ちょっとわかりづらいですよね。
回路部分だけにしてもう少しわかりやすく描き直すと、次のようになります。

とはいっても、どのように動作するのか分かりづらいですよね。
そこで、具体的な動作を見ていきます。
例えば、ホストが送信者になり、ホストからデバイスにデータを送る場合、次のように送信者以外はスイッチを閉じておきます。

この状態でホスト(送信者)がスイッチを操作すると、データが送信できるわけです。(自分の電球も点灯してしまいますが…)

この例ではホストが送信者ですが、デバイス(兄)が送信者になる場合は、ホストとデバイス(弟)がスイッチを閉じた状態にしておけば、デバイス(兄)はスイッチを操作してデータ信号を送信することができます。
この回路には特徴があります。
普通、データの送受信というと1対1で通信するイメージがありますよね。
でも上のような回路では、送信者の信号は受信者全員が受信してしまいますので、誰宛てかが分かりませんよね。
SPI通信の場合は通信相手を選択する信号(チップセレクト)がありました。
I2C通信では、通信相手を選択する仕組みを導入する必要があります。
この仕組みについては、記事の後半で説明します。
ここまでの話はリビングと勉強部屋の通信システムの話(スイッチと電球の話)ですが、実際のI2C通信の回路図を見ると、抵抗が接続されていますよね。
この抵抗の目的も含めて、実際のI2C通信のクロック信号線とデータ信号線のハードウェアを説明します。
I2C通信のハードウェア
それでは、I2C通信のクロック信号線とデータ信号線のハードウェア構成を確認していきましょう!
クロック信号線のハードウェア
SPI通信と同様に、I2C通信でもクロック信号はすべてホスト側が制御します。デバイスはクロック信号に合わせてデータの受信と送信を行います。
ホスト側のクロック信号端子(SCLピン)の内部構成は以下のようになっています。

このようにホスト側には単に電子的なスイッチが入っているだけです。
デバイス側はホストから送信されるクロック信号の電圧値を読むことになります。

つまり、ホストとデバイスでは次のように接続されていることになります。

この回路でホストがスイッチを操作した時にどうなるか考えてみます。
ホストがスイッチをONにした場合、デバイス側で電圧を測定すると0Vになりますので、デバイスは「0」というデータを受信できます。

次に、ホストがスイッチを離した場合を見てみます。

このような状況は、どこかで見覚えがありませんか?
基礎編の第24回でスイッチ回路を検討しました。
上の状況はそのときと全く同じなんです。
スイッチを離した場合、クロック信号線はどこにも接続されていませんので、電圧値が不安定になってしまい、デバイス側では電圧を確定することができません。
そこで、基礎編のスイッチ回路と同じように「プルアップ抵抗」が必要になるんです。

ホスト側のスイッチがOFFの場合、デバイス側の電圧はプルアップ抵抗により3.3Vになります。
I2C通信のプルアップ抵抗値は一般的に数k〜10kΩ程度です。
I2C通信のプルアップ抵抗は適切な値があるのですが、計算は意外に大変です。
実践編のI2C通信の回路条件であれば、10kΩで問題ありません。
このクロック信号線は、デバイスが複数ある場合は、以下のように接続します

データ信号線のハードウェア
データ信号は、ホスト、デバイス関係なく、お互いに送受信できますので、内部構成はホストもデバイスも同じです。
次のように、ホスト、デバイス共に電子的なスイッチと、電圧値を読み取る部分が接続されています。
ホストの内部構成は以下のようになっています。

デバイスの内部構成も同様です。

ホストとデバイスのデータ信号線を接続する場合、先ほどのクロック信号と同様の理由で、次のようにプルアップ抵抗が必要になります。

このような構成にした場合、受信者はスイッチを離しておき、送信者はスイッチを操作します。
ちょっと分かりづらいかもしれませんので、回路の動作を確認してみましょう。
例えば、送信者がホスト(PICマイコン)、受信者がデバイス(センサー・LCDモジュール)のケースを考えてみます。
デバイスは受信者ですのでスイッチを離した状態にします。つまりデバイス側のスイッチはないものとして考えることができます。
また、ホストは送信者ですので、スイッチの操作のみを行います。電圧値の読み取りはしませんので、この部分はないものとして考えることができます。
このように考えると、ホストからデバイスのデータ送信は次の構成になり、先ほどのクロック信号と同様に、ホストからデバイスに信号を送ることができます。

デバイスからホストにデー送信する場合はこれと逆になりますが、仕組みは同じです。
ここまでがI2C通信のハードウェアの説明です。
次に、実際にどのように信号をやり取りするのか確認していきましょう!
(この先まだ長いので、コーヒータイムにして休憩してください)
I2C通信手順のイメージ
I2C通信では2本の信号線を使用して、ホストとデバイス間でデータ(0か1か)を送受信できることがわかりました。
SPI通信の時はこれ以外にチップセレクト信号線がありました。チップセレクト信号を使って「通信相手のスレープを選択」「通信の開始」「通信の終了」をホストからデバイスに知らせることができました。
I2C通信の場合、ホストとデバイス間で0か1かのデータのやり取りしかできませんので、クロック信号とデータ信号のみを使って、デバイス選択、通信の開始、通信の終了のルールを決める必要があります。
I2C通信の通信手順は少ない信号線で多くのことを表現する必要があるため、SPI通信に比べるとかなり複雑です。
詳細な通信手順は次回の記事で説明しますが、今回の記事ではそれに先立って通信手順の概略を説明します。
まずは、I2C通信ではどのように通信を行なっているか概要を把握してみてください。
最初に通信相手のデバイスの選択方法について概略を説明します。
I2C通信では、デバイスごとに固有の番号が割り振られています。この番号のことを「デバイスアドレス」や「バスアドレス」と呼んでいます。
デバイスアドレスは7ビットの数値です(10ビットのモードもありますが、あまり使われないためこの入門シリーズでは7ビットのみを扱います)。
例えば、実践編で使用している温度センサは0x48、LCDモジュールは0x3Eというデバイスアドレスが割り当てられています。

ホストはクロック信号線とデータ信号線を使って、このデバイスアドレスの数値を送信してデバイスを選択します。
全てのデバイスは、ホストから送信されるデバイスアドレスを受信します。デバイスは、送られてきた数値が自分のアドレスだったら「あっ、自分だ」って感じでホストの話し相手(通信相手)になります。
このように、I2C通信の回路に複数のデバイスが接続されていても、デバイスアドレスという数値を送ることにより、通信相手となるデバイスを選択するようになっています。
SPI通信は物理的なチップセレクト信号線で通信相手を選択していましたが、I2C通信の場合はソフトウェアで相手を選択するイメージですね。
このデバイスアドレスについて、ちょっと疑問が出てくるかもしれません。
例えば、I2C通信の回路に温度センサーを2個接続したい場合、2個のセンサのデバイスアドレスが両方とも0x48ですとデバイスが正しく選択できませんよね。
このような場合を想定して、製品によってはデバイスアドレスを変更することができるものもあります。
今回使用する温度センサ(ATD7410)ではデバイスアドレスは4種類のいずれかが選択できます。
次の画像はADT7410温度センサモジュールの説明書の抜粋です。

モジュールの基板上に半田付けする箇所があり、そこを半田付けするかしないかで4種類のアドレスから選択できるようになっています。
一方、LCDモジュールはデバイスアドレスの変更はできません。0x3E固定です。
つまり、温度センサは1つのI2C通信回路に4個まで接続できますが、LCDモジュールは1個しか接続できない、という仕様になります。
もし、温度センサを5個以上、LCDモジュールを2個以上接続したい場合は、PICマイコンに空きピンがありますので、I2C通信回路をもう一つ用意すれば対応できます。
デバイスアドレスが選択できるか固定かは、デバイスモジュールの性格によります。
温度センサは複数の地点の観測をする可能性が高いので4種類のアドレスが選択できるようになっているのだと思います。
また、LCDモジュールは2個も3個も接続するケースは稀だろう、ということでアドレスが固定になっているものと思われます。
I2C通信手順
それでは、今までの知識をもとに、I2C通信の通信手順概略を説明します。
これから説明する通信手順は一番簡単なケースで、ホストから何バイトかデータを送信する例です。
例えば、PICマイコン(ホスト)がデバイス(LCDモジュール)に対して、「表示文字を全て消してください」というデータを送信する例です。
全部で7ステップありますので、順を追って確認してみてください。
1. デバイスがホストの信号を監視
データ通信が行われていない時、すべてのデバイスはホストの信号を監視します。(ホストの動きをじっと待ち構えてる感じですかね)
ホストがI2C通信を開始するとき、ホストから通信開始を知らせる信号を送ります。
全てのデバイスはそのホストからの通信開始の信号を監視しています。

2. ホストが通信開始の合図を送信
ホストがデバイスと通信を開始するとき、最初にホストは通信開始の合図を送ります。
ホストが具体的にどのようにクロック信号線とデータ信号線を制御するかは、次回の通信手順の記事で詳しく説明します。
ホストは「通信開始の合図」を送ったあとすぐ、通信相手のデバイスアドレスを送る、という決まりになっています。
そこで、すべてのデバイスはそのアドレスの受信待ち状態に入ります。

3. ホストがデバイスアドレス送信
ホストが通信開始の合図を送ったあと、ホストはこれから通信相手を指定するためにデバイスアドレスの数値を送ります。
データの送り方はSPIと同様、データ信号線とクロック信号線を使って送ります。
ホストがデバイスアドレスを送信するとき、7ビットのデバイスアドレスに加えて、最下位ビットに1ビットの情報を追加して8ビットデータとして送ります。
この追加する1ビットの情報は、これからデバイスに対して書き込みを行うのか、読み取りを行うのか、という情報です(書き込みが0、読み取りが1)。
この数値情報をもとにして、デバイスアドレスが0x3EのLCDモジュールは自分が選択されたと認識し、さらにこれから書き込みが行われることを認識します。
一方、デバイスアドレスが0x48のセンサーモジュールはこれから行われる通信は自分宛ではないと認識します。

送信するデバイスアドレスの8ビット数値のフォーマットは次のようになっています。

I2C通信のデバイスアドレスの数値表現には注意点があります。
LCDモジュールのデバイスアドレスは「0x3E」です。
このLCDモジュールは、ホストから表示データをデバイスに送るだけです。
つまりホストからデバイスに対してデータの書き込みしか行いませんので、読み書きの設定は常に0(=書き込み)となります。
先ほどのフォーマットに合わせると、以下のように表現できます。

ここからちょっとややこしくなります。
データシートによっては、次のように読み書きビットまで含めてデバイスアドレス、と呼んでいるケースがあります。

上のLCDモジュールのデバイスアドレスは「0x3E」と表現されるケースと「0x7C」と表現されるケースがあります。
次の図は、実践編で使用しているLCDモジュールの説明書に書かれているデバイスアドレスの説明部分です。

この説明では、読み書き指定ビットまで含めてデバイスアドレスと呼んでいますので、0x7Cになっています。
またArduinoのプログラムの場合、I2C制御関数でアドレス指定する場合は7ビット本来の指定になりますので「Arduino表現では0x3Eになります」と説明されています。
ちょっとややこしいですが、ご自分でI2Cモジュールを使用する場合、デバイスアドレスの表現に注意していただければと思います。
4.「了解」返信
次に選択されたデバイスは、認識したことをホストに伝えるために「了解!」という1ビットのデータをホストに伝えます。(具体的にはデバイスからホストに0を送ります)
ホストはデバイスから1ビットの0が返信されてきたら了解された、と認識します。

なお、選択されなかったデバイスは、今回の通信終了の信号を監視し続けます。
5. データ送信
ここまでの手順でホストは通信相手のデバイスが選択できました。
次はデータを送信します。
データ送信の手順はSPI通信と同様に、データ信号線にデータをセットした後、クロック信号を変化させて読み取りタイミングをー知らせます。

通信していないデバイスは引き続き通信終了を監視し続けます。
6.「了解」返信
ホストからスレーフに8ビットのデータを送るごとに、デバイスはホストに「了解」信号を返信します。(具体的には1ビットの0をデバイスからホストに送信します)

7. 通信終了
ホストからデバイスに全てのデータを送信したら、ホストは「通信終了」の信号を送ります。

すべてのデバイスは、今回のデータ通信が終わったことを認識し、次の通信開始に備えてホストの通信開始信号の監視を始めます。
以上がI2C通信手順の概要です。
なんだかずいぶん複雑な気もしますが、2本の信号線のみ(2組のスイッチと電球のみ)でデータ通信を行いますので、デバイス選択の手順などが複雑になっています。
次回の記事では、それぞれのステップの具体的なタイミングチャートを説明します。
更新履歴
日付 | 内容 |
---|---|
2018.6.5 | 新規投稿 |
2025.8.7 | 「ホスト」「デバイス」を「ホスト」「デバイス」に変更 |