今回から、PICマイコンのI2C通信のプログラム作成に入ります。
クロック信号の速度
I2C通信でもSPI通信でも、データの送受信はクロック信号の変化のタイミングで読み取る、というルールです。
SPI通信では、クロック信号の速度は特に規定はありませんでした。(ただし、クロック信号があまりにも早すぎるとデバイスが追いつけないケースもありますので限界値はあります)
一方、I2C通信ではクロック信号の速度は標準化されています。
I2C通信のバージョンによって異なりますが、次のように規定されています。単位は、bits/s(1秒あたりに何ビット送信できるか)です。
通称 | 速度(bits/s) |
---|---|
スタンダードモード | 100k |
ファストモード | 400k |
ハイスピードモード | 3.4M |
このようにクロックの速度は標準化されていますが、これ以外の速度では通信できない、というわけではありません。
ただ、実践編では初めてのI2C通信プログラムということで、標準のスタンスダードモード(100k bits/s)でプログラムを作成します。
クロック信号は速い方が良いですが、速くすればするほど電気的な信号は乱れてきます。
このような状況をなるべく避けるために、PICマイコンでは、波形がなるべく乱れないようにクロック信号とデータ信号の波形を調整する機能があります。
説明の流れ
I2C通信のプログラムは、PICマイコンに内蔵されているI2C通信機能を使用します。
I2C通信のプログラムは今まで出てきた他の機能と同様、最初にピンのデジタル/アナログ設定、入出力設定やその他関連する機能の設定などの初期設定が必要です。
初期設定後、I2C通信ができるようになります。
I2C通信の基本的な通信の流れは、スタートコンディションの生成から始まり、デバイスアドレスの送信、データの送受信、ストップコンディションの生成、という順番です。
そこで、これらの通信を関数にしてプログラムを作成することにします。
実際にデバイスとI2C通信を行う際、そのデバイスの通信手順に合わせて、これらの関数を呼び出すプログラムにします。
今回の記事では、次の流れで説明します。
- 1. ピンの設定
- 2. I2C通信機能設定(初期設定)
- 3. I2C通信関数の作成
- 3-1. 概要
- 3-2. I2C通信制御レジスタ(SSP1CON2)
- 3-3. スタートコンディション関数
- 3-4. ストップコンディション関数
- 3-5. データ送信関数
- 3-6. ACK/NACK受信関数
1. ピンの設定
I2C通信に使用するピンはクロック信号ピン(SCLピン)とデータ信号ピン(SDAピン)です。
これらのピンは次の設定にします。
ピン | デジタル/アナログ設定 (ANSELレジスタ) | 入出力設定 (TRISレジスタ) |
---|---|---|
クロック信号 | デジタル | 入力 |
データ信号 | デジタル | 入力 |
クロック信号はホストが出力しますし、データ信号は入出力になりますので、両方とも「入力」に設定するのはちょっと不思議ですよね。
PICマイコンのI2C通信機能を利用するとき、仕様上このように設定することになっています。
ピンこのように初期設定後、I2C通信を有効にすると、それぞれのピンは正しく動作するようになります。
プログラムとしては以下のように設定することにします。
ANSELC = 0b00000000;
TRISC = 0b00001100;
2. I2C通信機能設定(初期設定)
I2C機能の設定に関するレジスタは以下の4個です。
レジスタ | 設定内容 |
---|---|
❶SSP1STAT | 信号波形の調整、スターテス設定 |
❷SSP1CON1 | 通信モジュールの有効化、通信種類設定 |
❸SSP1CON3 | 通信モジュールの詳細設定 |
❹SSP1ADD | 通信速度設定 |
なんだか心が折れそうですが、一つ一つ紐解いてしていきましょう!
まず、レジスタ名をよく見ると、全て「SSP1」で始まっています。
設定レジスタ理解のための最初の一歩は、この「SSP1」の意味の解明からです。
今までの説明では「PICマイコンの中にはI2C通信モジュールが2個あります」という説明をしてきました。次のようなイメージです。

この説明は正確ではなく、実はこれらのモジュールは、ひとつのモジュールでSPI通信とI2C通信の両方をサポートしているんです。

