PIC解説

【PIC】I2C通信のやり方

投稿日:2020-04-01 更新日:

マイコンで Lチカが出来たら次は LCD に文字を表示したり、色々なセンサーを扱いたいですよね。今回はマイコンと周辺機器の通信によく使われる I2C通信のやり方をちょっとだけ詳しく解説していきます。

I2C通信とは

I2C (IICやI2Cとも) とは Inter-Integrated Circuit の略で、”あい すくえあーど しー”と読みます。普段は面倒なので”あい つー しー”って呼びますけどね。

I2C通信の特徴は、

  • SDA (Serial Data) と SCL (Serial Clock) の2本の線だけで通信ができる
  • 複数のデバイスを同一の線に接続できる(複数のデバイスがあっても線は2本だけ)

といったところですね。I2C と並んでよく使われる通信方式に SPI がありますが、そちらはデバイスごとに1本ずつ追加で線が必要になるので、ピンの少ないマイコンでは I2C通信の方が便利なことが多いと思います。

通信フォーマット

I2C通信では通信する2者のうち一方がマスター、他方がスレーブとなり、マスターがクロックを生成して通信を制御します。

I2C通信には 7-bitモードと 10-bitモードがあるのですが、一般的に使われるのは 7-bitモードの方なので、今回はそちらで説明していきます。

また、クロックの周波数は規格化されていて 100 kHz か 400 kHz で通信を行います。

通信のフォーマットはこのようになっています。

マスターがスタートコンディションを送信し、その後 7 bits のスレーブアドレスと 1 bit の Read/Write (Read:1  Write:0) の 8 bits を送信したら、スレーブから ACK (Acknowledge) が返ってきます。

その後は 8 bits のデータを送信したら ACK を返信するサイクルを繰り返します。図では 2サイクルしか描いていませんが、もっと連続して通信することも可能です。

受信のときは最終データを受信したら NACK (Not Acknowledge) を返信して最終データであることをスレーブにつたえます。

データの送受信が完了したらマスターがストップコンディションを送信して通信を終了します。

関連レジスタ

PIC で I2C通信をするときには、MSSP (Master Synchronous Serial Port) モジュールを使います。型番によっては MSSP を搭載していないものもあるので買う前にデータシートで確認しましょう。また、MSSP を2つ搭載してあるものもあります。

以下のレジスタの x は MSSP が 1つの場合には無視、2つあるときは 1 か 2 に読み替えてください。

ここでは各ビットの簡単な説明はしますが、使わないところは本当に簡単な説明しかないので、詳しく知りたい方はご自分で調べてください。また、マスターとして使うときの初期化の例も紹介します。

SSPxSTAT

各ビットの説明

SMP (SPI Data Input Sample bit)
1:スルーレート制御無効化(100 kHz) 0:有効化(400 kHz)

CKE (SPI Clock Edge Select)
1:SMBus互換入力有効化 0:無効化

D/A (Data / Adress)
1:受信データ=データ 0:アドレス

P (Stop)
1:ストップコンディション検出 0:未検出

S (Start)
1:スタートコンディション検出 0:未検出

R/W (Read / Write information)
(スレーブの場合)
1:Read受信 0:Write受信
(マスターの場合)
1:送信中 0:レディ

UA (Update Adress)
(10-bitモードのみ)
1:更新必要 0:不要

BF (Buffer Full Status)
1:データ転送中 0:転送完了

MSSP モジュールは SPI通信も可能なので SPI と名前のついているビットもありますが、I2C通信時も使うビットとなっています。

SSPxSTAT (SSP Status) レジスタは通信状態を管理するレジスタですが、マスターの場合は自分で通信を制御するのであまり重要ではありません。

設定が必要なのは SMP のみです。100 kHzで使う場合には 1、400 kHz で使う場合には 0 に設定します。
今回は 100 kHzで使いますので、

SSPxSTAT = 0x80;

としておきます。

また、プログラム中では R/W ビットを確認して送信完了を待つのにも使います。

SSPxCON1

各ビットの説明

WCOL (Write Collision Detect)
1:衝突発生 0:正常

