ベアメタルRPi版MicroPythonのI2C.writetoを修正

ちょっと間が空きましたが、ベアメタルRaspberry Pi版MicroPythonのメモです。

先日、フォーラムのこちらのやり取りで、I2C.writeto()のrepeated STARTの仕様を私が間違えていたことが判明しましたので、その修正を行いました。

I2Cのrepeated START自体は、以前実装した際にケアしていました。
当時の記事は以下です。

I2Cクラスを追加(RPiベアメタルMicroPython) – 楽しくやろう。

記事中では、writeのあとreadする場合のrepeated STARTについての記載は正しいのですが、writeのあとさらにwriteする場合については間違っています。
具体的には

しかし、中レイヤのAPIでstopパラメータを使えばとすることで同等の処理が行えるはずです。

の部分です。

この「同等の処理」とは1行目の0x40のあとに2行目のbufが連続して送られることを指しているのですが、I2Cの仕様としては、このコードは

・START→スレーブアドレス→0x40→pseudo STOP
・repeated START→スレーブアドレス→bufのデータ

という信号を生成するべきなので、0x40とbufを連続して送ることはできません。

というわけで実装をちょこっと修正しました。

連続してデータをwriteする仕組みについては、現在議論されているPR4020の仕様(I2C.writeto(addr, [cmd, data])という記法を許容。cmdとdataは連続して送信される)を実装する際に役立ちそう(かつ、I2C.writeto_mem()の実装で使用している)なので温存し、新たにi2c_write_start()という関数をi2cドライバに追加しました。

i2c_write_start()は、データ長0xffffのwriteを行う(ただしFIFOには何も書かないまま)関数です。STARTビットを送り、続いてスレーブアドレスを送り、その時点でFIFOが空なら送信データ待ち(STOPは送らない)になります。
また、呼ばれたときに直前のwriteが終了していない場合にはFIFOをリセットすることによってpseudo STOPを送信させます。
ソフトウェア実装I2CにおいてSTARTビットを送信する低レイヤAPIであるstart()に似ていなくもないのですが、Raspberry PiのI2CインタフェースはレジスタにWriteかReadかを設定しないと通信を開始できないので、start()の代用にはなりません。

MicroPythonからは、I2C.writeto()を呼ぶと、
i2c_write_start(addr, stop) → i2c_write(addr, buf, len, stop)
の順に実行されます。

直前のI2C.writeto(buf, len, stop)において、stop=FALSEだった場合には、続くI2C.writeto()を実行する際にpseudo STOP + repeated STARTが送信されます。

たとえば

i2c.writeto(60,bytearray([0,1]),False);i2c.writeto(60,bytearray([4]))

を実行すると、以下のような信号が生成されます。

このrepeated STARTの部分を拡大すると以下のようになり、STOPへ移行していないことが分かります。

これで修正はできたのですが、残念ながらSSD1306のドライバが使えなくなってしまいました。
ハードウェアI2CでSSD1306を使うには、0x40の後に送信データをコピーするというメモリ利用効率の悪い方法を使うか、前述のPR4020の新しいAPIを使う必要があります。
PR4020の仕様が固まったらなるべく早期に実装したいと思います。

ところで、嬉しいことに先日Adafruitさんから、このベアメタル版MicroPythonについてtweetされました。

今のところ、ブログのアクセスが増えたりということは無いですが、ちょっとは知名度が上がったかも?

コメント