I2C 通信ができないときのトラブルシューティング
組み込みシステムにおいては、センサや周辺デバイスをつなぐI2C 通信の理解が欠かせません。しかし、実際の開発現場では「通信できない」「ACK が返らない」といった不具合が頻発し、正常動作を担保するためには多くの時間と労力がかかります。
この記事では、不具合のループに陥ることなく効率的に原因を特定するためのポイントを解説します。実践的なトラブルシューティング手法と、一時的な通信エラーからシステムが確実に復帰する設計思想について理解できます。
- I2C通信ができないときのトラブルシューティング-ACKが返らない原因とバスハングアップ対策
- I2C 通信ができないときの波形確認!オシロスコープでのACK/NACK の見方
- なぜI2C が通信できない?ACK の仕組みとオープンドレインの基礎
- I2C 通信ができないときのソフトウェア対策!ACK タイムアウトとバス復旧
I2C通信ができないときのトラブルシューティング-ACKが返らない原因とバスハングアップ対策
マスタからスレーブへアドレスを送信した直後、ACK (応答信号)が返ってこない現象は、I2C 通信でもっとも頻発する初期トラブルの一つです。
まずは「仕様の解釈」「物理的な環境」「デバイスの状態」の3つの観点から論理的に原因を切り分けることが早期解決の近道となります。ここでは、見落としがちな原因と具体策を解説します。
スレーブアドレスの指定間違い
ソフトウェア側で最初に疑うべきは、マスタが指定しているスレーブアドレスの誤りです。まず確認すべき項目として、「対象のデバイスが10ビットアドレスを採用している可能性がないか」をチェックします。主流である7ビットアドレスのデバイスであっても、データシートには「7ビットアドレス」表記と、R/W ビットを含めた「8ビットアドレス(アドレスバイト)」表記が混在しているため注意が必要です。
たとえば、データシート上の7ビットアドレスが「0x50」の場合、実際の送出アドレスバイトはWrite 時が「0xA0」、Read 時が「0xA1」となります。マイコンのライブラリ仕様によっては、プログラム上で左に1ビットシフトさせた値(0xA0など)を引数に渡す必要がありますが、多くのAPI は7ビットアドレス(0x50)をそのまま渡す設計になっています。
そのため、7ビット渡しが前提のAPIに対して誤って左シフトしてしまうと、バス上に存在しないアドレスを呼び出し続けることになり、ACKは返りません。まずは使用するAPIや関数の仕様書を確認し、アドレスの指定が「7ビット渡し」なのか「アドレスバイト渡し」なのかを必ず検証してください。関数リファレンスの引数説明に「addr is 7-bit」と記載されていれば7ビット渡し前提であり、「8-bit address」や「including R/W bit」のような記載があればアドレスバイト渡し前提となります。
ハードウェアの配線やプルアップ抵抗の不備
プログラムが正しくても、物理的な結線に不備があれば通信は成立しません。I2C はオープンドレイン出力のため、SCL (クロック)とSDA (データ)両ラインに適切なプルアップ抵抗を接続し、High レベルに保持しておく必要があります。抵抗値は通常数kΩ が目安となりますが、実際にはバスの静電容量(バス容量)と通信速度によって決まるため、信号の立ち上がり時間が規格内に収まる範囲で選定しなければなりません。
プルアップ抵抗が未接続だとラインがHigh レベルに戻らず、また抵抗値が大きすぎると信号波形がなまり、正常にHigh レベルを認識できなくなります。また、開発現場で意外と多いのが「GND のつなぎ忘れ」です。マスタとスレーブの基板がわかれている場合、信号線だけでなくGND 線が接続されていないと、電圧レベルの基準が定まらず正常な通信ができません。
スレーブデバイスの初期化待ち・タイミング
ハードウェアもアドレスも正しいのにACK が返らない場合には、スレーブ側のデバイス状態が原因である可能性があります。
電源投入やリセットの直後、スレーブデバイスは内部の初期化処理を行っています。この処理が完了する前にマスタが通信を開始すると、スレーブは応答できずNACK となります。マスタ側の起動シーケンスにおいて、スレーブのデータシートに記載された「起動所要時間(Wake-up time)」を満たす十分なウェイト(待機時間)を設けてから通信を開始するよう設計を見直してください。
I2C 通信ができないときの波形確認!オシロスコープでのACK/NACK の見方
プログラム上の論理的な見直しでは解決しない場合、ソフトウェアのみでの原因特定は困難です。オシロスコープやロジックアナライザを用いて実際の信号の物理波形(SCL・SDA の電圧レベル)を観測し、トラブルシューティングをおこないます。波形から通信状態を読み解くための具体的なチェックポイントについて解説します。
正しいACK波形とNACK波形の違い
データやアドレスが正常に受信されたかは、オシロスコープ上の「9クロック目のSDAラインの状態」で判別できます。
マスタが8ビットのデータを送信し終えた直後、9番目のSCLパルスがHighになるタイミングに注目します。ACKビットの期間では、送信側がSDAを一度解放し、受信側がSDAをLowに引き下げることで「ACK(正常応答)」を表します。このとき、SDAラインがスレーブ(受信側)によって確実にLowレベルに引き下げられていれば「ACK波形」です。
一方、Lowに引き下げられずHighレベルのまま維持されていれば、応答していない「NACK波形」となります。ここで重要なのは、受信側がLowに引き下げられなかったケースだけでなく、送信側がSDAを解放していない(誤配線や実装ミスなど)ケースでも、波形上はNACKに見えてしまう点です。データ転送の区切りとなる9クロック目をピンポイントで確認し、誰がSDAを駆動すべきタイミングなのかを意識しながら、意図しないNACK波形が発生していないかを検証することが重要です。
波形のなまり(プルアップ抵抗値や配線長の影響)
波形観測では、デジタルなHigh レベル/Low レベル判定だけでなく、アナログ的な「信号の立ち上がり時間(波形のなまり)」にも着目します。
I2C はオープンドレイン構成のため、Lowレベル からHighレベル へ遷移する速度は、プルアップ抵抗値とバスの静電容量によるRC 時定数に依存します。抵抗値が大きすぎる場合や、配線が長すぎて寄生容量が増加した場合、波形が緩やかに立ち上がる「なまり」が発生します。
なまりが著しいと、規定のタイミングまでに電圧がHigh レベルのしきい値に達しないため、正常にHigh レベルを認識できずに通信エラーを引き起こします。立ち上がりが遅い場合にはクロック周波数を下げるか、仕様が許す範囲でより小さな値のプルアップ抵抗へ変更するなどといった物理的調整が求められます。
なぜI2C が通信できない?ACK の仕組みとオープンドレインの基礎
長期間の安定稼働が求められる組み込みソフトウェアでは、「とりあえず動いた」で終わらせず、プロトコルの仕様と物理的制約を正しく把握しておくことが大切です。ここでは、I2C 通信におけるエラー発生のメカニズムを解説します。
I2C 通信プロトコルの基本構成
I2C は、SCL とSDA の2本の信号線のみで通信をおこなう同期式シリアル通信です。通信時には、通信の主導権を握る「マスタ」と、要求に応答する「スレーブ」という役割を持ちます。デバイスによっては、双方を同時に実装できます。
クロック信号を生成し、通信の開始・終了を発行できるのはマスタのみです。スレーブは自発的にデータを送信できず、マスタからのアドレス指定とクロック供給を待つ受動的な状態に置かれます。単一マスタ構成の場合、この「一つのマスタがバス全体を統制し、指名されたスレーブだけが応答する」という主従関係が大前提となります。一方、I2C ではマルチマスタ構成も想定されています。
オープンドレイン出力の仕組み
I2C 通信、および「バスハングアップ」という致命的な障害の原因を理解する上で重要なのが「オープンドレイン出力」です。
一般的なマイコンの出力ピンは自らHigh とLow の両方を出力できますが、オープンドレイン出力は「自らLow に引き込むことしかできない」構造です。そのため、信号線をHigh にするには外部にプルアップ抵抗を接続し、電源電圧に引き上げておく必要があります。どのデバイスも Low レベルに引っ張っていないときだけ受動的にHigh レベルになる仕組みであり、「一つのデバイスが Low レベルに引いている限り、他の誰がどう頑張ってもHigh レベルにはならない」という物理的な制約が、バスを制御不能に陥らせることがあります。
ACK/NACK 信号が発生するタイミング
I2C通信では、データを8ビット送信するごとに、受信側がACKまたはNACKを返すルールになっています。マスタがスレーブにデータを送信する場合(Write)はスレーブがACK/NACKを返し、マスタがスレーブからデータを読み出す場合(Read)はマスタが各バイトにACKを返し、最後のバイトではNACKを返して「これ以上読み出さない」ことを通知します。この返答が9ビット目のクロックで発生するACK/NACK信号です。
たとえば、マスタからスレーブへデータを送信する(Write)場合、マスタは8ビットのデータをSDA線に送り終えると、一度SDA線を解放します。そして、9個目のクロックがHighの期間中にSDAのレベルをサンプリングし、受信側であるスレーブがSDA線をLowに引き込んでいれば「ACK」、Highのままであれば「NACK」と判定されます。
ここで重要なのは、NACKは必ずしも「異常」を意味しないという点です。前述のとおり、マスタが連続してデータを読み出す(Read)際、最後のデータを受け取った後に意図的にNACKを返し、「これ以上データは不要」とスレーブに伝達するプロトコル上の正常な手順としても使用されます。どのタイミングでどちらのデバイス(マスタかスレーブか)がSDAの制御権を持っているかを意識することが、波形解析の鍵となります。
I2C 通信ができないときのソフトウェア対策!ACK タイムアウトとバス復旧
異常が発生した際にシステム全体が停止しない「フェールセーフ」設計はきわめて重要です。ソフトウェア実装の観点から、致命的なフリーズを防ぎ、自律的に復旧するための手法を解説します。
エラーハンドリングとタイムアウトの考え方
通信処理の実装では「応答を無限に待ち続けてマイコンがフリーズする」事態を避けることが重要です。ACK 応答や「クロックストレッチ」の終了を待つ処理を単純なwhile ループなどで記述すると、異常時にシステム全体が停止します。
このリスクを回避するには、すべての待機処理に「タイムアウト」を導入することが欠かせません。タイムアウト後にはエラー通知やバスリカバリの処理、必要に応じてリトライを実施します。具体的には、タイマやカウンタを利用し、規定時間を超えて応答が無い場合はただちにエラーとして処理を中断するハンドリングを必ず実装します。これによりメインループに復帰し、安全に次の処理やリトライへ移行できます。
スレーブのフリーズ・バスハングアップの発生メカニズム
タイムアウトで無限ループを回避できても、バス自体が使用不能になる「バスハングアップ(SDA のLow 張りつき)」という厄介な問題が存在します。
バスハングアップは、スレーブが「0(Low)」を送信している最中に、マスタがリセットされたりクロック同期が外れたりすることで発生することがあります。オープンドレインの制約により、スレーブがSDA をLow に引いているかぎり、マスタから通信終了の合図(STOP コンディション)を出せません。結果、スレーブは「次のクロックがくるまでSDA をLow のまま保持し続ける」状態で固まることがあり、バスがビジー状態から復帰できなくなります(バスハングアップ)。
復帰不能を防ぐ自律的な復旧(バスリカバリシーケンス)
バスハングアップに陥った場合、マスタをソフトウェアリセットするだけではスレーブのフリーズを解除できず復帰不能となります。ここで、システムを自律的に立て直す代表的な手段の一つが「I2C バスリカバリシーケンス」です。
異常を検知したマスタのI2C機能を無効化し、SCL・SDA ピンを汎用GPIO 出力に切り換えます。次にマスタから手動でSCL ピンをHigh レベル/Low レベルにトグルし、クロックごとにSDA のレベルを確認します。最大9回クロックを送る間に、多くのスレーブは直前の送信処理を終えてSDAを解放できる場合があります。SDA が解放されてHigh レベルになったことを確認したら、マスタ側で一度SDAをLowに引き込み、SCLをHighにした状態でSDAをLow レベルからHigh レベルへ立ち上げて強制的にSTOP コンディションを作り、スレーブのバスをアイドル(フリー)状態に戻すことを試みます。
無事にSDA が解放された場合は、最後にピンをI2C ペリフェラルに割り当て直すことで安全に通信を再開できます。ただし、スレーブ側がSCL をLow レベルに保持し続けている場合や、非標準的なデバイスではこの手順で復旧しないこともあります。SCL がLow レベル固定のまま戻らない場合は、デバイス不良やハードウェアの短絡も疑い、対象デバイスの電源再投入やバスからの切り離しを含む段階的な復旧手順へ移行してください。
この自律的なリカバリ機構と段階的な設計こそが、過酷な環境で稼働を続ける組み込み機器の高い信頼性を支える要となります。