このモジュールことを「MSSPモジュール」と呼んでいます。
MSSPは「Master Synchronous Serial Port」の略で、日本語としては「マスター(ホスト)同期型のシリアルポート」という意味になります。
この用語について「マスター同期型」と「シリアルポート」の二つに分けて意味を説明します。
今まで説明してきましたSPI通信やI2C通信は、デバイス(マスター)がクロック信号を生成して、全てのデバイスはそのクロック信号に合わせてデータのやり取りを行います。
これは、「マスター(デバイスのクロック信号に)同期(して通信を行う)型」という意味合いです。
また「シリアルポート」とは、以前の記事でシリアル通信を説明しましたが、その「シリアル通信を行うポート」という意味になります。
PIC16F18857には、MSSPモジュールが2個搭載されています。
レジスタ名で表現する場合、これら2個のモジュールに対して「SSP1」「SSP2」という名称がつけられています。「SSP1」は「MSSPモジュール1」に関するレジスタ、ということになります。
また、データシートを調べる場合「SSP1STAT」という用語は使われないこともある点に注意してください。(データシートのPDFを「SSP1STAT」で検索すると探している情報が見つからないことがあります)
データシートでは、「SSPxSTAT」という用語が使われています。
モジュール1の「SSP1STAT」もモジュール2の「SSP2STAT」も同じ内容になりますので、数字の部分を「x」として、「SSPxSTAT」という用語になっています。(他のレジスタについても同様です)
これから上の表のそれぞれのレジスタの設定内容を説明します。
設定項目がかなり多いですが、通常のI2C通信を行う場合、一般的な設定がありますので「とりあえずこう設定しておけば動く」という設定値も記載します。
また、これらのレジスタはSPI通信とI2C通信兼用です。
データシートには「SPI通信の時はこういう意味の設定、I2C通信の時はこういう意味の設定になる」と記載されていますが、以下ではI2C通信のみの説明になります。
なお、PICマイコンのMSSPモジュールは、I2C通信のホストとデバイス両方に対応していますが、これから説明する内容は、ホストのみの説明になります。
❶ SSP1STATレジスタ
「SSP1 Status Register」は SSP1モジュールのステータス(通信状態)が格納されるレジスタです。
このレジスタを調べると現在の通信状況がわかります。(一部のビットは設定にも使用されています)
レジスタの構成は以下のようになっています。

