マイクロチップはこれについてのアプリケーションノートを書きました:
- I2Cスレーブの実装に関するAN734
- I2Cマスターの実装に関するAN735
- 環境監視用のネットワークプロトコルの設定に関するより理論的なAN736もありますが、このプロジェクトでは必要ありません。
アプリケーションノートはASMで動作していますが、Cに簡単に移植できます。
マイクロチップ社の無料のC18およびXC8コンパイラには、I2C関数があります。これらの詳細については、コンパイラライブラリのドキュメントのセクション2.4をご覧ください。ここにいくつかのクイックスタート情報があります:
セットアップ
すでにマイクロチップ社のC18またはXC8コンパイラを使用しています。どちらにもI2C関数が組み込まれています。それらを使用するには、以下を含める必要がありますi2c.h
。
#include i2c.h
ソースコードを見たい場合は、ここで見つけることができます:
- C18ヘッダー:
installation_path
/v
x.xx
/h/i2c.h
- C18ソース:
installation_path
/v
x.xx
/src/pmc_common/i2c/
- XC8ヘッダー:
installation_path
/v
x.xx
/include/plib/i2c.h
- XC8ソース:
installation_path
/v
x.xx
/sources/pic18/plib/i2c/
ドキュメントで/i2c/
は、関数が配置されているフォルダー内のどのファイルにあるかを確認できます。
接続を開く
マイクロチップ社のMSSPモジュールに精通している場合は、最初に初期化する必要があることがわかります。OpenI2C
関数を使用して、MSSPポートでI2C接続を開くことができます。これは、次のように定義されています。
void OpenI2C (unsigned char sync_mode, unsigned char slew);
を使用sync_mode
すると、デバイスがマスターであるかスレーブであるかを選択でき、スレーブである場合は、10ビットアドレスと7ビットアドレスのどちらを使用するかを選択できます。ほとんどの場合、特に小さなアプリケーションでは7ビットが使用されます。オプションは次のsync_mode
とおりです。
SLAVE_7
-スレーブモード、7ビットアドレス
SLAVE_10
-スレーブモード、10ビットアドレス
MASTER
-マスターモード
ではslew
、デバイスでスルーレートを使用するかどうかを選択できます。ここにあるものの詳細:I2Cのスルーレートとは何ですか?
2つのMSSPモジュール
PIC18F46K22のように、2つのMSSPモジュールを備えたデバイスには特別なものがあります。これらには、モジュール1用とモジュール2用の2つの関数セットがあります。たとえば、の代わりにおよびOpenI2C()
がOpenI2C1()
ありopenI2C2()
ます。
さて、あなたはそれをすべてセットアップし、接続を開きました。次に、いくつかの例を見てみましょう。
例
マスター書き込み例
I2Cプロトコルに精通している場合、一般的なマスター書き込みシーケンスは次のようになります。
Master : START | ADDR+W | | DATA | | DATA | | ... | DATA | | STOP
Slave : | | ACK | | ACK | | ACK | ... | | ACK |
最初に、START条件を送信します。これを電話を取り上げることを検討してください。次に、アドレスを書き込みビットで-番号をダイヤルします。この時点で、送信されたアドレスを持つスレーブは、自分が呼び出されていることを認識しています。彼は謝辞( "Hello")を送信します。これで、マスターデバイスはデータを送信できます-彼は話し始めます。彼は任意の量のバイトを送信します。各バイトの後、スレーブは受信したデータにACKを送信する必要があります(「はい、あなたの言う通りです」)。マスターデバイスが通信を終了すると、STOP状態でハングアップします。
Cでは、マスター書き込みシーケンスはマスターに対して次のようになります。
IdleI2C(); // Wait until the bus is idle
StartI2C(); // Send START condition
IdleI2C(); // Wait for the end of the START condition
WriteI2C( slave_address & 0xfe ); // Send address with R/W cleared for write
IdleI2C(); // Wait for ACK
WriteI2C( data[0] ); // Write first byte of data
IdleI2C(); // Wait for ACK
// ...
WriteI2C( data[n] ); // Write nth byte of data
IdleI2C(); // Wait for ACK
StopI2C(); // Hang up, send STOP condition
マスター読み取りの例
マスターの読み取りシーケンスは、書き込みシーケンスとは少し異なります。
Master : START | ADDR+R | | | ACK | | ACK | ... | | NACK | STOP
Slave : | | ACK | DATA | | DATA | | ... | DATA | |
この場合も、マスターが通話を開始し、番号をダイヤルします。しかし、彼は今、情報を入手したいと考えています。スレーブは最初に呼び出しに応答し、次に会話を開始します(データを送信します)。マスターは、十分な情報が得られるまで、すべてのバイトを確認します。次に、Not-ACKを送信し、STOP条件で電話を切ります。
Cでは、マスターパーツの場合は次のようになります。
IdleI2C(); // Wait until the bus is idle
StartI2C(); // Send START condition
IdleI2C(); // Wait for the end of the START condition
WriteI2C( slave_address | 0x01 ); // Send address with R/W set for read
IdleI2C(); // Wait for ACK
data[0] = ReadI2C(); // Read first byte of data
AckI2C(); // Send ACK
// ...
data[n] = ReadI2C(); // Read nth byte of data
NotAckI2C(); // Send NACK
StopI2C(); // Hang up, send STOP condition
スレーブコード
スレーブの場合は、割り込みサービスルーチンまたはISRを使用するのが最適です。マイクロコントローラーをセットアップして、アドレスが呼び出されたときに割り込みを受け取ることができます。そうすれば、バスを常にチェックする必要がなくなります。
まず、割り込みの基本を設定しましょう。割り込みを有効にし、ISRを追加する必要があります。PIC18には、高と低の2つのレベルの割り込みがあることが重要です。I2C呼び出しに応答することが非常に重要であるため、I2Cを高優先度割り込みとして設定します。私たちがやろうとしていることは次のとおりです:
- 割り込みが(別の割り込みではなく)SSP割り込みである場合のSSP ISRの書き込み
- 割り込みの優先度が高い場合に備えて、一般的な優先度の高いISRを記述します。この関数は、発生した割り込みの種類を確認し、適切なサブISR(たとえば、SSP ISR)を呼び出す必要があります。
GOTO
高優先度割り込みベクターの汎用ISRに命令を追加します。一般的なISRは多くの場合大きすぎるため、ベクターに直接配置することはできません。
次にコード例を示します。
// Function prototypes for the high priority ISRs
void highPriorityISR(void);
// Function prototype for the SSP ISR
void SSPISR(void);
// This is the code for at the high priority vector
#pragma code high_vector=0x08
void interrupt_at_high_vector(void) { _asm GOTO highPriorityISR _endasm }
#pragma code
// The actual high priority ISR
#pragma interrupt highPriorityISR
void highPriorityISR() {
if (PIR1bits.SSPIF) { // Check for SSP interrupt
SSPISR(); // It is an SSP interrupt, call the SSP ISR
PIR1bits.SSPIF = 0; // Clear the interrupt flag
}
return;
}
// This is the actual SSP ISR
void SSPISR(void) {
// We'll add code later on
}
次に行うことは、チップの初期化時に優先度の高い割り込みを有効にすることです。これは、いくつかの簡単なレジスタ操作によって実行できます。
RCONbits.IPEN = 1; // Enable interrupt priorities
INTCON &= 0x3f; // Globally enable interrupts
PIE1bits.SSPIE = 1; // Enable SSP interrupt
IPR1bits.SSPIP = 1; // Set SSP interrupt priority to high
これで、割り込みが機能しました。これを実装している場合は、今すぐ確認します。SSPISR()
SSP割り込みが発生したときにLEDの点滅を開始する基本を記述します。
さて、あなたはあなたの割り込みを機能させました。次に、SSPISR()
関数の実際のコードを記述します。しかし、最初にいくつかの理論。5つの異なるI2C割り込みタイプを区別します。
- マスター書き込み、最後のバイトはアドレス
- マスター書き込み、最後のバイトはデータでした
- マスター読み取り、最後のバイトはアドレスでした
- マスター読み取り、最後のバイトはデータでした
- NACK:送信終了
SSPSTAT
レジスタのビットを確認することで、現在の状態を確認できます。このレジスタは、I2Cモードでは次のようになります(未使用または無関係なビットは省略されます)。
- ビット5:D / NOT A:データ/非アドレス:最後のバイトがデータの場合はセット、最後のバイトがアドレスの場合はクリア
- ビット4:P:ストップビット:STOP条件が最後に発生した場合に設定されます(アクティブな操作がない場合)。
- ビット3:S:開始ビット:START条件が最後に発生した場合に設定されます(アクティブな操作がある場合)。
- ビット2:R / NOT W:読み取り/書き込みなし:操作がマスター読み取りの場合はセット、操作がマスター書き込みの場合はクリア
- ビット0:BF:バッファフル:SSPBUFFレジスタにデータがある場合はセット、そうでない場合はクリア
このデータを使用すると、I2Cモジュールの状態を確認する方法を簡単に確認できます。
State | Operation | Last byte | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 0
------+-----------+-----------+-------+-------+-------+-------+-------
1 | M write | address | 0 | 0 | 1 | 0 | 1
2 | M write | data | 1 | 0 | 1 | 0 | 1
3 | M read | address | 0 | 0 | 1 | 1 | 0
4 | M read | data | 1 | 0 | 1 | 1 | 0
5 | none | - | ? | ? | ? | ? | ?
ソフトウェアでは、デフォルトとして状態5を使用するのが最適です。これは、他の状態の要件が満たされていない場合に想定されます。この方法では、スレーブがNACKに応答しないため、何が起こっているのかわからない場合は応答しません。
とにかく、コードを見てみましょう:
void SSPISR(void) {
unsigned char temp, data;
temp = SSPSTAT & 0x2d;
if ((temp ^ 0x09) == 0x00) { // 1: write operation, last byte was address
data = ReadI2C();
// Do something with data, or just return
} else if ((temp ^ 0x29) == 0x00) { // 2: write operation, last byte was data
data = ReadI2C();
// Do something with data, or just return
} else if ((temp ^ 0x0c) == 0x00) { // 3: read operation, last byte was address
// Do something, then write something to I2C
WriteI2C(0x00);
} else if ((temp ^ 0x2c) == 0x00) { // 4: read operation, last byte was data
// Do something, then write something to I2C
WriteI2C(0x00);
} else { // 5: slave logic reset by NACK from master
// Don't do anything, clear a buffer, reset, whatever
}
}
どのような割り込みタイプがあるかを確認するために、ビットマスクを使用してSSPSTAT
レジスタを確認する方法(最初にANDを0x2d
とって、有用なビットのみが表示されるようにする)を確認できます。
割り込みに応答するときに送信または実行する必要があることを見つけるのはあなたの仕事です。それはアプリケーションによって異なります。
参考文献
繰り返しますが、マイクロチップがI2Cについて書いたアプリケーションノートについても触れておきます。
コンパイラライブラリのドキュメントがあります:コンパイラライブラリドキュメント
自分で設定する場合は、(M)SSPセクションのチップのデータシートでI2C通信を確認してください。マスターパーツにはPIC18F46K22、スレーブパーツにはPIC18F4620を使用しました。