MicroPython 是Python 3编程语言的精简高效实现, 其中包含 Python 标准库的一小部分,并针对在单片机和受限环境中运行进行了优化。MicroPython遵循python的语法规则。当我们想要添加MicroPython模块时,一般有两种方法,一种是用python脚本编写,一种是用C语言编写。python脚本编写时更方便,更快捷,但是功能受限,灵活度受限。用C语言编写,然后编译进固件,更灵活,自由度更大,但是难度也更大。如果想要编写一些更加贴近底层的模块,例如直接操作寄存器来控制单片机做某种功能,然后暴露给Python层,被Python直接调用,则必须用C语言编写MicroPython模块。
  MicroPython的官网为https://micropython.org/,源代码在github上,地址为:https://github.com/micropython/micropython
  MicroPython可以调用不同的模块(module),这些模块可以包含变量函数,以及类型等。在MicroPython中可以使用help()函数查看简单的帮助。例如在REPL下直接输入help(),可以显示基本的帮助界面,以ESP32的MicroPython为例,显示如下:

Welcome to MicroPython on the ESP32!

For generic online docs please visit http://docs.micropython.org/

For access to the hardware use the 'machine' module:

import machine
pin12 = machine.Pin(12, machine.Pin.OUT)
pin12.value(1)
pin13 = machine.Pin(13, machine.Pin.IN, machine.Pin.PULL_UP)
print(pin13.value())
i2c = machine.I2C(scl=machine.Pin(21), sda=machine.Pin(22))
i2c.scan()
i2c.writeto(addr, b'1234')
i2c.readfrom(addr, 4)

Basic WiFi configuration:

import network
sta_if = network.WLAN(network.STA_IF); sta_if.active(True)
sta_if.scan()                             # Scan for available access points
sta_if.connect("<AP_name>", "<password>") # Connect to an AP
sta_if.isconnected()                      # Check for successful connection

Control commands:
  CTRL-A        -- on a blank line, enter raw REPL mode
  CTRL-B        -- on a blank line, enter normal REPL mode
  CTRL-C        -- interrupt a running program
  CTRL-D        -- on a blank line, do a soft reset of the board
  CTRL-E        -- on a blank line, enter paste mode

For further help on a specific object, type help(obj)
For a list of available modules, type help('modules')

  通过输入help(‘modules’),可以查看运行的固件里面MicroPython 支持的所有固件

__main__          flashbdev         select            ujson
_boot             framebuf          socket            uos
_onewire          gc                ssl               urandom
_thread           hashlib           struct            ure
_webrepl          heapq             sys               uselect
apa106            inisetup          time              usocket
array             io                ubinascii         ussl
binascii          json              ucollections      ustruct
btree             machine           ucryptolib        utime
builtins          math              uctypes           utimeq
cmath             micropython       uerrno            uzlib
collections       network           uhashlib          websocket
errno             os                uhashlib          zlib
esp               random            uheapq
esp32             re                uio
Plus any modules on the filesystem

一、help(‘modules’)运行的C语言流程

  在MicroPython 源码的builtinhelp.c文件里面有mp_builtin_help函数,该函数的作用是创建内部的help功能。

STATIC mp_obj_t mp_builtin_help(size_t n_args, const mp_obj_t *args) {
    if (n_args == 0) {
        // print a general help message
        mp_print_str(MP_PYTHON_PRINTER, MICROPY_PY_BUILTINS_HELP_TEXT);
    } else {
        // try to print something sensible about the given object
        mp_help_print_obj(args[0]);
    }

    return mp_const_none;
}

  当调用mp_builtin_help函数时,如果没有传入参数,则打印帮助文档,帮助文档为MICROPY_PY_BUILTINS_HELP_TEXT。在mpconfig.h文件内有

// Use this to configure the help text shown for help().  It should be a
// variable with the type "const char*".  A sensible default is provided.
#ifndef MICROPY_PY_BUILTINS_HELP_TEXT
#define MICROPY_PY_BUILTINS_HELP_TEXT mp_help_default_text
#endif

  而在mpconfigport.h文件内又有如下定义:

