Raspberry Pi PicoのMicroPythonからscanvideoライブラリを利用できないかと思い、ここしばらく試行錯誤していました。
完成はしていないのですが、休暇も今日で終わりなので現状をメモとして残しておきます。
MicroPythonはCでモジュールを追加できますので、scanvideoを使うことも基本的には可能なはずです。
Cで書いたモジュールを追加するには、
(1)MicroPythonのport/rp2を直接編集する
(2)Exnternal C moduleとして実装する
(3)ネイティブコードの.mpyファイルを作る
という方法があります。ただし(3)はPicoを含めCortex-M0をサポートしていません(無理やり動かしている人は居るようですが)。
まず(2)をちょっと試しましたが、pico-extrasライブラリを外部Cモジュールから使おうとすると、慣れないcmakeの知識が必要になり、面倒くさくなって(1)の方法を取ることにしました。この場合は、MicroPythonのCMakeLists.txtがすでにpico-sdkを使えるように設定しているので、それを真似てpico-extrasを使えるようにできます。
次に、scanvideoライブラリ自体も一部修正が必要でした。scanvideoは自分が使うバッファをcallocで確保しています。しかし、MicroPythonはヒープをMicroPython自身が管理するので、Cモジュールはpico-sdkのメモリ管理機能を使ってはいけません。これは、とりあえずバッファをstatic変数として確保することで回避しました。
最後に、scanvideoはPIOを使用しますので、MicroPythonのPIOクラスとぶつかります。これ自体は予想していましたが、どうもそれに加えてscanvideoをリンクした時点で(APIを呼ばなくても)MicroPythonが動作しなくなってしまうようで(原因は不明)、やむなくソースコードからPIOクラス自体を外しました。
これでどうやらscanvideoライブラリを使うCモジュールが作れるようになりました。scanvideoを使うプログラムはこれまでにも作っていますので、あとは淡々と実装・・・と思っていたのですが、まだ何か他にも地雷があるようで、しばらく動かしているとscanvideoが動作しなくなったりMicroPythonごと落ちたりします。挙動からしてメモリ関連ではないかと思いますが、まだ原因を絞れていません。
ちなみに現状の実装は以下のような設計にしています。
- scanvideo(mode)
- scanvideoシングルトンオブジェクトを返す。modeはクラス変数VGA_MODE_640X480_60またはVGA_MODE_320X240_60を指定する(現在は前者のみサポート)。なお、この関数をコールした時点ではscanvideoの初期化は行わない。
scanvideoで画像を生成するには、scanlineデータを常に生成しつづける必要がありますが、その方法として
(1)タイマ割り込みを使う:start()、stop()
(2)whileループを使う:scanline_generation()
(3)メソッド自体が無限ループを内包する:render_loop()
という3つの方法を用意しています。穏便なのは(1)で、高速なのは(3)です。(2)は実験用ですね。
しかしいずれの方法を用いても、現状、安定性は低い(画像が乱れるレベルではなく、ハングアップする)です。
以下は画面をランダムな色で塗りつぶす例です。scanvideoの描画処理をCore1側で行わせるために、_threadクラスを使用しています。Core0側のfill(color)は、色の値の設定を行っているだけで、実際の描画はCore1側が行ないます。
from rp2extra import scanvideo
sc = scanvideo(scanvideo.VGA_MODE_640X480_60)
sc.test_pattern()
def start_video(sv):
sv.init()
sv.start()
import _thread
_thread.start_new_thread(start_video, (sc,))
from urandom import randint
sc.fill(sc.pixel_from_rgb(255,128,64))
for i in range(0, 100000):
sc.fill(randint(0,65536))
実行するとこんな感じの画面になります。
bitmap()を使うとPicoのMicroPythonにVRAMを持たせることができ、さらにframebufモジュールとFBConsoleを使ってそのVRAMにREPLを表示させることも可能です(すぐクラッシュしてしまいますが・・・)。
動画サンプルをいくつか、Twitterにも上げました。
It can fill the screen with given color; or render a bytearray as a bitmap. pic.twitter.com/mBlCKztTs2
— boochowp (@boochowp) May 5, 2021
And the framebuffer can display micropython's REPL by uos.dupterm() and FBConsole. VERY unstable, though… pic.twitter.com/pGBVgu7euP
— boochowp (@boochowp) May 5, 2021
コメント
http://vdmgr.g2.xrea.com/rpipico_vga.html
こちらのサイトの”フラッシュメモリ速度調査”によるとキャッシュの状態で実行速度が大きく低下するようです。
よくわかりませんが、タイミングが厳しい処理はこの影響を受けると思います。
ありがとうございます。リンク先のページ、色々調べられていて面白いですね。