それぞれの設定を詳しく説明します。
SMP:SPI Data Input Sample bit
このビットはSPIの名前が付いていますが、I2C通信の場合は次の内容になります。
設定値 | 設定内容 |
---|---|
1 | 信号波形をスタンダードモード(100kHz)用に調整 |
0 | 信号波形をファストモード(400kHz)用に調整 |
実践編ではクロック信号を100kHzに設定しますので、SMPは1に設定します。
CKE:SPI Clock Edge Select bit
このビットSPIの名前が付いていますが、I2C通信の場合は次の内容になります。
設定値 | 設定内容 |
---|---|
1 | 入力回路部をSMBus用に設定する |
0 | 入力回路部は通常用に設定する |
突然「SMBus」という用語が出てきました。
これはI2C通信の派生仕様の通信方法になります。
通常のI2C通信の場合は0に設定します。
D/A:Data/Address bit
このビットは、最後に送受信し値がデータだったのかアドレスだったのかの状態を示すビットです。
I2C通信でホストのプログラムを作成する場合、データの送受信は自分で管理しますので、このビットを見ることはないと思います。
このビットの値を調べると以下のことがわかります。このビットは状態を示すビット(=読み取り専用ビット)ですので値を設定しても無効です。
値 | 意味 |
---|---|
1 | 最後に送受信した値はデータ |
0 | 最後に送受信した値はアドレス |
P:Stop bit
このビットは、ストップコンディションが検出されたかどうかを示すビットです。
I2C通信でホストのプログラムを作成する場合、データの送受信は自分で管理しますので、このビットを見ることはないと思います。
このビットの値を調べると以下のことがわかります。
このビットは状態を示すビット(=読み取り専用ビット)ですので値を設定しても無効です。
値 | 意味 |
---|---|
1 | ストップコンディションが検出された |
0 | ストップコンディションが検出されていない |
S:Start bit
このビットは、スタートコンディションが検出されたかどうかを示すビットです。
注意事項はP(Stop bit)と同じです。
値 | 意味 |
---|---|
1 | スタートコンディションが検出された |
0 | スタートコンディションが検出されていない |
R/W:Read/Write bit information
このビットはデータ送信中かどうかを示すビットです。
I2C通信のホストの場合は以下の意味になります。
値 | 意味 |
---|---|
1 | データ通信中 |
0 | データ通信はしていない |
I2C通信でデータを送受信する場合、送受信が終わったかどうかを判定する方法は別にあります。
通常のI2C通信ブログラムを作る場合、このビットを見ることはそれほどないと思います。
このビットは状態を示すビット(=読み取り専用ビット)ですので値を設定しても無効です。
UA:Update Address bit
このビットは、I2Cデバイスアドレスを10ビットモードで使用しているとき、アドレス状況を示すビットです。
10ビットアドレスは使用しませんので説明は省略します。
このビットは状態を示すビット(=読み取り専用ビット)ですので値を設定しても無効です。
BF:Buffer Full Status bit
このビットは、データ送受信する一時的なデータ保存場所(バッファ)に、データがあるかないかの状態を示すビットです。
I2C通信でデータを送受信する場合、送受信が終わったかどうかを判定する方法は別にあります。
そのため、通常のI2C通信ブログラムを作る場合、このビットを見ることはそれほどないと思います。
このビットは状態を示すビット(=読み取り専用ビット)ですので値を設定しても無効です。
値 | 意味 |
---|---|
1 | データがまだ残っている (データ送信中) |
0 | データは残っていない (データ送信完了) |
なお、先ほどのR/Wビットでもデータ送受信完了はわかりますが、BFビットはACK/NACK信号は含んでいません。
R/Wビットは、「8ビットのデータ+1ビットのACK/NACK信号」の送受信が終わったかどうかがわかります。
BFは「8ビットのデータの送受信」(ACK/NACK信号は含まない)が終わったかどうかがわかります。
一般的なI2C通信のマスターモードの場合、SSPxSTATレジスタは以下の設定のどちらかになります。
SSP1STAT = 0x80; // クロック信号が100kHzの場合
SSP1STAT = 0x00; // クロック信号が400kHzの場合
「SSP1CON1」レジスタ
「SSP1CON1」は「SSP1 Control Register 1」の略で、SSP1モジュールの設定を行うレジスタです。
設定項目が多いため、他にもう2つのレジスタ(SSP1CON2とSSP1CON3)があります。
レジスタの構成は以下のようになっています。それぞれの設定を詳しく説明します。

