2C
複数のモジュールを接続するための伝送方式: I2C、2 つのバスを使用。
2 つのバスは、クロック バス SCL とデータ バス SDA です。
コミュニケーションプロセス
次に、I2C 上で 1 つのモジュール (マスター) から別のモジュール (スレーブ) にメッセージを送信するプロセスを見てみましょう。
- MCU は特定の方法を使用して自身を識別し、送信を開始します。
- MCU は LCD スレーブのアドレス + 読み書きビットを送信し、他のモジュールはそれを受信し、そのアドレスが自分のものではないことがわかるため、処理しません。
- LCD はそれを受信すると、ターゲットが自分自身であることを認識しているため、ack を返します。
- MCU は ACK を受信した後、データのフレームを送信します。
- 送信後、MCU は ACK を待ち、ACK を受信した後、次のデータ フレームの送信を続けます。
- 送信ストップビットが終了するまで送信します。
データ長は789などの設定が可能です。
バス上のデバイスは、オープンドレイン出力を備えた半二重通信を行います。
デフォルトのバスは、プルアップ抵抗によって High にプルアップされます。
デバイスの出力出力が Low の場合、バスはグランドにオンになり、バスは Low に引き下げられます (バス全体が Low に引き下げられます)。江謝科技氏の例はとても良いです。バスのクロスバーのようなものです。誰かがクロスバーを引っ張って引き下げます。クロスバー全体が引き下げられます。低くなると、誰かがバスを使用します。」
次に、バスがデータを送信する方法ですが、SCLとSDAの2つのバスはどのような状況でスタートストップ0 1ビットを示すのでしょうか?
まず、SDA の値は、SCL がハイレベルの場合にのみ意味を持ちます。
SDA は High から Low へ変化し、開始ビットを示します。Low から High まで、ストップ ビットを示します。
スタートビット以降、SDA のハイレベルは 1、ローレベルは 0 を意味します。
1 バイトのデータを送信した後、バスは High になり続けます。受信者がバスをプルダウンし、送信者がバスが 1 → 0 であることを発見した場合 (送信者自身がバスをプルダウンしたのではなく、受信者がバスをプルダウンしましたが、送信者はそれを検出できます)、受信者が正常に受信したことを意味します。 and pull バスを引っ張って「受信」を表示します。SDA がまだ高レベルにある場合は、受信機が ACK を正常に受信または送信できなかったことを意味します。
問題解決
I2C は非常に単純なマスター/スレーブ通信プロトコルですが、多くの制限があります。たとえば、7 ビット アドレス ラインでは 2^7 デバイスのみが許可されます。一度にマスター/スレーブ通信できるデバイスは最大 2 つです。デバイスの速度は制限されます。バス全体の通信などに影響を与えます。
質問 1: スレーブ デバイスの処理速度が遅すぎて、次のクロック サイクルで新しいデータ フレームを受信できない場合はどうすればよいですか?
方法: クロック ストレッチング、一定期間 SCL をプルダウンして、次のクロック サイクルがまだ到着していないかのように見せかけます。
質問 2: 複数のデバイスが競合するデータを同時に送信した場合はどうすればよいですか?
方法: バス アリビテーション。バスがデバイスによってプルダウンされていること、およびすべてのデバイスがバスがプルダウンされた信号を受信できることがわかっています。したがって、2 つのデバイスが同時に情報の送信を開始した場合、前のデータが一致しているかどうかは関係ありません。初めてデータが不一致の場合、一方のデバイスはデータ 0 を送信し、もう一方のデバイスはデータ 1 を送信します。 、SDA バスは DATA2 の 0 によってプルダウンされます。
DATA1 データを送信するデバイスは、誰かが私と同時にデータを送信しているため、バスは予想どおり 1 ではなく、その人によって 0 に引き下げられることを理解します。それから私は辞めます、あなたはそれを送ります。この場合、DATA2 によって送信されるデータのみが存在します。
質問 3: 上記で送信されるデータは毎回 1byte 8bit ですが、これでちょうどいいです。送信するアドレスが8ビットではない場合はどうなりますか?
方法: 8 ビット未満のアドレスには固定の追加スタート ビットが埋められ、8 ビットを超えるアドレスには 2 バイトが埋められ、不十分なアドレスにも追加のスタート ビットが埋められます。
質問 3: マスターがデータの送信を終了し、すぐにデータを受信してスレーブになりたい場合、それは可能ですか?
方法: sr 信号を通じてスタート ビットを再送信する (リピート スタート)。これにより、自身が書き込みではなく読み取りであることを識別し、通信を再開します。
アドレス指定形式
スレーブアドレスのアドレッシングにはいくつかの固定フォーマットがあります。
0000 000 0: ブロードキャスト、すべてのスレーブ ノードと通信します。スレーブが無視 (NACK) した場合、ブロードキャストには参加しません。ACKが返れば参加します。ただし、複数のスレーブが ACK を返した場合、マスターは誰が応答したかを知りません。
2 番目のバイトは、ソフトウェアの起動、クリア、リセットなどの動作に関連するものを送信します。
プログラミングアプリケーション
スレーブモード:
- I2C デバイスはデフォルトでスレーブ モードで動作します。
- 周辺クロックは I2C_CR2 レジスタにプログラムされます。周波数は2kHz〜100kHzです。
- ハードウェアは、送信される開始情報とアドレス情報を自動的に待ちます。
- addr 情報が OAR1 に格納されているアドレスと同じ場合、ターゲットは自身であることを意味します。ACK ビットが 1 の場合、ACK パルスを送信します。
- ADDR ビットを設定します。1 は一致を意味します。
- ITEVFEN が割り込みイベントフラグが 1 の場合、割り込みが発生します。
- TRA ビットは、スレーブが R モードか T モード (受信または送信) のどちらであるかを示します。
- BTFビットマークは没収されました。
そう言うとまだ少しわかりにくいのですが、I2C ではデータをスムーズに送信するために具体的にどのような処理が行われているのでしょうか?
まずはメインモードの概念から。マスター マスター モードはクロック信号を駆動して転送を開始し、スレーブ スレーブ モードは転送に応答します。
メインモード
送信:
すべての EV イベントは、対応するソフトウェア シーケンスが実行されるまで SCL を Low にプルします。
S: イベントを開始します。たとえば、CR2 レジスタにペリフェラル クロックを設定し、クロック レジスタを設定し、クロック レジスタを立ち上げ、CR1 をイネーブルしてクロックをイネーブルにし、CR1 にスタート ビットを設定し、バスが Low になって準備完了を示すのを待ち、スタート信号が発生し、メインモードに切り替わります。
EV5: イベントが正常に開始され、SB レジスタ = 1 に設定されます。SB レジスタ = 1 の後にのみアドレス フェーズを実行でき、SB および EV5 イベントはアドレス フェーズの実行後に自動的にクリアされます。
アドレス: アドレス段階。7 ビット アドレス + 1 リード/ライト ビットを送信し、スレーブの ACK を待ちます。ackを受信してEV6に入る。
EV6: addr ビット = 1 の設定は、アドレス フェーズが正常に実行され、マスターが ack を受信したことを意味します。EV6をクリアすると自動的にEV8に突入します。
EV8: TxE を設定し、ホストから送信されるデータを書き込む準備をします。TxE は、データ レジスタが空であり書き込み可能であることを示します。データが DR に書き込まれるたびに、TxE および EV8 イベントはクリアされます。データの書き込み後、データが送信され、ホストは ack を受信した後も送信を続けます。データ転送の終了を示すには、BTF=1 を使用します。
void i2c_write(uint8_t address, uint8_t *buffer, int buff_len) {
int i = 0;
// Send in sequence: Start bit, Contents of buffer 0..buff_len, Stop
while (((I2C1->SR2>>1)&1)); // wait until I2C1 is not busy anymore
I2C_GenerateSTART(I2C1, ENABLE); // Send I2C1 START condition
// wait for I2C1 EV5 --> Slave has acknowledged start condition
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
// Send slave Address for write then wait for EV6
I2C_Send7bitAddress(I2C1, address, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
while (i < buff_len){
I2C_SendData(I2C1, buffer[i]); // send data then wait for EV8_2
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
i++;
}
I2C_GenerateSTOP(I2C1, ENABLE); // send stop bit
}
買収:
フロントはマスター送信と同じです。
TxE は RxE に変更され、=1 はデータが受信されたことを示します。
マスターが停止イベントを設定(NACK を送信)すると、受信を停止します。
void i2c_read(uint8_t address, uint8_t *buffer, int buff_len) {
int i = 0;
// Start bit, Contents of buffer from 0..buff_len, sending a NACK
// for the last item and an ACK otherwise, Stop bit
I2C_GenerateSTART(I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); //EV5
// Send slave Address for write then wait for EV6
I2C_Send7bitAddress(I2C1, address, I2C_Direction_Receiver);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
I2C_AcknowledgeConfig(I2C1, ENABLE); // going to send ACK
while (i < buff_len - 1){
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); //EV7
buffer[i] = I2C_ReceiveData(I2C1); // get data byte
i++;
}
I2C_AcknowledgeConfig(I2C1, DISABLE); // going to send NACK
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); //EV7
buffer[i] = I2C_ReceiveData(I2C1); // get the last byte
I2C_GenerateSTOP(I2C1, ENABLE); // send stop
}
スレーブモード
送信:
start start イベントはマスターによって開始されます。スレーブはアドレスをチェックし、ack ビットを送信するかどうかを決定します。
EV1: addr ビットのセットはアドレスの一致を示します。
EV3-1: TxE ビットをセットし、受信データを開始します。ホストがこれ以上データを必要としないことを示すために NACK を返すか、ACK が失敗したことを示す AF=1 が返されるまで。
買収:
フロントからEV1、スレーブ送信までは同じです。
- データは DR レジスタから読み出されます。
- バイトを読み取った後、ack ビットが設定されている場合は、ack 情報を返します。
- RxE ビットは、受信データのステータス レジスタです。
- マスターがストップコンディションを生成すると停止します。
異常事態:
バスエラー、NACK、調停失敗、クロック例外タイムアウト。