#define MICROPY_PY_BUILTINS_HELP_TEXT       esp32_help_text

  说明如果宏定义MICROPY_PY_BUILTINS_HELP_TEXT 没有定义,则MICROPY_PY_BUILTINS_HELP_TEXT 指向的是mp_help_default_text,否则指向的是esp32_help_text。esp32_help_text就是上面当输入help()时打印的字符串数组。
  如果调用mp_builtin_help函数时传入了参数参数,则会执行mp_help_print_obj函数。mp_help_print_obj函数定义如下:

STATIC void mp_help_print_obj(const mp_obj_t obj) {
    #if MICROPY_PY_BUILTINS_HELP_MODULES
    if (obj == MP_OBJ_NEW_QSTR(MP_QSTR_modules)) {
        mp_help_print_modules();
        return;
    }
    #endif

    mp_obj_type_t *type = mp_obj_get_type(obj);

    // try to print something sensible about the given object
    mp_print_str(MP_PYTHON_PRINTER, "object ");
    mp_obj_print(obj, PRINT_STR);
    mp_printf(MP_PYTHON_PRINTER, " is of type %q\n", type->name);

    mp_map_t *map = NULL;
    if (type == &mp_type_module) {
        map = &mp_obj_module_get_globals(obj)->map;
    } else {
        if (type == &mp_type_type) {
            type = MP_OBJ_TO_PTR(obj);
        }
        if (type->locals_dict != NULL) {
            map = &type->locals_dict->map;
        }
    }
    if (map != NULL) {
        for (uint i = 0; i < map->alloc; i++) {
            if (map->table[i].key != MP_OBJ_NULL) {
                mp_help_print_info_about_object(map->table[i].key, map->table[i].value);
            }
        }
    }
}

  该函数的内容就是打印所有的module,还是打印引入的module。打印所有的module调用的是mp_help_print_modules函数,mp_help_print_modules函数定义如下:

STATIC void mp_help_print_modules(void) {
    mp_obj_t list = mp_obj_new_list(0, NULL);

    mp_help_add_from_map(list, &mp_builtin_module_map);

    #if MICROPY_MODULE_WEAK_LINKS
    mp_help_add_from_map(list, &mp_builtin_module_weak_links_map);
    #endif

    #if MICROPY_MODULE_FROZEN_STR
    extern const char mp_frozen_str_names[];
    mp_help_add_from_names(list, mp_frozen_str_names);
    #endif

    #if MICROPY_MODULE_FROZEN_MPY
    extern const char mp_frozen_mpy_names[];
    mp_help_add_from_names(list, mp_frozen_mpy_names);
    #endif

    // sort the list so it's printed in alphabetical order
    mp_obj_list_sort(1, &list, (mp_map_t*)&mp_const_empty_map);

    // print the list of modules in a column-first order
    #define NUM_COLUMNS (4)
    #define COLUMN_WIDTH (18)
    size_t len;
    mp_obj_t *items;
    mp_obj_list_get(list, &len, &items);
    unsigned int num_rows = (len + NUM_COLUMNS - 1) / NUM_COLUMNS;
    for (unsigned int i = 0; i < num_rows; ++i) {
        unsigned int j = i;
        for (;;) {
            int l = mp_print_str(MP_PYTHON_PRINTER, mp_obj_str_get_str(items[j]));
            j += num_rows;
            if (j >= len) {
                break;
            }
            int gap = COLUMN_WIDTH - l;
            while (gap < 1) {
                gap += COLUMN_WIDTH;
            }
            while (gap--) {
                mp_print_str(MP_PYTHON_PRINTER, " ");
            }
        }
        mp_print_str(MP_PYTHON_PRINTER, "\n");
    }

    // let the user know there may be other modules available from the filesystem
    mp_print_str(MP_PYTHON_PRINTER, "Plus any modules on the filesystem\n");
}

  mp_help_print_modules函数利用mp_help_add_from_map函数和mp_help_add_from_names函数将整个MicroPython系统可用的module全部组合加到了一起存储到了结构体list里面,然后逐个打印list里面的module。

二、C语言编写的模块的(module)分类及添加

  在mp_help_print_modules函数里面一共增加了四次module,内容如下:

mp_help_add_from_map(list, &mp_builtin_module_map);

    #if MICROPY_MODULE_WEAK_LINKS
    mp_help_add_from_map(list, &mp_builtin_module_weak_links_map);
    #endif

    #if MICROPY_MODULE_FROZEN_STR
    extern const char mp_frozen_str_names[];
    mp_help_add_from_names(list, mp_frozen_str_names);
    #endif

    #if MICROPY_MODULE_FROZEN_MPY
    extern const char mp_frozen_mpy_names[];
    mp_help_add_from_names(list, mp_frozen_mpy_names);
    #endif

  这些编译进内核的模块(module),有C语言编写的 ,也有Python语言编写的。其中mp_builtin_module_map和mp_builtin_module_weak_links_map都是用C语言编写的模块。