WCOL:Write Collision Detect bit
このあとに説明しますが、I2C通信でデータを送信する場合、その送信するデータを特定のレジスタに代入するとPICマイコンがそのデータを送信してくれます。
具体的には、例えば0x12
を送信したい場合、次のようにSSP1BUF
というレジスタに0x12
を代入するだけでPICマイコンがクロック信号とデータ信号を制御してI2C通信を行ってくれます。
SSP1BUF = 0x12;
データを複数バイト送信したい場合、SSP1BUFに代入した値が送信完了になるまで、次の値はSSP1BUF
に代入できません。
このWCOLビットは、SSP1BUF
に代入した値が送信完了したかどうかの状況を示します。このビットの値を調べると以下のことがわかります。このビットは状態を示すビット(=読み取り専用ビット)ですので値を設定しても無効です。
ただ、送信完了したかどうかは別の方法でチェックしますので、実践編ではこのビットはチェックしません。
値 | 意味 |
---|---|
1 | SSP1BUFの値は送信完了していない (次の送信データが代入できない) |
0 | SSP1BUFの値は送信完了した (次の送信データが代入できる) |
SSPOV:Receive Overflow Indicator bit
I2C通信でデータを受信する場合、受信したデータはSSP1BUF
レジスタに格納されています。
複数バイトを受信する場合、受信するレジスタはSSP1BUF
の1バイトしかありませんので、この値を取得しないうちに次のデータを受信すると、取得できないデータが出てきてしまいます。
このビットは、SSP1BUF
に受信したデータが保持されているのにも関わらず、次のデータを受信してしまったかどうかの状況を示します。(この状況をオーバーフローと呼びます)
このビットの値を調べると次のことがわかります。
値 | 意味 |
---|---|
1 | 受信したデータをSSP1BUFに保持している間に 次のデータを受信した(オーバーフローが発生した) |
0 | オーバーフローは発生していない |
このビットは状態を示すビット(=読み取り専用ビット)ですので値を設定しても無効です。
実践編ではこのビットはチェックしない方法でプログラムを作成します。
SSPEN:Synchronous Serial Port Enable bit
このビットは非常に重要です。
このビットでSSP1モジュールを有効にするかどうかの設定をします。
SSP1モジュールはSPI通信やI2C通信のホスト、デバイスの両方のモードをサポートしています。
このビットでSSP1モジュールを有効にした場合、このあとに出てくるSSPMビットで通信の種類を選択します。
このビットは次の意味になります。
値 | 意味 |
---|---|
1 | SPI/I2C通信を有効にする 割り当てたピンはSSP1モジュールに接続される |
0 | SPI/I2C通信を無効にする 割り当てたピンは通常の入出力ピンとして動作する |
CKP:Clock Polarity Select bit
このビットはI2C通信のホストでは使用しません。
I2C通信のデバイスで使用するビットですが、簡単に説明します。
I2C通信を行っているとき、ホストから送られてくるデータ量が多いとデバイス側で処理が追いつかないことがあります。
そのようなときは、デバイス側で「データ送るのちょっと待って!」と言いたいですよね。
このようなとき、デバイス側でこのビットを0にすると、その間ホストはデータ送信を一時停止してくれます。
PICマイコンがデバイスの場合、0を設定しておけばOKです。
SSPM:Synchronous Serial Port Mode Select bit
先ほどのSSPENでSSP1モジュールを有効にした場合、通信の種類はどれにするか、このビットで選択します。
一般的なI2C通信のホストの場合は「1000」を選択します。
次の意味になります。
値 | 意味 |
---|---|
1111 | I2C通信デバイス(10ビットアドレス) スタート/ストップ時に割り込み信号発生 |
1110 | I2C通信デバイス(7ビットアドレス) スタート/ストップ時に割り込み信号発生 |
1101 | - |
1100 | - |
1011 | I2C通信ホスト(ファームコントロール) |
1010 | SPI通信ホスト |
1001 | - |
1000 | I2C通信ホスト |
0111 | I2C通信デバイス(10ビットアドレス) |
0110 | I2C通信デバイス(7ビットアドレス) |
0101 | SPI通信デバイス (チップセレクト信号は使用しない) |
0100 | SPI通信デバイス (チップセレクト信号は使用する) |
0011 | SPI通信ホスト(クロック信号はタイマー使用) |
0010 | SPI通信ホスト(クロック信号は周波数の64分の1を使用) |
0001 | SPI通信ホスト(クロック信号は周波数の16分の1を使用) |
0000 | SPI通信ホスト(クロック信号は周波数の4分の1を使用) |
一般的なI2C通信のホストでは次の設定になります。
SSP1CON1 = 0x28;
「SSP1CON3」レジスタ
SSP1CON2レジスタを飛ばして、SSP1CON3レジスタです。
SSP1CON2レジスタは設定用ではなく、I2C通信のデータ通信制御(スタート/ストップコンディションの制御、データ送受信制御)を行うレジスタです。
機能の設定はSSP1CON1とSSPCON3で行います。(ちょっと謎ですが…)
SSP1CON3レジスタの構成は次のようになっています。

