13)こんどはI2Cで

SPIの次はI2Cのネタを書くだろうと思われている方のご期待に沿うべく、I2Cでもやってみよう。
今回もPIC12F1822を使用する。
下図のように接続している。
I2CはSCLとSDAの2本だけで済むので、パッケージが8ピンでも空きピンが使えてデバッグしやすい。計数するパルスはT1CKI(RA5)を出力に設定して、主な処理の途中で出力を反転させた。

mote側のappは02-i2cのi2c_app.cを改造して、CLIコマンドにデバッグ用のコマンドを追加した。
リードコマンド(rddata)とライトコマンド(wrdata)、I2Cアドレス設定コマンド(wraddr)の3つである。


I2Cアドレスの初期値は0x66とした。
CLIから"rddata 2"と打つとカウント値2バイトが読める。
バイト数は32バイトまで設定できるが、3バイト以降は適当な値(0x02からインクリメント)を返すようにしている。
ライトコマンドも受け付けるが、PIC側で受けた値は捨てている。
下図は"rddata 2"を打った時の観測結果である。


Sleepから復帰して再度Sleepするまで約520usecになっている。
SPI改よりもさらに短くなった。
PICのクロックは2MHzに設定している。

PICのSSP割り込み処理の先頭で内部OSCの安定(HFIOHL=1)を待っている。
この時間は上図の左のカーソルからRA5の最初の立ち上がりまでなので、およそ140usecくらいだ。
その後、SSP1IFチェック、SSP1IFフラグクリア、SSP1BUF読み出しまでが最初のRA5=1の間の処理である。
RA5をクリア後、Read ModeとWrite Modeの振り分け、AddressとDataの振り分けを行う。
ここではRead(mote <- PIC)、Address検出なのでRA5をセットしTMR1のカウンター値の読み出し処理を行う。
2番目のRA5=1の部分が、TMR1のカウンター値の読み出し処理時間である。
カウンター値を読むまでにRA5の立ち上がりエッジは2回あるので、カウンター値の最初の値は0x0002になる。
SSP1CON3のAHEN=1に設定しているので、ここまで9番目のSCL=1がストレッチされている。

RA5をクリア後、SSP1CON2のACKDTをクリアしSSP1CON1のCKPをセットするとSCLがリリースされる。
9番目のSCLの立下りで2回目のSSP1IF=1が来るのでSSP1IFチェック、SSP1IFフラグクリアを行いRA5をセットする。

3番目のRA5=1の部分では、TMR1Hの値をSSP1BUFに書き込み、SSP1CON1のCKPをセットしている。
SSP1CON2のSEN=1に設定しているので、SSP1BUFの書き込みが終わるまで次のSCL=1がストレッチされている。

以降、SSP1IFチェック、SSP1IFフラグクリア、NACKチェック、SSP1BUFの書き込み、CKPセットの繰り返しとなる。
NACK検出でループを抜けてSSP割り込み処理を終了する。
尚、Write Modeの処理はソースをご覧いただきたい。
また、外部よりT1CKIにパルス入力を接続する場合は、350行目と351行目を下記のように変更してほしい。

; MOVLW b'00001111' ; Set RA<3>,RA<2>,RA<1>,RA<0> as input
MOVLW b'00101111' ; Set RA<5>,RA<3>,RA<2>,RA<1>,RA<0> as input

ちゃんと動いてくれればSPI改よりも省電力だ。
I2Cで怖いのは2本の信号が抵抗を介してPullupされていることだ。
何らかの不具合でクロックストレッチの状態で固まると、(電源電圧÷Pullup抵抗)分の電流をずっと垂れ流ししてしまうのである。
電源電圧が3VでPullup抵抗が4.7KΩだと約640uAとなり、バッテリーで数年駆動を前提としている機器では致命的だ。
2本の信号ラインを引き回すような使い方はおすすめできない。

CLIから"rddata 20"と打つとこんな感じです。


□ソースをこちらに置きました。

コメント

このブログの人気の投稿

12)非同期パルス

2)OnChipSDKとは