ちょっと間が空きましたが、ベアメタル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されました。
MicroPython on bare metal Raspberry Pi Zero / Zero W @boochowp #piday @Raspberry_Pi https://t.co/AHNDmHYc9a
— adafruit industries (@adafruit) 2018年8月10日
今のところ、ブログのアクセスが増えたりということは無いですが、ちょっとは知名度が上がったかも?
コメント