🍓

Raspberry PiのI2C実装のバグとその対策

に公開

この記事はUMITRON Advent Calendar 2025 3日目の記事です

はじめに

I2C は組み込み開発などで用いられることがある通信規格の一種で、Raspberry PiのSoC[1]にはI2Cコントローラが内蔵されています。しかしRaspberry Pi 4以前のモデルでは、このI2Cコントローラにいくつかのバグが存在します。この記事ではこのRaspberry PiのI2Cのバグについて解説します。できるだけ組み込み開発に明るくないソフトウェアエンジニアでも読めるよう努めました。

I2Cとは

I2C(Inter-Integrated Circuit, アイ・スクエアド・シー)は通信規格の1つで、主に組み込み開発で用いられます。通信にはデータ線とクロック線の2本を使用し、クロック線が規則正しく電圧をHIGH/LOWを繰り返し、それに合わせてデータ線からデータを送信します。登場人物として controller と target があり、 controllerが通信を開始し、 targetが応答するという、サーバクライアントモデルに似た構成を取ります。[2]

また、前述の通りクロック線は電圧のHIGH/LOWが規則正しく切り替わっているのですが、通信時にtarget側の応答に時間がかかる場合、targetはクロック線をLOWに引き下げることでcontrollerにちょっと待って、と伝えることができます。これをclock stretchingと呼びます。

Raspberry Pi 3以前

Raspberry PiのSoCにはI2Cのハードウェア実装が入っています。例えばRaspberry Pi 3BにはBCM2837、あるいはBCM2837B0というBroadcom製SoCが使われており、このSoCにI2Cコントローラが内蔵されています。

Raspberry PiでSoCのI2C実装を利用するには /boot/config.txt に以下のような記述をします。

dtparam=i2c_arm=on

クロックのスピードを指定する場合はi2c_arm_baudrateを指定します。

dtparam=i2c_arm=on,i2c_arm_baudrate=100000

これらのI2Cコントローラには共通のバグがあります。前述のclock stretchingが発動し、targetがクロック線をLOWに引き下げた後、再びHIGHに戻す際にタイミングが狂ってしまい、エラーが発生します。

このバグが、Raspberry Pi 3以前のモデルにも共通して存在します。

詳細は以下のページで解説されています。

対策としてはクロックスピードを落としてclock stretchingが発動しづらくするか、根本的に解決するにはそもそもI2Cのハードウェア実装を利用せず、後述するソフトウェア実装を使う方法があります。

Raspberry Pi 4

Raspberry Pi 4のSoCであるBCM2711のデータシートには「BCM283xで見られたclock stretchingバグを修正した」と明示されています[3]が、完全には解決していません。

BCM2711にはBSC(Broadcom Serial Control)という名前のI2Cコントローラが8つ搭載されており、BSC0 - BSC7まであります。このうちBSC2, BSC7 はユーザが直接利用することは想定されておらず、通常利用できるのはBSC0, BSC1, BSC3, BSC4, BSC5, BSC6の6つです。このRaspberry Pi 4のBSCのうちBSC0, BSC1はclock stretchingのバグが修正されておらず、BSC2-7のみ修正されています[4]

前述の通り、BSC2-7のうちユーザが使用できるのはBSC3-6です。これらを利用するには、例えばBSC3を利用する場合、/boot/config.txtに以下のように記述します。

dtoverlay=i2c3

BSC4-6を利用する場合も同様に、i2c4, i2c5, i2c6を指定します。 この設定で新しいBSCを用いることで既存のclock stretchingのバグを回避することができます。が、依然として新しいBSCでもclock stretchingを完全にはサポートしていません。raspberrypi/linuxのissue #4884 では以下の2つの問題が指摘されています。

  • クロックのスピードが変わる
  • 短いclock stretchingでエラーになる

1つずつ見ていきます。

クロックスピードが変わる問題について。I2CのクロックスピードはRaspberry Piのcore clockという、GPUなどに使われているクロックに依存しています。そして、このcore clockは、例えばチップの温度が上がりすぎた時に減少するなど、Raspberry Piの起動中に変化することがあります。BSCがこのクロックに依存しているため、core clockが変化するとI2Cのクロックスピードも変化します。I2Cの仕様では任意のタイミングでクロックスピードが変化することを規定していないため、機器によってはこれが原因で動かなくなる可能性があります。ですが、これは例えば以下のようにcore clockを固定することで回避可能です[5]

core_freq=500
core_freq_min=500