それぞれの設定を詳しく説明します。
ACKTIM:Acknowledge Time Status bit
I2C通信では8ビットデータを送信または受信したあと、1ビットのACK/NACK情報をやりとりします。
このビットはそのACK/NACK信号のやり取り中かどうかの状況を示します。
状態を示すビットですので、設定は無効です。
次の意味になります。
値 | 意味 |
---|---|
1 | ACK/NACK信号処理中 |
0 | ACK/NACK信号は処理していない |
PCIE:Stop Condition Interrupt Enable bit
SCIE: Start Condition Interrupt Enable bit
PCIEとSCIEは同じような内容の設定になりますのでまとめて説明します。
I2C通信では通信の開始はスタートコンディション、終了はストップコンディションの信号状態を作ります。
PICマイコンをI2C通信のデバイスとして動作させる場合、通信の開始と終了を監視する必要がありますが、プログラムでチェックするのは大変です。
そこで、スタート/ストップコンディションが発生した場合、割り込み信号生成してくれる機能があります。
これらのビットはその割り込み信号を発生するかどうかの設定です。
次の意味になります。
値 | 意味 |
---|---|
1 | 割り込み信号を発生する |
0 | 割り込み信号を発生しない |
実践編ではPICマイコンはマスターですので、割り込みは設定しません。
BOEN:Buffer Overwrite Enable bit
このビットはデバイスの時に有効なビットで、受信データのレジスタを上書き許可するかどうかの設定になります。
実践編ではホストで使用しますので説明は省略します。
ホストの場合は設定は無効になりますので、0を設定しておきます。
SDAHT:SDA Hold Time Selection bit
I2C通信ではデータを送信する際、データ信号線にデータをセットして、クロック信号を変化させてデータを読み取ってもらいます。
具体的には、データ信号をセット、クロックを立ち上げて、クロックを立ち下げて次のデータをセット、という流れです。
データ受信側は、クロック信号が変化した瞬間にデータを読み取ることは難しく、クロック信号が変化したことを確認してからデータを読み取ります。
そのため、クロック信号を立ち上げて、次にクロック信号を立ち下げたあとに次のデータをセットしますが、データ読み取りがのんびりしているデバイスがいるかもしれません。
その場合は、データを保持しておく時間をちょっと長めにしてあげることが必要になるケースもあります。
このビットは、クロック信号を立ち下げてから、どのぐらいの時間データ信号を保持しておくかを設定します。
次の意味になります。
値 | 意味 |
---|---|
1 | クロック信号を立ち下げてから、 少なとも300nsはデータを保持する |
0 | クロック信号を立ち下げてから、 少なとも100nsはデータを保持する |
実践編のプログラムのクロック速度は100kHzで作成します。
クロック信号の速度が100kHzというのはかなりゆっくりです。
この設定は100nsでも問題ありませんので、0に設定することにします。
SBCDE:Slave Mode Bus Collision Detect Enable bit
このビットは、PICマイコンをデバイスにしたときのデータ信号の衝突(他のI2Cモジュールと同時にデータ通信を行ってしまう)を検出するかどうかの設定です。
ホストの場合、設定は無効になりますので説明は省略します。
設定は0にします。
AHEN:Address Hold Enable bit
DHEN: Data Hold Enable bit
これらのビットは、I2C通信のデバイスの時に有効な設定です。
ホストでは関係ありませんので詳しい説明は省略します。
デバイスの時に、アドレスを受信してそのアドレスが自分のアドレスであった場合や、データを受信した場合、何か処理する必要があります。
ホストは次々にデータを送ってきますが、そのデータ処理が追いつかない場合、ホストに「ちょっと待ってて」と知らせることができます。その「ちょっと待ってて」をするかどうかの設定になります。
ホストの場合は設定は無効になりますので、0を設定しておきます。
SSP1CON3レジスタの設定は次のようになります。
SSP1CON3 = 0x00;
「SSP1ADD」レジスタ
このレジスタは、I2Cのホストとデバイスで意味が異なります。
ホストの場合はクロック信号の速度設定、デバイスの場合はI2Cデバイスアドレスを設定します。
ホストの場合、クロック信号の速度は次の式で計算される値になります。
クロック信号の速度(周波数)= PICマイコンの動作周波数 ÷ ( (SSP1ADDの値+1)× 4)
実践編では、PICマイコンの動作周波数は4MHz、I2Cのクロック信号の速度はスタンダードモードの100kHzにします。
SSP1ADDの値を求めます。
最初に上の式を変形します。
SSP1ADD = (PICマイコン動作周波数 ÷ (I2Cクロック信号周波数 × 4)) - 1
値を代入して計算すると次のようになります。
SSP1ADD=4MHz ÷ (100kHz × 4)- 1 = 10 - 1 = 9
まとめると、SSP1ADDの値を9に設定すると、I2Cクロック周波数は100kHzになります。
この値はPICマイコンの動作周波数とI2Cクロック周波数で決まりますので、一般的な設定というものはなく、それぞそれのケースで計算することになります。
実践編では次のように設定します。
SSP1ADD = 0x09;
I2C通信機能設定まとめ
今までの設定をまとめます。I2C通信のマスターでクロック信号の速度が100kHzの場合、以下の設定になります。
ANSELC = 0b00000000; // アナログ/デジタル設定
TRISC = 0b00001100; // 入出力設定
SSP1STAT = 0x80; // クロック信号は100kHzを使用
SSP1CON1 = 0x28; // I2C通信のマスターモードを有効化
SSP1CON3 = 0x00; // CON3はデフォルト設定
SSP1ADD = 0x09; //クロック信号速度を100kHzに設定
以上でI2C通信の設定(MSSP1モジュールの設定)が終わりました。
ここでコーヒーブレークを入れて、次はI2C通信の関数を作っていきましょう!
3. I2C通信関数の作成
3-1. 概要
I2C通信のデータ送受信は、次の流れで行います。

