MicroPython 外部 C モジュール¶
MicroPython で使うモジュールを開発する際、Python 環境では壁に突き当たってしまうことがあります。これは多くの場合、特定のハードウェアリソースにアクセスできないとか、Python の実行速度の遅さによるものです。
MicroPython 性能の最大化 に提示しているもので解決できない場合、モジュールの一部または全部を C 言語で書くのが現実的な解です。
作ろうとしているモジュールが一般的に入手可能なハードウェアやライブラリで動くように設計するのであれば、MicroPython ソースツリー中の似たモジュールに倣い、ソースツリー内で実装して、プルリクエストを出すことを検討してください。ただし、よく知られていないシステムた独自のシステムをターゲットにしている場合は、メインの MicroPython リポジトリの外部に保持するほうが合理的です。
この章では、そのような外部モジュールを MicroPython の実行ファイルまたはファームウェアイメージにコンパイルする方法について説明します。
外部 C モジュールの構造¶
MicroPython のユーザー C モジュールは、次のファイルを含むディレクトリーです:
モジュールのソースコードファイルである
*.c
や*.h
。これらのファイルには通常、C言語レベルでの機能実装と、それを MicroPython の公開関数/モジュールにバインディングするものから成っています。
現在のところ、このような関数/モジュールを書くための最良の参考資料は MicroPython のソースツリー内で似たモジュールを見つけて、それらを例として使うことです。
micropython.mk
はモジュール用の Makefile の一部を含みます。$(USERMOD_DIR)
は micropython.mk 内で利用可能で、モジュールのディレクトリパスを示します。これは各 C モジュールに対して再定義されるので、micropython.mk
の中でローカルの make 変数に展開しておくべきです。たとえばEXAMPLE_MOD_DIR := $(USERMOD_DIR)
とします。micropython.mk
ではモジュールの C ファイルをSRC_USERMOD
に追加する必要があります。これは$(USERMOD_DIR)
の展開コピーのディレクトリ中に置かれているものとして設定します。たとえばSRC_USERMOD += $(EXAMPLE_MOD_DIR)/example.c
のようにします。カスタムの
CFLAGS
設定またはインクルードファイルのフォルダ定義がある場合には、CFLAGS_USERMOD
に追加します。完全な使用例については下記を参照してください。
基本的な例¶
ここでとりあげる単純な名前のモジュール example
は、2つの整数引数を加算して結果を返す単一の関数 example.add_ints(a, b)
を提供します。
ディレクトリ構成:
example/
├── example.c
└── micropython.mk
example.c
// 第一に必要となるインクルードファイル
#include "py/obj.h"
#include "py/runtime.h"
#include "py/builtin.h"
// Python から example.add_ints(a, b) として呼び出す関数
STATIC mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) {
// MicroPython の入力オブジェクトから int 値を取得
int a = mp_obj_get_int(a_obj);
int b = mp_obj_get_int(b_obj);
// 加算を行い、MicroPython オブジェクトに変換
return mp_obj_new_int(a + b);
}
// 上記の関数への Python 参照を定義
STATIC MP_DEFINE_CONST_FUN_OBJ_2(example_add_ints_obj, example_add_ints);
// example モジュールのすべてのプロパティを定義。
// テーブルのエントリは、属性名(文字列)と MicroPython オブジェクト参照の
// キー/値のペア。
// すべての識別子と文字列は MP_QSTR_xxx として書き、ビルドシステムによって// ワードサイズの整数に最適化されます(内部文字列)。
STATIC const mp_rom_map_elem_t example_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example) },
{ MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) },
};
STATIC MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table);
// モジュールオブジェクトを定義
const mp_obj_module_t example_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&example_module_globals,
};
// Python で利用できるように make するようモジュールを登録
MP_REGISTER_MODULE(MP_QSTR_example, example_user_cmodule, MODULE_EXAMPLE_ENABLED);
micropython.mk
EXAMPLE_MOD_DIR := $(USERMOD_DIR)
# すべての C ファイルを SRC_USERMOD に追加
SRC_USERMOD += $(EXAMPLE_MOD_DIR)/example.c
# 必要であればモジュールのフォルダをインクルードパスに追加
# この例では実際には必要ない
CFLAGS_USERMOD += -I$(EXAMPLE_MOD_DIR)
最後に MODULE_EXAMPLE_ENABLED を 1 に定義する必要があります。これを行うには CFLAGS_EXTRA=-DMODULE_EXAMPLE_ENABLED=1
を make
コマンドに追加するか、 mpconfigport.h
か mpconfigboard.h
を編集して次のものを追加します。
#define MODULE_EXAMPLE_ENABLED (1)
ポートによって異なる構造になっているので、正しい方法はポートに依存することに注意してください。適切に行われなかった場合、コンパイルはされたとしてもモジュールのインポートはできないでしょう。
C モジュールを MicroPython にコンパイルする¶
モジュールをビルドするには make
コマンドに追加のフラグ USER_C_MODULES
で組み込みたいモジュールすべて(このモジュール自体ではない)のあるディレクトリを指定して、MicroPython をコンパイルします(getting started を参照)。たとえば次のような構成の場合:
ディレクトリ構成:
my_project/
├── modules/
│ └──example/
│ ├──example.c
│ └──micropython.mk
└── micropython/
├──ports/
... ├──stm32/
...
stm32 ポート用のビルド:
cd my_project/micropython/ports/stm32
make USER_C_MODULES=../../../modules CFLAGS_EXTRA=-DMODULE_EXAMPLE_ENABLED=1 all
MicroPython でのモジュールの使い方¶
MicroPython 自前でビルドすると、上記で実装したモジュール example.c
は、他の組み込みモジュールと同じように Python でアクセスできるようになります。たとえば次のように使います。
import example
print(example.add_ints(1, 3))
# should display 4