I2Cの続編です
WEBではかなりI2Cの規格・プログラミングが公開されていますので、実際のトラブルに対する経験を書いてみたいと思います。
ハード的には
1)ウンともスンとも動かない時
テスターがあったら、動作させる前(電源入れた直後)にデバイス側とマイコン側のSCL,SDAの電圧を測ってみましょう。両方とも Highになっていますか?両方ともLowの場合はデバイス側の電源か、マイコン側のプルアップ抵抗をチェックしましょう。「デバイスのVdd端子を間違えて、動いてなかった」なんてことはよくあります。もちろんデバイスのアドレスデーター設定端子が合っているかチェックしましょう。アドレスが違うと反応しません。また送るアドレスデーターは間違えていませんか?
アドレスデーターの最下位ビットは Read(1)/Write(0)の区別のため実際のアドレスより送るデータは2倍の値になります。
2) どちらかのラインがLowのままの時
電源・プルアップ関係がOKならば、電源を切ってテスタでGNDとのショートチェックをしましょう。バスラインがどこかで半田くずかゴミでショートしていませんか?テスタはできれば導通でブザーが鳴る機能を使うとわかり易いですね。デバイスの接続端子ミスも可能性があります。
3) デバイスを繋ぐと動かない
マイコンから個別にラインを操作できるならば、SCLがHighのままにしてSDAをLowにしてみる。
ちゃんとラインが Lowになりますか?OKならSCLもLowにしてみてCLKラインがLowに制御できますか?
これがOKでまだ動かないならば、オシロスコープが必要ですね。
最初の8ビットアドレスデーターを送ってみて、ACKがデバイスから返ってきますか?デバイスからのACKのLowが電圧が完全に0Vにならないなど、送る側のLow電圧も高い場合、プルアップ抵抗が小さすぎるとか、デバイスの電源電圧の違いとかチェックしましょう。バスライン・クロックラインのの波形はOKですか?
4) アドレスデーターはACKが来るけど、その後動かない
CPUからの9ビット目のCLK処理はタイミング合ってますか?デバイスがACKを出したままCPUのCLKを待っている状態で止まっている可能性があります。
5) 時々動かない
デバイスがEEPROMなど書き込みに時間がかかるデバイスを使っていませんか?
書き込み時は数10mSかかる場合があるので、最後の書き込みからある程度時間がたたないとBUSY状態になって連続して書き込めません。EEPROMでも、あるアドレスから数バイト(8バイトぐらいが多い)は連続書き込み出来るが、それ以上は完了時間が必要な時がありますので、デバイスの規格表をチェックしましょう。
また、ラインにノイズ防止のためのコンデンサ、EMIフィルタ等入れている場合に波形がなまって送るデーターの値によってエラーになる場合があります。CLOCKスピードを下げて実験するかコンデンサEMIフィルタの値を小さくしてみます。プルアップ抵抗の値を小さくしても効果のある場合があります。
6) CPUは 3.3Vなのにデバイスは 5Vなのでどうしよう?
PICなどは入力電圧が 3.3V Vdd でも 5.5V入力を保障していますので問題ないですが、専用のレベル変換ICなどを利用するのも良いでしょう。
7) 連続読み込みが出来ない
デバイスからデーター読み込み時に ACKを返してますか?ACKを返さないと1バイトで終了と思って通信が終わってしまします。詳細はI2C Readのはなしで...
ソフト的には
1)まず開始時にバスラインを確認しましょう。
CLKは通常値を読めないので仕方がありませんが、DATAは読めるので Idle状態( CLOCK/DATAともHigh)の設定時に DATAが Lowだったら、どれかのデバイスがCLK待ちかも知れません。STARTとSTOPコンディションを送るつもりでまずDATA を Lowにして次にCLOCKをLowに、そして先にCLOCKを立ち上げ、その後DATAをHignにします。DATAはHighに戻りましたか?
2)ステータスフラグはOKか
ハードウエアを利用したI2Cドライバの場合は、ステータスフラグのリセットなどきちんとおこなっているかどうか確認しましょう。
H8用のサンプルプログラムを紹介します。
unsigned char set_device ( unsigned char dev ,unsigned short Addr )
{
int n ;
n = 100 ;
while (IIC2.ICCR2.BIT.BBSY); //【1】I2Cバスがフリーになるまで待つ
IIC2.ICCR1.BYTE = (IIC2.ICCR1.BYTE & 0xCF) | 0x30;//【2】マスタ送信モードに設定(MST=1, TRS=1)
do {
IIC2.ICCR2.BYTE = 0xBD; //【3】開始条件を発行(BBSY=1, SCP=0)
IIC2.ICDRT = SLA | Wbit; //【4】コントロールバイトを送る
while(! IIC2.ICSR.BIT.TEND); //TEND をチェックし、送信が完了するまで待つ
--n ;
if( n < 0 ){
return( 0 ) ; // 一定期間で完了しなかったら Errorを出して抜ける
}
} while ( IIC2.ICIER.BIT.ACKBR ); //【5】ACKの返送を確認
IIC2.ICDRT = (unsigned char)( Addr & 0x00FF ); //【7】下位メモリアドレスを送信
while(! IIC2.ICSR.BIT.TEND); //TEND をチェックし、送信が完了するまで待つ
return(1) ; // OK
}
3) エラーが出た時の対策をしましょう。
ACKが来なかった時、ACK待ちで止まってしまうことがあります。止まってしまってOKなら良いですが、一定時間(一定回数)リトライしてダメならデーター0xffをとりあえず返すなど待ちループに入ってしまうのを回避するようにしましょう。
4) 必要以上速いスピードを追求しない
デジタルのHigh/Lowパルスは雑音を伴いますし、高速にすればハード的に伝送品質が落ちます。必要な速度を考慮してマージンのあるスピードで制御しましょう。ソフト的待ち時間を使ってCLOCKを作っている場合など、High期間とLow期間が同じになるような波形を作ったほうが、ハード的デバッグなどタイミングがチェックしやすくなります。
5)データーは 0x00 から 0xFF まで送受してみましょう
また、アドレスを変えて反応が無くなることも確認して、デバイスが壊れた時の対応のチェックもしましょう。
参考書----------------------------------------------------