shibaのアイコン画像
shiba 2020年04月03日作成 (2020年04月03日更新)
セットアップや使用方法 セットアップや使用方法 閲覧数 13791
shiba 2020年04月03日作成 (2020年04月03日更新) セットアップや使用方法 セットアップや使用方法 閲覧数 13791

I2Cのクロック周波数を下げるときの注意点

はじめに
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
}

(測定波形)
キャプションを入力できます

I2Cのクロック周波数を下げてみよう
また、fSCLを40kHzに下げる場合の記述例と測定波形は以下となる。

(記述例)
Wire.setClock(40000); // fSCL = 40kHz

(測定波形)
キャプションを入力できます

I2Cのクロック周波数をもっと下げてみよう
同様に、fSCLを20kHzに下げる場合の記述例と測定波形は以下となる。

(記述例)
Wire.setClock(20000); // fSCL = 20kHz

(測定波形)
キャプションを入力できます

I2Cのクロック周波数を更に下げてみよう(失敗)
ところが、更に10kHzに下げるために以下のように記述しても10kHzにはならず、
測定波形からわかるようにかえって27kHz強に上昇してしまう。

(記述例)
Wire.setClock(10000); // fSCL = 10kHz

(測定波形)
キャプションを入力できます

これはなぜであろうか。

なぜ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

(測定波形)
キャプションを入力できます

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)

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

(測定波形)
キャプションを入力できます

以上のように、
Wire.setClock(fSCL)に記述するfSCLは、
 設定レジスタ(TWBR)をオーバーフローさせないように下限値を持つこと、
 分周器を使用する場合は分周比Nを踏まえてN倍しておく必要があること、
に注意が必要である。

5
ログインしてコメントを投稿する