MicroPythonからラズパイのフレームバッファへ描画する

gpumoduletest.jpg

前回試した、Raspberry Piのベアメタルプログラミングでの画面表示を、ベアメタル版MicroPythonに載せてみました。
Raspberry PiのフレームバッファにMicroPythonから描画できました。

MicroPythonにはハードウェア非依存のフレームバッファ実装があります(extmod/modframebuf.c)。

framebuf — Frame buffer manipulation — MicroPython 1.9.3 documentation

framebufモジュールは、オブジェクト生成時にフレームバッファのメモリを外部からbytearrayオブジェクトとして与えることができます。
従って、前回のやり方でRaspberry PiのGPUにフレームバッファを確保させ、そのバッファのアドレスをMicroPythonのオブジェクトとして取得できれば、描画はframebufモジュールへ任せることができます。

従って、以下のような関数を作れば画面表示ができると考えました。

・fb_init(w=640, h=480, bpp=16, screen_w=0, screen_h=0, offset_x=0, offset_y=0)
フレームバッファを初期化する。スクリーンの解像度は、0が与えられたときはフレームバッファの解像度と同じとする。

・fb_data()
フレームバッファ用のメモリをbytearrayオブジェクトで返す。

・fb_rowbytes()
フレームバッファの1ライン分のバイトサイズを返す。(framebufではstrideというパラメータがあるが、単位はピクセル数なので、ピクセル数に変換する必要あり)

これをgpuというモジュールとして実装して、以下のように動作させてみた結果が冒頭のスクリーンショットです。

MicroPython 7db564a-dirty on 2018-03-24; Raspberry Pi with ARM1176JZF-S
>>> import gpu
>>> import framebuf
>>> gpu.fb_init(480,270,screen_w=1920,screen_h=1080)
>>> fb = framebuf.FrameBuffer(gpu.fb_data(),480,270,framebuf.RGB565)
>>> fb.text('MicroPython!',0,0,0xffff)
>>> fb.text('MicroPython!',32,8,0xf800)
>>> fb.text('MicroPython!',16,16,0x07a0)
>>> fb.text('MicroPython!',48,24,0x001f)
>>> fb.line(0,0,480,270,0x7689)
>>> fb.rect(200,100,90,45,0x3333)
>>> fb.fill_rect(200,200,90,45,0x1128)
>>> gpu.fb_rowbytes()
960
>>> fb.scroll(40,0)
>>> fb.scroll(0,30)

このモジュールの現時点の実装はこちらにあります。

new feature: gpu module implements video core mailbox interface and f… · boochow/micropython-raspberrypi@d9e09b6

ポイントは、GPUが確保したメモリをMicroPythonのbytearrayに変換する部分ですが、mp_obj_new_bytearray_by_refという関数があらかじめ用意されていました(こちらの記事)ので、これを使いました。

MicroPythonへモジュールを追加する方法については、以前こちらの記事でもちょっと書きましたが、あらためて関数を追加する方法なども含めてまとめておきます。

なお、以下の項目に対応する型やマクロの定義はMicroPythonのpy/obj.hにまとまっています。

●モジュールの追加
・新しいモジュールのためのCソースファイルを作り、Makefileに追加する。
・Cソースの中で、mp_obj_module_t型(構造体)の定数を宣言する。メンバ変数はbaseとglobalsの2つ。これがこのモジュールを表す内部定数となる。
・その定数をmpconfigport.hの中でextern宣言して参照できるようにする。
・mpconfigport.hの中でMICROPY_PORT_BUILTIN_MODULESへ、

{ MP_ROM_QSTR(MP_QSTR_モジュール名), MP_ROM_PTR(&上記の定数) }

を追加する。

●モジュール内グローバルオブジェクトの追加
・mp_rom_map_elem_t型の定数配列を宣言する。これがモジュール内グローバルオブジェクトのリストとなる。
・MP_DEFINE_CONST_DICT(定数名,上記の定数配列の名前)でmp_obj_dict_t型の定数を宣言する。
・mp_obj_dict_t型の定数を、モジュールを表すmp_obj_module_t型のglobalsからポイントする。
・グローバルオブジェクトのリストへ、

{ MP_ROM_QSTR(MP_QSTR_オブジェクト名), マクロ(オブジェクトの実体) }

という形式でグローバル変数を追加する。
「マクロ」はオブジェクトの実体がintならMP_ROM_INT(整数値)、それ以外ならMP_ROM_PTR(ポインタ)。

●関数の追加
・関数の実体は、mp_obj_t型を返す関数として定義する。
・マクロMP_DEFINE_CONST_FUN_OBJ_XXで関数をMicroPythonのオブジェクトとして定義する。XXの部分は関数の引数の個数により異なる。(0~3:0個~3個、VAR:可変長、VR_BETWEEN:最小個数と最大個数が決まっている、KW:キーワード=値の形式を用いる)
・グローバルオブジェクトのリストmp_rom_map_elem_t型配列へ

 { MP_ROM_QSTR(MP_QSTR_関数名), MP_ROM_PTR(&関数名オブジェクト名) }

を追加する

こんな感じです。

今回実装したモジュールの、インタフェース部分だけを抜き出した雛形的なものを作ってみましたので、上記の解説と併せてご覧ください。

コメント