温度センサー(ADT7410)のI2C通信では、LCDモジュールとは違った通信手順が必要です。今回の記事では、その通信手順を説明します。
LCDモジュール制御と異なる点
温度センサー(ADT7410)のI2C通信制御は、LCDモジュールのそれと大きく異なるところがあります。
LCDモジュールの制御では、PICマイコンからLCDモジュールに対してデータを送信するだけでした。
温度センサーの場合、PICマイコンからセンサーに設定データなどを送信する以外に、温度データをセンサーからPICマイコンに返信してもらう必要があります
ADT7410のI2C通信手順では、デバイスからホストにデータを返信してもらう、という新しい通信手順が必要になります。
そこで今回の記事では、ADT7410のI2C通信手順について、次の項目に分けて説明します。
- 動作設定の通信手順
ADT7410の動作設定のI2C通信手順は、LCDモジュールとほぼ同じです。 - 温度データ取得の通信手順
ADT7410からPICマイコンに温度データを返信してモラます。
今まで出てきた通信手順に加え、新しい通信手順が必要になります。
❶ 動作設定の通信手順
ADT7410の動作設定を行う場合、動作設定のレジスタ(アドレス0x03)に1バイトのデータを書き込みます。
0x03に動作設定データを書き込む手順は次のようになります。

最初に、I2C通信を開始するために、PICマイコンはスタートコンディションを生成します。
次にPICマイコンは、「デバイスアドレス7ビット + 読み書き指定1ビット」の1バイトデータをADT7410に送信します。
ADT7410のデバイスアドレスは0x48です。
また動作設定を行うための書き込みですので、「デバイスアドレス + 読み書き設定」は、次のように0x90になります。

PICマイコンがこの1バイトデータを送信した後、ADT7410はACKを返信します。
次にPICマイコンは動作設定レジスタのアドレスである0x03を送信します。PICマイコンが0x03を送信したあと、ADT7410はACKを返信します。
続いてPICマイコンは動作設定の1バイトデータを送信します。この1バイトの中身については次回の記事で詳細に説明します。
PICマイコンが送信した動作設定データをADT7410が受信できると、ADT7410はACKを返信します。
これで、レジスタアドレス0x03に設定データが書き込まれましたので、PICマイコンはI2C通信を終了するためにストップコンディションを生成します。
この通信手順は、いずれの動作もLCDモジュール制御プログラムで作成したI2C通信関数が使用できますので、ADT7410の動作設定プログラムはそれらの関数を使用して作成します。
❷ 温度データ取得の通信手順
LCDモジュールでは、データ送信はホストからデバイスの片方向でしたが、ADT7410ではホストからデバイス(動作設定データ)、デバイスからホスト(温度データ)というように双方向でデータがやり取りされます。
このように、デバイスからホストへのデータ送信手順は初めて出てきますので、最初に概要を説明します。
ADT7410が測定した温度データは、メモリマップのアドレス0x00と0x01に格納されています。
PICマイコンがこのデータを読むには、ADT7410と次のようなやり取りを行います。
- PICマイコンからADT7410に「0x00番地からデータを送って!」と伝える
- ADT7410がPICマイコンに0x00番地に格納されている1バイトデータを返信する
- 続いて0x01番地に格納されている1バイトデータを返信する
❶でPICマイコンが読み取るアドレスを指定すると、❷でそのアドレスのデータ、❸でその次のアドレスのデータ、というようにADT7410は指定されたアドレスから順次データをPICマイコンに返信します。
次に、このやり取りを、I2C通信の最初に指定する「書き込み/読み取り」の観点で表現すると次のようになります。
- PICマイコンからADT7410に、読み取るデータの開始アドレスである「0x00」を書き込む
- PICマイコンが、0x00番地のデータに格納されているを読み取る
- 続けて、PICマイコンが0x01番地に格納されているデータを読み取る
このやり取りのポイントは、通信の途中で、データのやり取りが「書き込み」から「読み取り」に変わっている点です。
I2C通信手順では、スタートコンディションを生成したあと、必ず「デバイスアドレス(7ビット)+ 読み書き指定(1ビット)」を送信します。
上の手順❶は、PICマイコンからADT7410にデータを書き込むわけですから、この読み書き指定の1ビットは「書き込み(0)」となります。
手順❷と❸では、PICマイコンがADT7410からデータを読み取っています。
手順❷と❸では読み書き指定の1ビットは「読み取り(1)」になるはずですが、手順❶で書き込み指定していますので、矛盾が出てきてしまいます。
そこでこのような矛盾がないように、このように通信の途中で読み書きが変更になる場合、変更のタイミングで再度スタートコンディションを発行して通信の仕切り直しをします。
次の図は、上の手順❶から❸のI2C通信手順になります。

