I2Cスニファー

こんにちは 何らかの理由で職場で問題が発生しました。I2Cで動作するデバイスがあり、そのプロトコルを理解する必要がありました。 したがって、I2Cインターフェース用のスニファーが必要です。I2Cを介して出入りするすべてのものをUARTポートに出力し、コンバーターを介してコンピューターのCOMポートに出力します。



開始する



atmeg8のペアだけが手元にあり、それらを使用しない理由を私は決めました。 次に、スニファー回路についての質問が生じました。



2つのオプションがありました-スニファーを並列に含めるか、開回路に含めるか。 明らかに、最初のオプションははるかに単純に見えますが、実際は完全に異なっています。 しかし、まず最初に。



要するに、インターフェース自体についてです。 I2C(AtmelovskyのTWI)は、SCLとSDAの2本のワイヤを使用します。 1つ目は信号のクロッキングを担当し、2つ目は情報を直接送信します。 インターフェイスには、START状態とSTOP状態もあります。



したがって、最初に考えたのは、プローブを取り、一方ではatmega8の外部割り込みのレッグに接続し、もう一方ではSDAラインでリーディングエッジをキャッチし、過去に0または1を決定することでした。 、STOPシグナルが正しく処理されなかったため。



2番目の考えは同じことですが、SCLラインで割り込みをキャッチし、割り込みで通常のデジタルレッグに接続されたSDAラインを読み取ります。 ここでは、同じSTOP条件を除いて、すべてがより実行可能に見えましたが、ブレッドボードに組み立てて何が起こるかを確認することにしました。



下のコードに明らかな欠陥が見つかった場合は、事前に謝罪します。拒否されたコードバージョンをひざの上のメモリから復元するからです。



割り込みハンドラコードは次のようになりました。



ISR(INT0_vect) { cli(); if (bitIsHigh(PINB, 0)) uart_send_char('1'); else uart_send_char('0'); sei(); }
      
      





ゼロと1がポートに流れ込みましたが、データが間違っていることがすぐに明らかになりました。これらは予想よりはるかに少なく、同じリクエストを繰り返すと変化しました。 理由を見つける過程で、それはすべて、データがuartインターフェイスへのアクセスのために失われたという事実に帰着しました。ちなみに、これは最大安定速度38 kbit / sで動作し、I2C自体は100 kbit / sで動作しました。 UARTを許容可能な速度に変換するために必要な周波数の水晶振動子がないため、UARTの速度を上げることはできませんでした。 したがって、uartを使用した作業を割り込みから削除する必要がありました。 次のようなものを得ました:



 static uint8_t data = 0; static uint8_t idx = 7; ISR(INT0_vect) { cli(); data |= bitIsHigh(PINB, 0) << (idx--); if (!idx) { uart_send_char(data); data = 0; idx = 7; } sei(); }
      
      





すべてが動作するように安定しましたが、データのみがまだ意味を持ちませんでした。 STOP処理などを含むアルゴリズムを数時間使用した後、別の方法で処理することにしました。



正しい道を



並列接続スキームでスニファーを実装しようとしても、どれも出てきませんでした。 これに基づいて、1つのオプションしかありませんでした。マイクロコントローラーをギャップに含める必要がありました。つまり、応答デバイスのマスターと元のマスターの下位の両方である必要がありました。 おそらく混乱するかもしれませんが、実際にはそうではありません。



atmega8にはボード上にハードウェアI2Cが1つしかないため、プロトコルが機能するためにはソフトウェアサポートを記述する必要があることは明らかです。



結果は次のコードです。



 ISR(TWI_vect) { cli(); uint8_t status = TWSR; uint8_t b; char s[4]; s[3] = 0; _delay_ms(1); switch (status & I2C_STATUS_MASK) { case I2C_STATUS_SR_RX_ADR_ACK:/* case I2C_STATUS_SR_RX_ADR_NACK:*/ uart_send_str("-AW:"); uart_send_int( TWDR ); i2csoft_start(); i2csoft_open_write(I2C_ADDRESS); break; case I2C_STATUS_SR_RX_DATA_ACK:/* case I2C_STATUS_SR_RX_DATA_NACK:*/ b = TWDR; sprintf(s, " %.2X", b); uart_send_str(s); i2csoft_write_byte(b); break; case I2C_STATUS_SR_RX_STOP_RESTART: uart_send_str("E\n"); _delay_ms(10); do { _delay_us(5); i2csoft_start(); } while (!i2csoft_open_read(I2C_ADDRESS)); break; case I2C_STATUS_BUS_ERROR: uart_send_str("B\n"); break; case TW_ST_SLA_ACK: uart_send_str("-AR:"); uart_send_int( TWDR ); b = i2csoft_read_byte(); sprintf(s, " %.2X", b); uart_send_str(s); TWDR = b; break; case TW_ST_DATA_ACK: b = i2csoft_read_byte(); sprintf(s, " %.2X", b); uart_send_str(s); TWDR = b; break; case TW_ST_DATA_NACK: case TW_ST_LAST_DATA: b = i2csoft_read_byte(false); uart_send_str("L\n"); break; default: uart_send_char('U'); uart_send_int(status); uart_send_char(' '); break; } TWCR |= (1<<TWINT); sei(); }
      
      





マスターデバイスはハードウェアI2C atmegaに接続され、実行デバイスは任意の2つのデジタルレッグに接続されます。1つはSCLモードで動作し、もう1つはSDAです。 上記のコードが行うことは、I2Cを介して割り込みを受信し、プログラムインターフェイスで同様の状態を引き起こし、サービス情報が何が起こっているのかを理解するためにuartに書き込まれることです。 設定された遅延は特定のデバイス用に選択されたもので、他のデバイスではわずかに異なる場合があります。 その結果、非常に堅固なスニファーが得られます。



興味がある人は、ソースコードをgithubから取得できます。



All Articles