SSPOV (Receive Overflow Indicator)
1:オーバーフロー発生 0:正常

SSPEN (SSP Enable)
1:MSSPモジュール有効化 0:無効化

CKP (Clock Polarity Select)
1:ストレッチオフ 0:オン

SSPM (SSP Mode Select)
1110:I2Cスレーブモード 7ビットアドレス 割り込みあり
1000:I2Cマスターモード
0110:I2Cスレーブモード 7ビットアドレス
※他にもモードはあるが割愛

SSPxCON1 (SSP Control 1) レジスタは MSSP モジュールを制御するレジスタで、ここで設定が必要なのは SSPEN と SSPM の2つです。
SSPEN:1、SSPM:1000 とすれば良いので、

SSPxCON1 = 0x28;

とすれば良いでしょう。

SSPxCON2

各ビットの説明

GCEN (General Call Enable)
1:同胞検出許可 0:禁止

ACKSTAT (Acknowledge Status)
1:ACK未受信 0:受信済み

ACKDT (Acknowledge Data)
返信するACK設定
1:NACK 0:ACK

ACKEN (Acknowledge Sequence Enable)
1:ACKDTビットを返信(返信後自動クリア)

RCEN (Receive Enable)
1:受信許可 0:禁止

PEN (Stop Condition Enable)
1:ストップコンディション送信(送信後自動クリア)

RSEN (Repeated Start Condition)
1:リピートスタートコンディション送信(送信後自動クリア)

SEN (Start Condition)
1:スタートコンディション送信(送信後自動クリア)

SSPxCON2レジスタは実際に通信を制御するときに使用するレジスタで、初期化で設定すべきことはありませんので、

SSPxCON2 = 0x00;

としておきましょう。

SSPxCON3

各ビットの説明

ACKTIM (Acknowledge Time Status)
1:ACK中 0:非ACK中

PCIE (Stop Condition Interrupt Enable)
1:Stop割り込み許可 0:禁止

SCIE (Start Condition Interrupt Enable)
1:Start割り込み許可 0:禁止

BOEN (Buffer Overwrite Enable)
1:バッファ上書き許可 0:禁止

SDAHT (SDA Hold Time Selection)
SDA保持時間
1:300 ns 以上 0:100 ns 以上

SBCDE (Slave Mode Bus Collision Detect Enable)
1:スレーブバス衝突検出有効化 0:無効化

AHEN (Adress Hold Enable)
1:アドレス保持有効化 0:無効化

DHEN (Data Hold Enable)
1:データ保持有効化 0:無効化

SSPxCON3 は主にスレーブのときに使用するレジスタで、マスターのときには特に使用しないので、

SSPxCON3 = 0x00;

としておきます。

SSPxADD

スレーブのとき:

SSPxADD<7:1> の 7 bits にスレーブアドレスを設定することができます。

マスターのとき:

マスターのときはアドレスとは関係がなく、クロックの周波数の設定に使われます。

 周波数 = Fosc / (4 * (SSPxADD<7:0> + 1 ))

という計算式ですので、

 SSPxADD = (Fosc / (4 * 周波数)) – 1;

となります。

SSPxBUF

SSPxBUF は通信に使用するバッファです。ここにデータを書き込めば勝手に送信してくれます。受信したデータもここに格納されます。

SSPxMSK

SSPxMSK はスレーブのときに受け取ったスレーブアドレスにマスクをかけて自分のアドレスと照合できるようです。
正直使い道が分かりませんが…

I2C制御ライブラリ

続いてこれらのレジスタを使って I2C通信を制御するライブラリを作成します。1から作るのも良いですがすでに多くの方がネットでライブラリを公開されているので、それをベースに作ろうと思います。

最後にまとめたものを掲載するので、過程はどうでもいいという方は下の方にスクロールしてください。

参考にしたのはこちらのサイトです。I2C通信について細かく書かれていますが、英語をたくさん読むのは面倒なので下の方にあるサンプルプログラムだけ頂いちゃいましょう。

頂いたプログラム