通常のI2C通信では、スタートコンディションから始まりストップコンディションで終わります。
一方で上のやり取りのように、通信の途中で書き込みと読み取りが変わる場合、書き込みのあと再度スタートコンディションを発行します。
そのスタートコンディションで読み取り指定を行い、データの読み取りを行って、最後にストップコンディションを発行して通信が終了します。
このように、通信途中で生成するスタートコンディションのことを「リピートスタートコンディション」と呼びます。
リピートスタートコンディションと特別な名称が付いていますが、SCL/SDAの信号パターンはスタートコンディションと同じです。
ただし、PICマイコンのI2Cモジュールは内部処理の関係で、リピートスタートコンディションの生成はスタートコンディションの生成とは別に用意されています。
そのため、プログラム作成では、リピートスタートコンディション用のI2C通信関数を作成しておくことにします。
また読み取り通信では、PICマイコンがADT7410からデータを受信しますので、データ受信の状態(データが正しく受け取れたか)を、PICマイコンから ADT7410に返信する必要があります。
このように、PICマイコンから ADT7410にACK/NACK信号を返信する処理も必要になりますので、PICマイコンのACK/NACK返信用のI2C通信関数も作成します。
ということで上の通信を「リピートスタートコンディション」に修正すると以下のようになります。

なお、このやり取りでは注意点があります。
読み取り通信の最後に、PICマイコンからADT7410に対してACKではなくNACKを返信します。
これはデータの読み取りが最後であることを意味しています。
最後にADT7410のデータシートを確認しましょう!
次の図はデータシートの記載されている、温度データを読み取るI2C通信手順です。
今までの説明と合わせて、通信手順を追ってみていただければと思います。