2.1、mp_builtin_module_weak_links_map

  mp_builtin_module_weak_links_map的定义如下

#if MICROPY_MODULE_WEAK_LINKS
STATIC const mp_rom_map_elem_t mp_builtin_module_weak_links_table[] = {
    MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS
};

MP_DEFINE_CONST_MAP(mp_builtin_module_weak_links_map, mp_builtin_module_weak_links_table);
#endif

  mp_builtin_module_weak_links_map最终指向的是宏定义MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS。

2.2、mp_builtin_module_map

  mp_builtin_module_map的定义如下:

STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = {
    { MP_ROM_QSTR(MP_QSTR___main__), MP_ROM_PTR(&mp_module___main__) },
    { MP_ROM_QSTR(MP_QSTR_builtins), MP_ROM_PTR(&mp_module_builtins) },
    { MP_ROM_QSTR(MP_QSTR_micropython), MP_ROM_PTR(&mp_module_micropython) },

#if MICROPY_PY_ARRAY
    { MP_ROM_QSTR(MP_QSTR_array), MP_ROM_PTR(&mp_module_array) },
#endif
#if MICROPY_PY_IO
    { MP_ROM_QSTR(MP_QSTR_uio), MP_ROM_PTR(&mp_module_io) },
#endif
#if MICROPY_PY_COLLECTIONS
    { MP_ROM_QSTR(MP_QSTR_ucollections), MP_ROM_PTR(&mp_module_collections) },
#endif
#if MICROPY_PY_STRUCT
    { MP_ROM_QSTR(MP_QSTR_ustruct), MP_ROM_PTR(&mp_module_ustruct) },
#endif

#if MICROPY_PY_BUILTINS_FLOAT
#if MICROPY_PY_MATH
    { MP_ROM_QSTR(MP_QSTR_math), MP_ROM_PTR(&mp_module_math) },
#endif
#if MICROPY_PY_BUILTINS_COMPLEX && MICROPY_PY_CMATH
    { MP_ROM_QSTR(MP_QSTR_cmath), MP_ROM_PTR(&mp_module_cmath) },
#endif
#endif
#if MICROPY_PY_SYS
    { MP_ROM_QSTR(MP_QSTR_sys), MP_ROM_PTR(&mp_module_sys) },
#endif
#if MICROPY_PY_GC && MICROPY_ENABLE_GC
    { MP_ROM_QSTR(MP_QSTR_gc), MP_ROM_PTR(&mp_module_gc) },
#endif
#if MICROPY_PY_THREAD
    { MP_ROM_QSTR(MP_QSTR__thread), MP_ROM_PTR(&mp_module_thread) },
#endif

    // extmod modules

#if MICROPY_PY_UERRNO
    { MP_ROM_QSTR(MP_QSTR_uerrno), MP_ROM_PTR(&mp_module_uerrno) },
#endif
#if MICROPY_PY_UCTYPES
    { MP_ROM_QSTR(MP_QSTR_uctypes), MP_ROM_PTR(&mp_module_uctypes) },
#endif
#if MICROPY_PY_UZLIB
    { MP_ROM_QSTR(MP_QSTR_uzlib), MP_ROM_PTR(&mp_module_uzlib) },
#endif
#if MICROPY_PY_UJSON
    { MP_ROM_QSTR(MP_QSTR_ujson), MP_ROM_PTR(&mp_module_ujson) },
#endif
#if MICROPY_PY_URE
    { MP_ROM_QSTR(MP_QSTR_ure), MP_ROM_PTR(&mp_module_ure) },
#endif
#if MICROPY_PY_UHEAPQ
    { MP_ROM_QSTR(MP_QSTR_uheapq), MP_ROM_PTR(&mp_module_uheapq) },
#endif
#if MICROPY_PY_UTIMEQ
    { MP_ROM_QSTR(MP_QSTR_utimeq), MP_ROM_PTR(&mp_module_utimeq) },
#endif
#if MICROPY_PY_UHASHLIB
    { MP_ROM_QSTR(MP_QSTR_uhashlib), MP_ROM_PTR(&mp_module_uhashlib) },
#endif
#if MICROPY_PY_UCRYPTOLIB
    { MP_ROM_QSTR(MP_QSTR_ucryptolib), MP_ROM_PTR(&mp_module_ucryptolib) },
#endif
#if MICROPY_PY_UBINASCII
    { MP_ROM_QSTR(MP_QSTR_ubinascii), MP_ROM_PTR(&mp_module_ubinascii) },
#endif
#if MICROPY_PY_URANDOM
    { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&mp_module_urandom) },
