シリアル通信でまだ試してみていなかった I2C (I2C : Inter-Integrated Circuit) について勉強していこうと思います。
アマチュア無線局の開局申請などですっかり放置していたわけですが、そっち方面もぼちぼちやっていきたいので、細切れになりますけど都度やってみたことを書いていくことにします。これまで同様にロジックIC 使って回路を組んでみたいわけですが、完成はいつになることやら。途中挫折もありってことで、ご容赦ください。
なお、これまでにやってみた SPI や UART については、以下の記事を参照ください。
I2C とは
えー、ググってください m(_ _;)m
簡単に言うと、I2C は SPI と同じ同期式シリアル通信の一種。SPI と違う点は、ペリフェラルの選択はアドレス信号によるってところかな。伝送路はクロック信号 SCL とデータ入出力 SDA の 2本で、出力はオープンドレインです。コントローラが信号を送るとペリフェラルが ACK (acknowledgement : 肯定応答) を返すってあたりが、なかなか手強そうな感じです。
Arduino からの I2C 信号出力
Arduino から出力される I2C 信号を確認してみましょう。
使用するのは Arduino Nano Every です。これをコントローラとして、送出される信号を観測してみました。
スケッチ
Arduino Nano Every の Wire ライブラリを使用して、I2C 信号を出力させるスケッチです。
- #include <Wire.h>
- void setup() {
- Wire.begin();
- }
- void loop() {
- static byte address = 0x54;
- static byte data = 0xFF;
- Wire.beginTransmission(address);
- Wire.write(data);
- Wire.endTransmission();
- delay(500);
- }
Wire により、アドレス 0x54 とデータ 0xFF を送出するだけのスケッチです。ただし、有効なペリフェラルを接続していないため ACK が返ってきませんから、アドレスを送出するだけになるはずです。
I2C 出力波形
Arduino Nano Every の SCL (A5ピン) と SDA (A4ピン) が出力ピンです。
I2C は出力がオープンドレインですから、伝送路にプルアップ抵抗が必要です。が、Wire では内部プルアップを有効にしているらしいので、外部プルアップ抵抗はつけていません。
図1 は、Arduino Nano Every の SCL (黄) と SDA (青) の出力波形です。が …
なんですか? これは。
パルスの立ち上がり波形がなまってしまっています。Arduino UNO と Arduino NANO でも確認してみましたが、同様の波形でした。
標準モードのクロック周波数は 100KHz ですが、これを高速モード 400KHz にしたときの波形が図2 です。もうね、これはクロックじゃない。
立ち上がりの時定数は 1.25μs でした。CMOS の入力の立ち上がり時間を 500ns/V とすると問題なさそうですけど、立ち上がりでデータを読み込む同期回路のクロックとしては問題ありな気がします。
まず、この波形をなんとかしないといけません。
回路の定数を調べる
パルスの立ち上がり波形がなまってしまうのは、Arduino の寄生容量によるものだということはすぐに想像できます。回路の定数を調べてみましょう。
まず、内部プルアップの抵抗値です。測定方法は簡単ですね、抵抗でプルダウンしたときの出力電圧を測ればわかります。A5ピンの無負荷時の出力電圧 VS は 4.6V でした。次に抵抗 RL=33KΩ でプルダウンしたとき、出力電圧 VL は 2.4V でした。このときの内部プルアップ抵抗 RS は、
RS = (VS / VL - 1) RL = (4.6 / 2.4 - 1) x 33 = 30.3 [KΩ]
オシロスコープでの計測で、パルスの立ち上がりの時定数 τ は 1.25μs でした。寄生容量 CP は、
CP = τ / RS = (1.25 x 10-6) / (30.3 x 103) = 41.3 [pF]
となります。
そこで、外部プルアップ抵抗として RP=4.7KΩをつけることにします。時定数 τ は、
τ = CP (RS RP) / (RS + RP) = 41.3 x 10-12 x (30.3 x 4.7) / (30.3 + 4.7) x 103 = 0.168 [μs]
外部プルアップ抵抗 4.7KΩをつけたときの出力波形が図3 です。拡大した波形を記録していませんが、このときの時定数は 0.17μs でした。計算通りですね。
外部プルアップ抵抗をこれ以上小さくしても波形はあまり変化しませんでした。ペリフェラルをつなぐと影響が出るかもしれませんので、最終的にはカットアンドトライで決めることになるでしょう。
アドレスフォーマット
Arduino Nano Every から送出された I2C 信号の内容を確認しておきましょう。今回はペリフェラルが接続されておらず、ACK が返ってきませんので、アドレスの送出だけが行われています。
図4 の黄色は SCL の出力で、クロック信号 (標準モード) です。周波数は 108.2KHz です。
青色は SDA の出力です。ここでは A6~A0 が 7bit のアドレスになっていて、MSB から 0x54 が送信されています。RW は通信方向で、送信 (Write) が 0、受信 (Read) が 1 です。
最後が ACK で、ペリフェラルが受信完了していれば 0 を送ってきます。が、ここでは ACK が返ってきませんので 1 になっています。
後記
今回は、Arduino Nano Every から、ライブラリ Wire を使って I2C で送信するときの出力波形を確認してみました。
パルスの立ち上がり波形がかなりなまっていたのは、ちょっと驚きでした。調べていませんが、Arduino の出力はオープンドレインではないし、INPUTモードで内部プルアップ抵抗のオンオフで出力させているんじゃないのかな。しかたないのかもしれません。とにかく、Arduino の内部プルアップだけでは安定した I2C通信はできないように思います。プルアップ抵抗には、ちょっと注意が必要です。
さて、ペリフェラルを接続していないので、コントローラ (Arduino) はデータを送出してくれません。ならば ACK を返してあげようじゃありませんか。次回は、とりあえず ACK を返す回路を考えてみましょう。