MicroPython扩展C模块

在开发用于 MicroPython 的模块时,您可能会发现您遇到 Python 环境的限制,通常是由于无法访问某些硬件资源或 Python 速度限制。

如果通过最大化 最大化MicroPython速度 速度中, 的建议无法解决您的限制,则用C(和/或 C++,如果为您的端口实现)编写部分或全部模块是一个可行的选择。

如果您的模块旨在访问或使用常用硬件或库,请考虑在 MicroPython 源代码树中与类似模块一起实现它,并将其作为拉取请求提交。但是,如果您的目标是晦涩的或专有的系统,则将其保留在主 MicroPython 存储库的外部可能更有意义。

本章介绍如何将此类外部模块编译为 MicroPython 可执行文件或固件映像。

另一种方法是 在 .mpy 文件中 使用本机机器代码 .mpy 文件中的本机机器代码 ,它允许编写放置在 .mpy 文件中的自定义 C 代码,该代码可以动态导入到正在运行的 MicroPython 系统中,而无需重新编译主固件。

外部 C 模块的结构

MicroPython 用户 C 模块是一个包含以下文件的目录:

  • *.c*.h 的源代码文件的模块。

    这些通常包括正在实现的低级功能和用于公开函数和模块的 MicroPython 绑定函数。

    目前编写这些函数/模块的最佳参考是在 MicroPython 树中找到类似的模块并将它们用作示例。

  • micropython.mk 包含此模块的 Makefile 片段。

    $(USERMOD_DIR) 可用作 micropython.mk 模块目录的路径。因为它是为每个 c 模块重新定义的,所以应该在您``micropython.mk`` 的本地 make 变量中扩展,例如 EXAMPLE_MOD_DIR := $(USERMOD_DIR)

    你的``micropython.mk`` 必须将模块源文件添加到``$(USERMOD_DIR)`` 到``SRC_USERMOD`` 的扩展副本中,例如 SRC_USERMOD += $(EXAMPLE_MOD_DIR)/example.c

    如果您有自定义编译器选项 CFLAGS``(例如 -I 添加目录以搜索头文件),则应将这些选项添加到C代码的 ``CFLAGS_USERMOD 和C++代码的CXXFLAGS_USERMOD 。

    请参阅下面的完整使用示例。

基本示例

这个名为的简单模块 example 提供了一个函数``example.add_ints(a, b)`` ,它将两个整数参数相加并返回结果。它可以 在示例目录 的 MicroPython 源代码树中找到, 并且有一个源文件和一个包含上述内容的 Makefile 片段:

目录结构:

example/
├── example.c
└── micropython.mk

example.c

// Include required definitions first.
#include "py/obj.h"
#include "py/runtime.h"
#include "py/builtin.h"

// This is the function which will be called from Python as example.add_ints(a, b).
STATIC mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) {
    // Extract the ints from the micropython input objects
    int a = mp_obj_get_int(a_obj);
    int b = mp_obj_get_int(b_obj);

    // Calculate the addition and convert to MicroPython object.
    return mp_obj_new_int(a + b);
}
// Define a Python reference to the function above
STATIC MP_DEFINE_CONST_FUN_OBJ_2(example_add_ints_obj, example_add_ints);

// Define all properties of the example module.
// Table entries are key/value pairs of the attribute name (a string)
// and the MicroPython object reference.
// All identifiers and strings are written as MP_QSTR_xxx and will be
// optimized to word-sized integers by the build system (interned strings).
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);

// Define module object.
const mp_obj_module_t example_user_cmodule = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t*)&example_module_globals,
};

// Register the module to make it available in Python
MP_REGISTER_MODULE(MP_QSTR_example, example_user_cmodule, MODULE_EXAMPLE_ENABLED);

micropython.mk

EXAMPLE_MOD_DIR := $(USERMOD_DIR)

# Add all C files to SRC_USERMOD.
SRC_USERMOD += $(EXAMPLE_MOD_DIR)/example.c

# We can add our module folder to include paths if needed
# This is not actually needed in this example.
CFLAGS_USERMOD += -I$(EXAMPLE_MOD_DIR)

Finally you will need to define MODULE_EXAMPLE_ENABLED to 1. This can be done by adding CFLAGS_EXTRA=-DMODULE_EXAMPLE_ENABLED=1 to the make command, or editing mpconfigport.h or mpconfigboard.h to add

#define MODULE_EXAMPLE_ENABLED (1)

Note that the exact method depends on the port as they have different structures. If not done correctly it will compile but importing will fail to find the module.

将 cmodule 编译成 MicroPython

To build such a module, compile MicroPython (see getting started) with an extra make flag named USER_C_MODULES set to the directory containing all modules you want included (not to the module itself). For example:

目录结构:

my_project/
├── modules/
│   └──example/
│       ├──example.c
│       └──micropython.mk
└── micropython/
    ├──ports/
   ... ├──stm32/
      ...

Building for stm32 port:

cd my_project/micropython/ports/stm32
make USER_C_MODULES=../../../modules CFLAGS_EXTRA=-DMODULE_EXAMPLE_ENABLED=1 all

MicroPython 中的模块使用

一旦内置到您的 MicroPython 副本中,该模块现在可以像任何其他内置模块一样在 Python 中访问,例如 example.c

import example
print(example.add_ints(1, 3))
# should display 4