I2C通信を割り込みでハンドリングする

開発中の製品でマイコンに接続するある機器がI2C接続で、

効率よく通信しようとすると割り込み処理でやらないとな、

ということでいろいろ調べていたのだが、ちょっと複雑だった。


そもそもI2Cってなんやねんという話ですが。

I2Cとは (ROHM)

SCLとSDAの2つの通信線で複数の機器をぶらさげて通信できると。

ただ、今回の場合、この構成にそこまでメリットがあるわけでもない。

接続先の機器が1つで、さらに言えば送信一辺倒だからである。


I2Cの典型的な使い方としては、温度センサなどを複数ぶら下げて、

設定を送って、値を読み出すというような使い方が多いのかな。

I2C接続のEEPROMを何かで使ってたような記憶もあるが、

双方向でやりとりするような用途には適しているのかも。

SPIだと、CS, CLK, MOSI, MISO で4本ぐらい使うところ、SCLとSDAの2本でいけるので。


で、I2Cってけっこう複雑なんですよね。

データ送信する場合、STARTコンディションにして、アドレスを送って、

するとスレーブからACKが返ってくるのでこれを確認する。

そしてデータ1byteを送るとACKが返ってくるので確認する。

これを繰り返し最後にはSTOPコンディションにして終わりと。

これを割り込みで処理しようとするとACKされるたびに割り込みが入る形になる。

このマイコンではI2CとFIFOの組み合わせはできないらしい。


受診の場合は、アドレスを送ってACKを受け取る部分は同じ。

次にマスタはSCLのクロックを駆動して1byte受信する。

続けて受信する場合はACKを送信、最後の場合はACKを送信せずにSTOPコンディションにする。

この最後にACKを送信するか、しないかという判別のこともあり、

この受信では最後にACKを送るか、送らず終わるかを受信開始時に表明する必要がある。

このため受信完了時に都度割り込みが入り、それを消化するまでは次の受信が始まらない仕組みである。


この操作をDMAと組み合わせてやる方法があったが、えらく複雑だった。

これ、まだI2Cのマスタ側はよいけど、I2Cのスレーブ側はより複雑である。

そこで知ったけど、I2Cってのはスレーブ側がマスタを待たせる手段があって、

1byte受信し終えた後、SCLの立ち下がりを検出して、SDA=Loを出力、

それを確認後、マスタはSCLを立ち上げるわけだが、ここでSDA=Hiになるのを待つんですね。

このSDA=HiにせずにLoのまま保つことでマスタを多少待たせられると。

受信側にとってみれば次の受信時にACKを返すか、返さないかという選択肢がある。

これ以上受信するデータがない場合はACKを返さないというやり方もあるため。

(このあたりのポリシーはI2Cデバイスにもよる)

ここの処理が終わるまで待たせることもできるんですね。


結局やることとしては他の通信とそう大差はないのかもしれないが、

I2Cのステータス管理を1byteごとに行うのが原則なので、

そこをペリフェラルで肩代わりしてくれる部分もあって、

DMA転送の場合はその機能との組み合わせが前提ですよと、

あれこれ複雑なことが書いてあって、めんどくさい! となったのだった。

1byte単位で割り込み入れてもらうならポーリングのコードがかなり参考になってよかったが。