プログラムの内容は上記のレジスタの説明を見れば理解できるかと思います。

とても簡潔ですばらしいのですが、このライブラリは PIC16F877A 用に作られているので、自分の環境に合わせてカスタマイズしていきます。

今回は PIC16F18326 で MSSP2 を使います。

変更点

  • 2行目 SSPCON → SSPCON1 (これはミスだと思うのですが…)
  • 全体 SSP → SSP2
  • 5行目   0 → 0x80
  • 6,7行目 → 削除 (main関数で初期化する)
  • 14行目 SEN → SSP2CON2bits.SEN
  • 18行目 RSEN → SSP2CON2bits.RSEN
  • 22行目 PEN → SSP2CON2bits.PEN
  • 35行目 (a)?0:1 → a

主には MSSP モジュールが 2個あることへの対応です。全体の SSP を SSP2 に変えるのは、エディタの置換機能で全て置換してしまうと良いです。

35行目に関しては、a が 0 なら 1 を代入し、1 なら 0 を代入するというのが、直感的ではないと感じたので a をそのまま代入するようにしました。

これでちゃんと動くと思うのですが、私の環境では動きませんでした…

色々試してみた結果、デフォルトのピンに明示的に割り付けを設定すると上手くいきました。下記プログラムの11~14行目です。
//理由は分からないけどとりあえず動いてるからヨシ
はプログラマーの定番ですよね?この部分の解説はしませんが、もしも私と同様に上手くいかない場合は試してみてください。

以下が完成したライブラリです。

i2c.c と i2c.h をプロジェクトにインポートして、i2c.h をインクルードすればライブラリが使えるようになります。よく分からない場合は i2c.c の関数を main.c にコピペしても問題ありません。

ここまで来たら気を付けなければいけないのはあと1つだけです。

変更前のライブラリにも書いてありましたが、SDA と SCL はデジタルで”入力”にしないといけません。

それでは次回はこのライブラリを使って LCD に文字を表示したいと思います。

【PIC】I2CでLCDに文字を表示する

センサーなどから値を読み取りたい場合にはこちらも参考にどうぞ。

【PIC】I2Cでセンサーの値を読み取る

-PIC解説

執筆者:


  1. Kontrol より:

    貴重な記事ありがとうございます。

  2. 子華 より:

    アドレスマスク(SSPxMSK)は、
    複数スレーブへの同時配信時に使用します。
    (アドレスの割り振り方に工夫が必要です)

    マスクされたアドレスビットがDon’t careとなり、
    マスクされていないアドレスビットのみの一致で
    自分への配信とみなすことができるようになります。

Kontrol へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

関連記事

【PIC】ADCの使い方 サーミスタで温度測定

今回は ADC (Analog to Digital Converter) の使い方を解説します。 PIC の IOピンは通常デジタルの入出力しか出来ませんが、ADC を使用することでアナログ入力(電 …

PIC超入門!ゼロからLチカまでの道【その2:プロジェクト作成~プロパティ設定】

前回 MPLAB X IDE を導入したので、今回はプロジェクトの作成から説明していきます。 前回の記事はこちら PIC超入門!ゼロからLチカまでの道【その1:部品購入~IDE導入】 目次1 プロジェ …

PIC超入門!ゼロからLチカまでの道【その3:コンフィギュレーション~プログラミング】

前回プロジェクトを作成して設定まで終わったのでいよいよプログラミングに入ります。PIC の第一の関門であるコンフィギュレーションも順を追ってやっていけば簡単にできます。 前回の記事はこちら PIC超入 …

【PIC】SPI通信のやり方

多くの PIC に搭載されている MSSPモジュールを使うと I2C と SPI の2つのシリアル通信を行うことができます。 今回は SPI通信のやり方(マスター)を紹介します。 I2C についてはこ …

【PIC】I2Cでセンサーの値を読み取る

電子工作といえば色々なセンサーを使ってみたいですよね。 温度・湿度センサーや加速度センサー、ジェスチャーセンサーなんてものもあります。多くのセンサーは I2C 通信で扱えるので一つやり方を覚えてしまえ …