(このデータシートではストップコンディションがありませんが、省略しているか書き忘れですかね…)
関数の作成
I2C通信のデータ読み取りでは、以下の新しい処理が必要です。そのためこれらの関数を作成しておきます。
- リピートスタートコンディション生成
- PICマイコンのADT7410からのデータ受信
- PICマイコンからADT7410へのACKまたはNACK返信
❶ リピートスタートコンディション生成
リピートスタートコンディションを生成するには、SSP1CON2
レジスタのRSEN
ビットを1にします。
RSEN
に1を設定すると、PICマイコンがSCL信号とSDA信号を制御してスタートコンディションを生成してくれます。
ただし、RSEN
を1にしたあとはPICマイコンがスタートコンディションの生成を終えるまで、待ってあげる必要があります。(待たずに割り込みで処理する方法もありますが、実践編では待つ方法にしています)
PICマイコンはスタートコンディションの生成を終えると、SSP1IF
というレジスタを1にしてくれます。
スタートコンディションを生成するには以下の手順になります。
SSP1IF
を0にしておくSSP1CON2
のRSEN
を1にするSSP1IF
が1になるのを待つ
(PICマイコンがスタートコンディションの生成を終えるのを待つ)SSP1IF
を0に戻しておく
これをプログラムにすると以下のようになります。
この関数は引数、返り値はありません。
// リピートスタートコンディション生成
void i2cProtocolRepeatStart() {
SSP1IF = 0;
SSP1CON2bits.RSEN = 1;
while (SSP1IF == 0) {}
SSP1IF = 0;
}
❷ PICマイコンのADT7410からのデータ受信
PICマイコンがADT7410から1バイトのデータを受信するには以下の手順になります。
SSP1IF
を0にしておくSSP1CON2
のRCEN
を1にするSSP1IF
が1になるのを待つ
(PICマイコンが1バイトのデータ受信を終えるのを待つ)SSP1IF
を0に戻しておく- この時点で受信したデータは
SSP1BUF
レジスタに格納されている
これをプログラムにすると以下のようになります。
この関数は引数はなく、返り値は受信した1バイトデータです。
// 1バイトデータ受信
uint8_t i2cProtocolReceiveData() {
SSP1IF = 0;
SSP1CON2bits.RCEN = 1;
while (SSP1IF == 0) {}
SSP1IF = 0;
return SSP1BUF;
}
❸ PICマイコンからADT7410へのACKまたはNACK返信
PICマイコンからADT7410に対してACK信号を生成するには、SSP1CON2
レジスタのACKDT
とACKEN
を使用します。
ACKDT
(Ack Data)ではACKかNACKかを指定します。ACKの場合は0、NACKの場合は1をセットします。
ACKDT
にACKかNACKかを指定したら、ACKEN
(Ack Enable)に1をセットすると、PICマイコンのI2C通信モジュールがACK/NACK信号を生成してくれます。
ACK/NACK信号を生成する手順は次のようになります。
SSP1CON2
レジスタのACKDT
にACKの場合は0、NACKの場合は1をセットするSSP1CON2
レジスタのACKEN
を1にセットするSSP1IF
が1になるのを待つ
(PICマイコンがスタートコンディションの生成を終えるのを待つ)
関数の引数をACKかNACKにして1つの関数で対応する方法がありますが、ACK返信とNACK返信の2つの関数にしました。(特に深い意味はありません)
// Ack送信
void i2cProtocolSendAck() {
// ACKDTにACKをセット(負論理なので0を設定)
SSP1CON2bits.ACKDT = 0;
// NACK信号生成
SSP1CON2bits.ACKEN = 1;
while (SSP1CON2bits.ACKEN) {}
}
// Nack送信
void i2cProtocolSendNack() {
// ACKDTにNACKをセット(負論理なので1を設定)
SSP1CON2bits.ACKDT = 1;
// NACK信号生成
SSP1CON2bits.ACKEN = 1;
while (SSP1CON2bits.ACKEN) {}
}
更新履歴
日付 | 内容 |
---|---|
2018.8.12 | 新規投稿 |
2025.8.18 | 「マスター」「スレーブ」を「ホスト」「デバイス」に変更 |
返信ありがとうございます
概ね理解しました
お陰で基礎的な事は分かって来たので
無駄に買ってしまった半導体で色々と独自に
実験していこうかと思います
お世話になります
リピートスタートコンディションでスレーブ側が(クロック)信号を受け取り
仕切り直し(リセット)するという事ですが
もしやと思いスタートコンディションでも出来るんではと思ったら
案の定問題なく動作しました
これはデーターが多くなった場合等の為に、リセットして誤作動が起こらない様にする為の仕様という事ですかね?
ご質問どうもありがとうございます。
すみません、記事内の説明が悪かったです。
リピートスタートコンディションはクロックのリセットという意味合いではなく、通信路を占有して通信を継続する、という意味になります。プログラムは1対1で通信していますので、リピートスタートコンディションでなく、スタートコンディションでも動作します。
なお、受信側が受信データが処理できずに、マスターに対して送信を待って欲しいときのために、I2C通信では「クロックストレッチ」という機能があります。これは、スレーブ側がマスター側に、データ通信をちょっと待って、という信号を送る機能です。具体的にはスレーブ側がクロック信号をLOWにします。