あとは、マスター受信をタイムアウトでなく、まともなストップコンディションで、通信を終了することだ。GPIOによるAVRのスレーブインターフェース完成(9/1/2015)やった、やりました。久しぶりの勝利の美酒。マスター読み込みが最後までトラブっていたが、本日、遂に、タイムアウトを起こさず、一連のデータ送信(スレーブから)が問題なく終了した。マスター読み込みは、通信の終わりにマスターからNACKビットが来るのでストップコンディションを調べる必要もない(マスターは送っているが)はずなのだが、このNACKを認めず、常にタイムアウトになっていた。送信のエラーがとれて余裕が出来たので、ロジアナのプローブをさらに追加し後処理のプロセスに入れ、受信のシーケンスを仔細に追った。その結果、意外なところが原因であることが分かった。いやいやロジアナさまさまである。実は、NACKを認めていなかったのではなく、プログラムはNACKを見てとっくに通信を終了させていたのだが、次のストップコンディションの発行をスタートコンディションと勘違いして、この通信のタイムアウトが発生していた。これがタイムアウトの正体だった。間違っていたのは、スレーブ側ではなく、何と送り側(マスター)のストップコンディション発行の仕方だった。ストップコンディションの前に準備するSDAとSCLの変化のタイミングがロジアナを見ると少し短すぎる。このためSDAの変化の把握が割り込みで遅れ、SCLがHighになったところで認識されるのでスタートコンディションになってしまっていたのだ。うーむ、これは現在のロジックでは解決できない。頭を抱えていたが、SCLの変化を少し遅らせればこれは回避できることに気が付いた。マスターでのロジックにwaitを加え試してみる。めでたくスレーブ受信のタイムアウトはなくなった。あわてて、おおもとのNXP社の正式仕様を調べる。P48に、スタート/ストップコンディションのSDAとSCLの変化は半クロック(100kbpsで4μs)空けることというスペックを見つけた。マスターの修正はスペック通りだった。I2Cに自信がついてきた。最初良くわからなかった、データラインのChaNさんの方式(入力方向にしてラインHigh、出力方向にしてLow)が完全に理解できたのは大きい。I2Cの基本の部分である、プルアップされたライン上で複数のディバイスの間で通信するテクニックである(ワイヤードORというのだそうだ)。これまでは見よう見まねでI2Cマスターなどを書いていたが、こんな複雑な(面白い)しかけがあったということがわかっただけでも(いまさらながら)、こういう自作ならではの収穫である。Arduinoなどのラッパー製品を使っているだけではまず出来ない経験だろうと胸を張る(単なる負け惜しみに近いが)。お約束のソースコードの公開は整理してからと考えたが、少し気が変わった。このテストステートメントてんこ盛りの形のままの方が、かえって参考になるような気もする。何に苦労したかが一目瞭然で情報量は多い。整形した完成品のソースより参考になると思う。最後のシーケンスの連続化でまたてこずる。完成(9/5/2015)とはいうものの、もう少し動かして動作を確認しておいた方が良いだろう。当初予定したSR04とI2Cの送受信の組み合わせも動かしておきたい。これが動けば大威張りだ。自信を持ってソース公開に踏み切れる。マスター側からコマンドを送って、SR04の測定をスタートさせ、得られたデータを送り返させるシーケンスである。これが当初の目的である。I2Cを使ったSR04の制御の第一歩だ。ところが、これがまた難航したのである。送信、受信とも個別では全く問題なく動作する。ロジアナで見ても、綺麗な波形が想定通りに出ている。今度は、これの組み合わせだが、やることは同じだ。マスター側のプログラムに手を入れて、通信シーケンスを出すコマンドを作って動かした。これがまた全く動かない。やれやれどこが悪いのか。ロジアナで波形を見る。ありゃりゃあ、書き込みが終わった後の読み込みのシーケンスがでたらめである。そもそも最初のスタートコンディションを全く拾っていない。これは何か別のスレーブでの動作が邪魔していることは明らかである。最大の容疑者はUARTだ。ここのUARTはソフトUARTなので文字送信中は割り込みを禁止している。ロジアナに、送信関数の割り込み禁止部分にGPIOピンをあてて、波形をとってみたら一目瞭然だった。考えてみたら至極当然のことなのだが、マスター側で待ちを予想してwaitをとっていたのだが圧倒的に少なすぎた。今度もロジアナのお世話に大変なった。いやあ、こいつは無敵だ。リピートスタートコンディションの認知も出来た(9/12/2015)最後に残ったリピートスタートコンディションの実装である。EEPROMのI2Cのように、コマンドを送ってストップコンディションを発行せず、再びスタートコンディション(リピートスタートコンディション)を出して、データ読み込みに切替え、1シーケンスでデータを受け取る機能である。今度のSR04のシーケンスでは使いにくい(測定に要する時間が長すぎて1シーケンスにしにくい)が、内部のディバイスの状況を伝えるときには便利で実装しておきたい。それに、ストップコンディションを実装しているとき、ここに少し手を加えれば、リピートスタートコンディションもすぐ作れそうな感触だった。久しぶりの仕事があって電子工作に手が出ない日が続いていたが、一段落したので早速、リピートスタートコンディションの実装にとりかかる。考えていた通り、マスター送信のストップコンディション検知部にコードを追加する。if文にelseを加えるだけである。ほどなく出来上がった。こいつはテストが厄介で、送り側のプログラムにも手を入れる必要があるが、乗りかかった船である。黙々とこちらも準備する。マスター書き込み宣言で1バイト送った後、連続してスタートコンディションを発行し、マスター読み込みのシーケンスを送り込むコマンドである。スレーブ側バッファーに残っているSR04の距離データを呼び出すコマンドである。スレーブ側は、多重処理になるのでスレーブ側のテスト用のUART出力は厳禁だ。慎重にwaitステートメントを入れてUARTをずらす(これがないとやっぱり不安だ)。頻々とエラーが起きる。_delay_us()のマクロは、ある程度以上の待ちになるとおかしくなって、ちゃんと遅れてくれないようだ。ウェブを見るがどうも要領を得ない。_delay_msなどに切り替えて凌ぐ。ロジアナで波形を見ながら徐々にデータを正確にしていく。最後に残ったのが、リピートスタートをしたシーケンスのあとに、必ずマスター書き込みデータが化ける不具合だった。リピートスタートは、データの第一ビットを読んだあと初期化されるので、バッファーにデータが残る可能性があるのだが、それがどうして汚れるのか理屈がつかない(必ずというのが気に入らない)。なったら対症療法になるけれど、シーケンスに入る前に必ずバッファーをクリアしてから始めることにした。これで想定した機能はすべて揃ったことになる(マスター読み込みのあとのリピートスタートコンディションと、ストップコンディションは未実装だが)。完成まで1ヶ月を要したが、GPIOだけによるI2Cスレーブインターフェースが完成した。ベータバージョンだけれど、とりあえずソースコードを公開することにする。スレーブの861の空いているI/Oピンは、殆どがロジアナのプローブに使われている。以下にスレーブ側のソース一式(プロジェクト名がSR04でなくHR04になっています。プロジェクト名を変える方法がわからないのでそのまま。ご了承ください)と、マスター側のプロジェクトをAtmelStudioのフォルダーの形でかためたものを置きます。ソースコードにはあえてロジアナのプローブステップを沢山残しています。