#endif
#if MICROPY_PY_USELECT
    { MP_ROM_QSTR(MP_QSTR_uselect), MP_ROM_PTR(&mp_module_uselect) },
#endif
#if MICROPY_PY_USSL
    { MP_ROM_QSTR(MP_QSTR_ussl), MP_ROM_PTR(&mp_module_ussl) },
#endif
#if MICROPY_PY_LWIP
    { MP_ROM_QSTR(MP_QSTR_lwip), MP_ROM_PTR(&mp_module_lwip) },
#endif
#if MICROPY_PY_WEBSOCKET
    { MP_ROM_QSTR(MP_QSTR_websocket), MP_ROM_PTR(&mp_module_websocket) },
#endif
#if MICROPY_PY_WEBREPL
    { MP_ROM_QSTR(MP_QSTR__webrepl), MP_ROM_PTR(&mp_module_webrepl) },
#endif
#if MICROPY_PY_FRAMEBUF
    { MP_ROM_QSTR(MP_QSTR_framebuf), MP_ROM_PTR(&mp_module_framebuf) },
#endif
#if MICROPY_PY_BTREE
    { MP_ROM_QSTR(MP_QSTR_btree), MP_ROM_PTR(&mp_module_btree) },
#endif

    // extra builtin modules as defined by a port
    MICROPY_PORT_BUILTIN_MODULES
};

MP_DEFINE_CONST_MAP(mp_builtin_module_map, mp_builtin_module_table);

  mp_builtin_module_map指向的的是一个名字为mp_builtin_module_table的结构体数组,该数组内存储量大量的模块定义,其中宏定义MICROPY_PORT_BUILTIN_MODULES是跟端口密切相关的,及跟平台单片机密切相关的。

三、与硬件平台相关的模块

  与硬件平台密切相关的固件都存储在宏定义MICROPY_PORT_BUILTIN_MODULES和宏定义MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS中。这两个宏定义的定义在mpconfigport.h文件内,以ESP32为例:

#define MICROPY_PORT_BUILTIN_MODULES \
    { MP_OBJ_NEW_QSTR(MP_QSTR_esp), (mp_obj_t)&esp_module }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_esp32), (mp_obj_t)&esp32_module }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_utime), (mp_obj_t)&utime_module }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_uos), (mp_obj_t)&uos_module }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_usocket), (mp_obj_t)&mp_module_usocket }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_network), (mp_obj_t)&mp_module_network }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR__onewire), (mp_obj_t)&mp_module_onewire }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_uhashlib), (mp_obj_t)&mp_module_uhashlib }, \

#define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \
    { MP_OBJ_NEW_QSTR(MP_QSTR_binascii), (mp_obj_t)&mp_module_ubinascii }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_collections), (mp_obj_t)&mp_module_collections }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_errno), (mp_obj_t)&mp_module_uerrno }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_hashlib), (mp_obj_t)&mp_module_uhashlib }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_heapq), (mp_obj_t)&mp_module_uheapq }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_io), (mp_obj_t)&mp_module_io }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_json), (mp_obj_t)&mp_module_ujson }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_os), (mp_obj_t)&uos_module }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_random), (mp_obj_t)&mp_module_urandom }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_re), (mp_obj_t)&mp_module_ure }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_select), (mp_obj_t)&mp_module_uselect }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&mp_module_usocket }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_ssl), (mp_obj_t)&mp_module_ussl }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_struct), (mp_obj_t)&mp_module_ustruct }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&utime_module }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_zlib), (mp_obj_t)&mp_module_uzlib }, \

  我们编写的模块可以添加到MICROPY_PORT_BUILTIN_MODULES宏定义的后面,这样在系统加载模块式,我们的模块就可以被加载进去了。