i2c clock stretchingとは?
I2C通信では、マスターデバイスがクロック速度を決定します。
ただし、I2Cスレーブがマスターによって指定されたクロック速度と連携できず、少し遅くする必要がある状況があります。これは、クロックストレッチングと呼ばれる方法によって行われます。
I2Cスレーブは、バス速度を下げる必要がある場合にSCLをLowにholdします。マスターは、クロック信号をHigh状態にした後、クロック信号を実際に読み出し、SCLが実際にHighになるまで待機する必要があります。
Raspberry Piに使われているBroadcomのSoCのバグで正常に動作しません。センサーをはじめ、結構Clock Stretchingは使われているので、どうにかしないとI2Cがまともに使えません。
http://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html
回避方法としては
- I2Cのクロックを下げる
- i2c-gpioを使う
の2点となります。今回はi2c-gpioを使って回避してみます。
i2c-gpioとは
GPIOのbitbang機能を使ってI2C通信を行うものです。raspbianなどではすでにドライバが準備されており、適切に設定することで使用することが可能になります。
設定方法
具体的にはREADMEに書いてある通りになります。
Name: i2c-gpio
Info: Adds support for software i2c controller on gpio pins
Load: dtoverlay=i2c-gpio,<param>=<val>
Params: i2c_gpio_sda GPIO used for I2C data (default "23")
i2c_gpio_scl GPIO used for I2C clock (default "24")
i2c_gpio_delay_us Clock delay in microseconds
(default "2" = ~100kHz)
すでにGPIO2,3を使ってI2Cデバイスをくっつけたけど、i2c-gpioに切り替えたい場合、config.txtに以下の設定を行うことで使用可能になります。
標準のI2Cを無効化(下記の行をコメントアウト)
#dtparam=i2c_arm=on
以下の行を追記してi2c-gpioを有効化します。
dtoverlay=i2c-gpio,bus=1,i2c_gpio_sda=2,i2c_gpio_scl=3,i2c_gpio_delay_us=2
これでSoCのI2Cコントローラではなく、i2c-gpioのドライバを使ってアクセスすることが可能になります。
正常に起動したら、i2cdetectを使って正常に通信できていることを確認できればOKです。
デバイスツリーにデバイスを登録する
デバイスツリーを使ってi2cデバイスを登録したい場合、dtsに下記のように対応させたいデバイス情報を追記することでできます。
以下はRTC(RV3029)を認識させる例です。
// Overlay for i2c_gpio bitbanging host bus.
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2835";
fragment@0 {
target-path = "/";
__overlay__ {
i2c_gpio: i2c@0 {
reg = <0xffffffff>;
compatible = "i2c-gpio";
gpios = <&gpio 23 0 /* sda */
&gpio 24 0 /* scl */
>;
i2c-gpio,delay-us = <2>; /* ~100 kHz */
#address-cells = <1>;
#size-cells = <0>;
rv3029c2@56 {
compatible = "rv3029c2";
reg = <0x56>;
};
};
};
};
fragment@1 {
target-path = "/aliases";
__overlay__ {
i2c_gpio = "/i2c@0";
};
};
fragment@2 {
target-path = "/__symbols__";
__overlay__ {
i2c_gpio = "/i2c@0";
};
};
__overrides__ {
i2c_gpio_sda = <&i2c_gpio>,"gpios:4";
i2c_gpio_scl = <&i2c_gpio>,"gpios:16";
i2c_gpio_delay_us = <&i2c_gpio>,"i2c-gpio,delay-us:0";
bus = <&i2c_gpio>, "reg:0";
};
};
効果はあったのか?
CSS811というセンサーでたまに出ていたエラーが見られなくなっているので、効果はありそうです。
参考:
https://qiita.com/spicemanjp/items/50474e56ddccabf02b01
https://www.kernel.org/doc/Documentation/devicetree/bindings/i2c/i2c-gpio.txt