shiba が 2020年04月03日16時13分34秒 に編集
コメント無し
本文の変更
**はじめに** ArduinoでI2Cバスの通信速度(fSCL:SCLクロック周波数)をI2C標準仕様のデフォルト値の100kHzから変更する場合、 Wireライブラリをincludeした上で、setup内にWire.setClock(fSCL)を記述すれば良いことが知られる。 参照:https://www.arduino.cc/en/Reference/Wire https://www.arduino.cc/en/Reference/WireSetClock fSCLの範囲はI2Cバスの標準仕様上は0~100kHzであるが、実際にfSCLを下げる場合は、 fSCLに記載可能な値には下限値が存在すること、 分周器を用いる場合は分周比Nを踏まえてN倍した値とすること、 に注意が必要である。 ネット上にこれらを直接説明する情報を検索できなかったため、考察を備忘録として以下にまとめる。 **I2Cのクロック周波数を設定してみよう** 例えば、fSCLのデフォルト値(100kHz)の記述例と測定波形は以下となる。 (記述例) #include <Wire.h> void setup() { Wire.begin(); Wire.setClock(100000); // fSCL = 100kHz } (測定波形) ![キャプションを入力できます](https://camo.elchika.com/a724520a9316e950aeb143948d20e6e2f8d258bd/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66393365663436612d613563352d346531392d623834372d3734356364333438623833392f64613937333338372d626663322d346365332d623961372d323436386664343464316563/) **I2Cのクロック周波数を下げてみよう** また、fSCLを40kHzに下げる場合の記述例と測定波形は以下となる。 (記述例) Wire.setClock(40000); // fSCL = 40kHz (測定波形) ![キャプションを入力できます](https://camo.elchika.com/04adbf573f8578aeac00a0de96738574c2bbeb77/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66393365663436612d613563352d346531392d623834372d3734356364333438623833392f39633664386536632d633836312d346438372d386661302d316338383663373866346165/) **I2Cのクロック周波数をもっと下げてみよう** 同様に、fSCLを20kHzに下げる場合の記述例と測定波形は以下となる。 (記述例) Wire.setClock(20000); // fSCL = 20kHz (測定波形) ![キャプションを入力できます](https://camo.elchika.com/b58b98457133448ca32a0848ffcc1b837cd6e88a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66393365663436612d613563352d346531392d623834372d3734356364333438623833392f31616564656538302d343439352d346363342d616633392d663033653538666631613435/) **I2Cのクロック周波数を更に下げてみよう(失敗)** ところが、更に10kHzに下げるために以下のように記述しても10kHzにはならず、 測定波形からわかるようにかえって27kHz強に上昇してしまう。 (記述例) Wire.setClock(10000); // fSCL = 10kHz (測定波形) ![キャプションを入力できます](https://camo.elchika.com/125bf2f07283e00a2ee5b7dd8241a2b43e8c4eaf/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66393365663436612d613563352d346531392d623834372d3734356364333438623833392f31613431303437352d333638382d343263642d623062352d393237366161366238336564/) これはなぜであろうか。 **I2Cのクロック周波数を下げるのになぜ失敗したのだろうか** **まず、I2Cのクロック周波数を導出する式を見てみよう** 使用したMCUはATmega328P(3.3V、8MHz)であった。 資料(https://avr.jp/user/DS/PDF/mega328P.pdf)を見ると、ATmega328Pは8bitのMCUで、 fSCLは以下の式で与えられる。 fSCL = fMCU / ( 16 + 2 * TWBR * N ) ここで、 fMCU:MCUクロック周波数、8MHz。 TWBR:TWIビットレートレジスタ (TWI Bit Rate Register)、ビット数は8bit。 N:分周比、TWPS1,0で設定。 TWPS1,0 : TWIプリスケーラビット (TWI Prescaler Bits)、ビット数は2bit。 (TWPS1,0)=(0,0)の場合、N = 1、 (0,1)の場合、N = 4、 (1,0)の場合、N = 16、 (1,1)の場合、N = 64。 **デフォルトのクロック周波数100kHzを筆算で確認しよう** デフォルトの100kHzは、fMCU = 8MHz、TWBR = 32、N = 1の時の値である。 fSCL = 8000kHz / (16 + 2 * 32 * 1) = 8000kHz / 80 = 100kHz **I2Cのクロック周波数を下げた場合を筆算で確認しよう** 例えば、 Wire.setClockを40kHzと記述すると、TWBRが92に設定されると考えられる。 TWBR = (8000kHz / 40kHz - 16) / 2 = (200 - 16) / 2 = 184 / 2 = 92 20kHzと記述すると、TWBRが192に設定されると考えられる。 TWBR = (8000kHz / 20kHz - 16) / 2 = (400 - 16) / 2 = 384 / 2 = 192 **I2Cのクロック周波数を下げた場合(失敗)も筆算で確認しよう** ところが、10kHzと記述すると、TWBRを392に設定する必要がある。 TWBR = (8000kHz / 10kHz - 16) / 2 = (800 - 16) / 2 = 784 / 2 = 392 392は2進数表現では110001000と9bit必要である。 TWBRは8bitレジスタであるため、扱える値が0~255の範囲に限定され、オーバーフローしてしまうと考えられる。 **I2Cのクロック周波数の設定レジスタがオーバーフローするとどうなるのだろうか** 392の2進数表現の110001000の下位8bitの10001000は136である。 TWBR = 136としてfSCLを計算してみると、27.8kHzとなる。これは、測定結果と一致する。 fSCL = 8000kHz / (16 + 2 * 136 * 1) = 8000kHz / 288 = 27.8kHz **I2Cのクロック周波数の設定レジスタがオーバーフローしないぎりぎりはどうなるのか** TWBRの最大値は255であり、fSCLの下限値は、15.2kHzとなると考えられる。 fSCL = 8000kHz / (16 + 2 * 255 * 1) = 8000kHz / 526 = 15.2kHz (記述例) Wire.setClock(15200); // fSCL= 15.2kHz (測定波形) ![キャプションを入力できます](https://camo.elchika.com/535e02c3e21544853457b011ee34d3eef0acfccc/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66393365663436612d613563352d346531392d623834372d3734356364333438623833392f34643636613665372d396465342d343539302d396634632d613031393137363362316137/) **I2Cのクロック周波数を10kHzに下げるにはどうしたら良いのか** 8MHzの8bit MCUの場合、fSCLを15.2kHzよりも下げる場合は、分周比Nを4以上に設定する必要がある。 分周比Nを制御するTWPS1,0は、twi.cの以下で設定されている模様である。 (twi.cの記述例(デフォルト)) cbi(TWSR, TWPS0); // TWSRレジスタのTWPS0をクリア(0) cbi(TWSR, TWPS1); // TWSRレジスタのTWPS1をクリア(0)
fSCLを10kHzに下げるには、例えば、分周比Nを4に設定するとともに、fSCLを40kHzに記述する必要がある。
**I2Cのクロック周波数を10kHzに下げてみよう(成功)**
fSCLを10kHzに下げるには、例えば、分周比Nを4に設定するとともに、fSCLを40kHzに記述することで可能となる。
(twi.cの記述例) sbi(TWSR, TWPS0); // TWSRレジスタのTWPS0をセット(1) cbi(TWSR, TWPS1); // TWSRレジスタのTWPS1をクリア(0) (記述例) Wire.setClock(40000); // fSCL= 40kHz (測定波形) ![キャプションを入力できます](https://camo.elchika.com/b3e342640024c2be8d80d00eb74d989b59cf7687/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66393365663436612d613563352d346531392d623834372d3734356364333438623833392f30313762353538302d653264342d343863362d393963642d626366646337373664303662/) 以上のように、 Wire.setClock(fSCL)に記述するfSCLは、
設定レジスタ(TWBR)のビット数がオーバーフローさせないようにしない下限値を持つこと、
設定レジスタ(TWBR)をオーバーフローさせないように下限値を持つこと、
分周器を使用する場合は分周比Nを踏まえてN倍しておく必要があること、 に注意が必要である。