本稿からいよいよコマンドからI2Cの制御ができるようにしていきます。
ここで考慮したいのが使い勝手と処理スピードのバランスです。例えばPythonから直接SCL, SDAが見えて制御できれば何でもできるわけですが、あまりにも非効率なのは議論を待たないでしょう。しかし今あるライブラリの関数だとデバッグ性も拡張性も低い。ではどこら辺りが落としどころなのか?
ここでもう一度、I2Cプロトコルのおさらいをしますと、
===
(1) データ転送は、SCL=highの時SDAを安定させ、SCL=lowの時SDAを変化させて行うのが基本。
(2) SCL=highの時SDAをhigh->lowとしたらSTART、SCL=highの時SDAをlow->highとしたらSTOP。全てのトランザクションはSTARTで始まりSTOPで終わる。START->STOPの間にSTARTが再びあっても良い。START/STOPはマスター側からしか起こさない。
(3) 送信側が8bitデータを転送した直後、受信側がACK or NACKを返す。マスターが送信側の時はスレーブが受信側だがその逆もある。ACK (NACK)とは、SCL=highの時SDA=low(high)で示す。
===
以上を踏まえてプロトコルを次のように分類します。
===
(1) マスターから一方通行でリプライのないアクション。
(2) マスターからアクションを起こしてリプライを待つ。
(3) マスターが予期しないときスレーブからの割り込み動作は考慮しない。
===
(1)には、START, STOPが入ります。
(2)を更に整理すると
(2-1)マスターから1byteデータを送ってスレーブからのACKを待つ。1byteデータには、7bitのslave address+R/W識別bit、8bitデータ、のバリエーションがある。1つのコマンドでは使いにくいので3つに分ける。シェルからコマンド打つとき、頭の中でアドレスを1bitシフトしてR/Wの1bitをつけて、は面倒だと思った。
(2-2)「マスターから何らかのアクション」を起こされてスレーブからデータを送るシーケンスが始まる。データ受信後マスターがACKを返せば継続、NACKを返せばそこで終了。「マスターから何らかのアクション」は(2-1)に分類される。マスターが起動した後にマスターがとるアクションにはACKを返すかNACKを返すか2種があるのでコマンドを分ける。アルファベット1つに常に引数をつけるよりコマンドを分けた方がいいと判断した。
===
と言うわけで最終的には次のように分類できます。先頭のアルファベットがコマンドです。hexaの0~9,A~Fを避けたらこうなりました。
S:START
P:STOP
R:7bitのslave addressにRと識別する1bitをつけて1byte writeしてACKを待つ
W:7bitのslave addressにWと識別する1bitをつけて1byte writeしてACKを待つ
T:8bitのregister address/dataを1byte writeしてACKを待つ
K:slaveから8bit送ってきたらACKを返す
N:slaveから8bit送ってきたらNACKを返す
ここまで