そこで、それぞれのステップで行う処理を関数化しておくことにします。
関数名は、次のようにi2cProtocol
で始まる名前にして、その後はそれぞれの信号制御の名前にしました。

注意点が2点あります。
一つ目は、「デバイスアドレス送信」と「データ送信」は目的は異なるものの、処理としては8ビットのデータ送信です。
そのため、デバイスアドレス送信の関数とデータ送信の関数、というように2つの関数は作成せずに、8ビットデータを送信する関数として作成します。
二つ目は、上のデータ通信はホストからデバイスに2バイトのデータを送信する例です。
実際にはデバイスからホストにデータを送信するモジュールもあります。(実践編で使用する温度センサーなど)
デバイスからホストにデータ送信する関数は温度センサのプログラムを説明する際に作成することにします。
3-2. I2C通信制御レジスタ(SSP1CON2)
PICマイコンでI2C通信でデータ通信を制御するには、SSP1CON2
レジスタを制御します。
SSP1CON2レジスタは次の構成になっています。

GCEN:General Call Enable bit
このビットはPICマイコンがデバイスのときの設定ですので詳細な説明は省略します。
I2C通信のデバイスアドレス0x00
は特殊なアドレスとして定義されていて、ホストからデバイスアドレスとして0x00
が指定された場合のデバイスの挙動を設定するビットです。
ACKSTAT:Acknowledge Status bit
このビットは、ACK/NACKの状態を表します。
8ビットのデータ送信を行なった後、送信先から「了解信号」、つまりACK/NACK信号が返信されてきます。
その返信されてきた信号がACKかNACKのどちらかかを示すビットです。
次の意味になります。
値 | 意味 |
---|---|
1 | NACK |
0 | ACK |
ACKDT:Acknowledge Data bit
上のACKSTATビットは、ACK/NACKの状態を示すビットでした。
このACKDTビットは、ACK/NACKを返信するビットです。
8ビットのデータ受信を行なった後、問題なく受信できた場合は送信先に対して「了解信号」、つまりACK信号を返信する必要があります。
このビットでその返信する信号を設定します。
注意点としては、このビットはあくまでACK/NACKの信号を設定するビットです。
実際にACK/NACKの返信は次のACKENビットを操作することにより行います。
次の意味になります。
値 | 意味 |
---|---|
1 | NACK |
0 | ACK |
ACKEN:Acknowledge Sequence Enable bit
このビットを制御することにより、先ほどのACKDTビットで指定したACK/NACK信号を生成します。
次の意味になります。
値 | 意味 |
---|---|
1 | このビットを1にすると、PICマイコンはSDA信号とSCL信号を制御してACKDTで指定したACK/NACK信号を生成します。 処理が終わるとこのビットは自動的に0に設定されます |
0 | ACK/NACK信号生成を行なっていないことを意味します |
RCEN:Receive Enable bit
このビットを制御することにより、データ受信を行います。
このビットを操作してデータ受信を行う関数は、温度センサの制御プログラムを作成する際に出てきます。
今回の記事で作成する関数には出てきませんが、後ほど使用しますのでここで確認しておきます。
次の意味になります。
値 | 意味 |
---|---|
1 | このビットを1にすると、PICマイコンはデバイスから返信されてくる8ビットデータを受信します。 処理が終わるとこのビットは自動的に0に設定されます |
0 | データ受信処理を行なっていないことを意味します |
PEN:Stop Condition Enable bit
このビットを操作することにより、ストップコンディションを生成します。
次の意味になります。
値 | 意味 |
---|---|
1 | このビットを1にすると、PICマイコンはSCL信号とSDA信号を制御してストップコンディションを生成します。 処理が終わるとこのビットは自動的に0に設定されます |
0 | ストップコンディション生成を行なっていないことを意味します |
RSEN:Repeated Start Condition Enable bit
今までの説明では、I2C通信はスタートコンディションから始まり、ストップコンディションで終わる、と説明してきました。
一般的な通信はこのような通信方法になりますが、他の通信手順もあります。
センサーなどのI2Cデバイスでは、最初に読み出したいレジスタのアドレスを送信して、その後そのレジスタのデータを受信するという手順になります。
この送信と受信の間にストップコンディションを発行してしまうと、デバイスによっては「一連の操作が完了した」と誤解して、どのレジスタのデータを返信するればよいか忘れてしまうことがあります。
このようなケースでは、「デバイスから読み出したいレジスタのアドレスを送信」と「そのレジスタのデータを受信」間で、ストップコンディション、スタートコンディションを発行するのではなく、このリピートスタートコンディションを発行することにより仕切り直しをすることができます。
このビットを操作することにより、リピートスタートコンディションを生成します。
次の意味になります。
値 | 意味 |
---|---|
1 | このビットを1にすると、PICマイコンはSCL信号とSDA信号を制御してリピートスタートコンディションを生成します。 処理が終わるとこのビットは自動的に0に設定されます |
0 | リピートスタートコンディション生成を行なっていないことを意味します |
SEN:Start Condition Enable bit
このビットを操作することにより、スタートコンディションを生成します。
次の意味になります。
値 | 意味 |
---|---|
1 | このビットを1にすると、PICマイコンはSCL信号とSDA信号を制御してスタートコンディションを生成します。 処理が終わるとこのビットは自動的に0に設定されます |
0 | スタートコンディション生成を行なっていないことを意味します |
以下に、これらのレジスタを制御してI2C通信の制御関数を作成します。
3-3. スタートコンディション関数
スタートコンディションを生成するには、SSP1CON2レジスタのSENビットを1にします。
SENに1を設定すると、PICマイコンがSCL信号とSDA信号を制御してスタートコンディションを生成してくれます。
SENを1にしてからPICマイコンがスタートコンディションの生成を終えるまで、待ってあげる必要があります。(待たずに割り込みで処理する方法もありますが、処理が難しくなるので実践編では待つことにします)
PICマイコンはスタートコンディションの生成を終えると、SSP1IF
というビットを1にしてくれます。
スタートコンディションを生成するには以下の手順になります。
SSP1IF
を0にしておくSSP1CON2
のSEN
を1にする
これをきっかけにPICマイコンはスタートコンディションを生成しますSSP1IF
が1になるのを待つSSP1IF
を0に戻しておく
これをプログラムにすると以下のようになります。
// スタートコンディション生成
void i2cProtocolStart() {
// SSP1CON2レジスタのSENビットを1に設定するとスタートコンディションが生成される
// 発行が完了するとSSP1IFが1になるのでwhile文で待つ
SSP1IF = 0;
SSP1CON2bits.SEN = 1;
while (SSP1IF == 0) {}
SSP1IF = 0;
}
3-4. ストップコンディション関数
ストップコンディションの生成は、スタートコンディションを同様の手順になります。
SSP1IF
を0にしておくSSP1CON2
のPEN
を1にする
これをきっかけにPICマイコンはスタートコンディションを生成しますSSP1IF
が1になるのを待つSSP1IF
を0に戻しておく
これをプログラムにすると以下のようになります。
// ストップコンディション生成
void i2cProtocolStop() {
// SSP1CON2レジスタのPENビットを1に設定するとストップコンディションが生成される
// 発行が完了するとSSP1IFが1になるのでwhile文で待つ
SSP1IF = 0;
SSP1CON2bits.PEN = 1;
while (SSP1IF == 0) {}
SSP1IF = 0;
}
3-5. データ送信関数
データ送信はSSP1BUFレジスタに送りたいデータを代入するだけで、あとはPICマイコンがSCL信号とSDA信号を制御してデータ送信してくれます。
なお、スタート/ストップコンディションと同様に、データ送信が終わるまで待ってあげる必要がありますので、SSP1IF
が1になるのを待ちます。
データ送信の手順は以下のようになります。
SSP1IF
を0にしておくSSP1BUF
に送りたいデータをセットする
これをきっかけにPICマイコンはSSP1BUF
のデータを送信しますSSP1IF
が1になるのを待つSSP1IF
を0に戻しておく
これをプログラムにすると以下のようになります。
// 1バイトデータ送信
void i2cProtocolSendData(uint8_t data) {
// SSP1BUFに送信したいデータをセットすると、そのデータが送信される
// 発行が完了するとSSP1IFが1になるのでwhile文で待つ
SSP1IF = 0;
SSP1BUF = data;
while (SSP1IF == 0) {}
SSP1IF = 0;
}
3-6. ACK/NACK状態確認関数
Ack/Nackの状態は、SSP1CON2レジスタのACKSTAT
ビットにセットされます。
ACKの場合が0、NACKの場合が1です。
つまり、SSP1CON2bits.ACKSTAT
の値を調べればすぐにわかるのですが、一連のI2C通信関数を作成していますので、Ack/Nack状態確認も関数にします。
特に意味はありませんが、ACKを0x00、NACKを0xFFに定義して、その値を返り値にすることにしました。(boolean型の方がいいかもしれません)
// I2C Ack/Nack定義
#define I2C_ACK 0x00
#define I2C_NACK 0xff
// Ack/Nackチェック
uint8_t i2cProtocolCheckAck() {
uint8_t ackStatus;
if (SSP1CON2bits.ACKSTAT) {
ackStatus = I2C_NACK;
} else {
ackStatus = I2C_ACK;
}
return ackStatus;
}
以上で必要なI2C通信を行う関数ができました。
次回から液晶モジュールの制御プログラムの作成に入ります。
更新履歴
日付 | 内容 |
---|---|
2018.6.22 | 新規投稿 |
2025.8.11 | 説明補足 |
pic16f18857 ICで i2cデーター送受中に外部から割り込みがはいるとssp1ifが立たなくなりますが、有効にさせるにはどのようにすれば良いでしょうか?
またssp1ifの代わりのPEN,RSEN,SENを代用は可能でしょうか?
ご質問ありがとうございます。
実際のプログラム動作状況によりますが、I2C通信でデータ受信バッファがオーバーフローしている可能性が考えられます。
(データ受信中に割り込み処理が入り、その間にデータ受信バッファに次のデータが来てしまう、という状況です)
解決策としては、基本的なものは次の内容になります。
・割り込み処理を極力短時間にする
割り込み処理で時間がかかる処理をしている場合は、グローバル変数でフラグを用意して、割り込み処理内ではそのフラグを立てるだけにします。
割り込み処理で行いたいことは、I2C通信を処理しているメインのプログラム内で処理するようにします。
このようにすると、I2C通信と時間的に排他処理ができるようになります。
・I2C通信のデータ送受信中は割り込みを禁止する
I2C通信でスタートコンディションからストップコンディションまで、割り込みを禁止する、という方法です。
ただ、割り込み処理がクリティカルな処理の場合は別途プログラム構造を見直す必要があるかな、と思います。
なお、PEN,RSEN,SENはI2Cマスターの場合は動作を指示するフラグですので、基本的にはこれらを使うのは難しいと思います。
大変有り難うございます。
ご指摘と通り割り込みがちょっと長いようですので変更し確認してみます。
大変有り難うございました。
管理者様:大変お世話になります。
i2c確認した所、PIR3のBCL2IFでバス衝突確認されました。
この場合の処理として、I2Cに送り込む作業を一番最初からやり直しとなるのでしょうか?
(この時の注意点等有りましたらご伝授の程)
それともリピートスタートコンディションを使って再開させるのでしょうか?
改めてよろしくお願い申し上げます。
I2Cのバス衝突があったんですね。
I2Cのバス衝突が発生した場合、リピートスタートコンディションで復活できる可能性もありますが、受信バッファのデータが不完全の可能性が高いです。
基本的にはI2C通信をやり直すのが適当です。
通信をやり直す方法としては、STOPコンディションを発行して再度通信を再開するか、STOPが効かない場合はI2C2CON0bits.ENで一度I2Cを無効化して有効化するのがよいかな、と思います。