ただし当然core clockを固定するとcore clockを自動で調整する機能による恩恵が得られません。

次に、短いclock stretchingでエラーになる問題について。Raspberry Piのエンジニア(と思われる)の方が同issueに「clock stretchingには最低でも1/2 clock cycle必要」とコメントしており[6]、実際ユーザによる検証でも短いclock stretchingで通信に失敗することが報告されています[7]。この問題はやはりクロックスピードを落とすことで緩和はされますが完全に回避することは難しく、根本的に解決するにはソフトウェア実装のI2Cを使う必要があります。

Raspberry Pi 5

Raspberry Pi 5ではメインのSoCは引き続きブロードコム製のチップであるBCM2712ですが、新規に開発されたI/OコントローラであるチップRP1が搭載されており[8]、I2Cはこのチップに実装されています。Raspberry Pi 5のI2Cのバグは(私が探した限り)報告されておらず、これまであったバグは解消されたようです。

I2Cソフトウェア実装

Raspberry Pi 4以前のI2Cハードウェア実装のバグを回避するにはハードウェア実装を利用せず、ソフトウェア実装のI2Cを使う方法があります。ハードウェア実装を利用する場合、LinuxカーネルがSoCの所定のレジスタにデータを書き込む・読み込むことでハードウェアがそれをI2C通信に変換することで通信できますが、ソフトウェア通信ではGPIOピンに直接HIGH/LOWを書き込んだり読み込んだりすることでI2C通信を行います。GPIOとはGeneral Purpose Input/Outputの略で、ソフトウェアから入出力できるピンのことです。このようにハードウェアの機構を用いずに直接GPIOを操作しI2C通信などを行うことをbit bangingと呼びます。このbit bangingによるI2Cを行うドライバがLinuxカーネルには組み込まれているため、ユーザはハードウェア実装を利用するのと同程度の設定でソフトウェアI2Cを利用できます。ただし、ソフトウェア実装のためCPU負荷がハードウェア実装よりは高くなる点には注意が必要です。

具体的には以下のような記述を/boot/config.txtに追加することでソフトウェア実装を利用できます。

dtoverlay=i2c-gpio,sda=23,scl=24,i2c_gpio_delay_us=5

sdaがデータ線を割り当てるGPIOの番号、sclがクロック線を割り当てるGPIOの番号です。i2c_gpio_delay_usが分かりづらいのですが、クロックサイクルの半分のマイクロ秒を指定します。例えば100kHzで通信したい場合、1クロックサイクルは10マイクロ秒なので、i2c_gpio_delay_usには5を指定します。

このような設定でソフトウェア実装のI2Cを利用することで、Raspberry Pi 4以前のI2Cハードウェア実装のバグを回避することができます。

ちなみに、READMEのi2c_gpio_delay_usの項目で default "2" = ~100kHz という記述があり、クロックサイクルの半分なら辻褄が合わないのですが、コード等を読む限り、 ~100kHz が誤りで、正しくは 250kHz だと思われます。

該当のREADME

参照したソースコード

終わりに

Raspberry PiのI2Cのハードウェア実装のバグと、その回避方法としてのソフトウェア実装のI2Cについて解説しました。弊社でもRaspberry PiとI2Cを用いたプロダクト開発をしており、この問題に遭遇し、ソフトウェア実装を用いて解決しました。この記事が、このバグで困っている方、この分野に興味のある方の参考になれば幸いです。

脚注
  1. System on a Chipの略。CPU、メモリコントローラ、各種入出力インターフェースなどコンピュータの主要機能を1つの半導体チップに統合したもの ↩︎

  2. 元々controllerはmaster, targetはslaveと呼ばれていましたが、BLMの流れを受けて名称が変更されたようです。ウェブ上の情報ではまだまだmaster/slaveの方が多そうなので、もしこのあたり検索する場合はmaster/slaveという用語を用いると良いと思われます。 ↩︎

  3. https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf の 3.1. 参照 ↩︎

  4. https://github.com/raspberrypi/linux/issues/4884#issuecomment-1038929049 ↩︎

  5. https://www.raspberrypi.com/documentation/computers/config_txt.html#overclocking ↩︎

  6. https://github.com/raspberrypi/linux/issues/4884#issuecomment-1038929049 ↩︎

  7. https://github.com/raspberrypi/linux/issues/4884#issuecomment-1040673515 ↩︎

  8. https://www.raspberrypi.com/news/rp1-the-silicon-controlling-raspberry-pi-5-i-o-designed-here-at-raspberry-pi/ ↩︎

Discussion