阅读micropyton源码-添加C扩展类模块(1)
苏勇,2021年8月
Introduction
在前文《为micropython添加模块(2)-类模块》中,我按照源代码排列的顺序,大体阅读了micropython添加C扩展模块的编程方式及其源代码。在通读micropython源码之后,我认为使用自顶向下的方式阅读和描述这部分更有助于开发者理解和编程。经年之前,我对micropython中用引脚创建引脚的方式百思不得其解,但对这种用法觉得比较有趣,此次再阅读代码,颇有一些收获,在此也一并做些记录,同时也可作为开发指南参考。
本文以mimxrt的port为参考模板,添加一个Pin模块,并撰写在mm32f3270上的Pin模块。
注意: 本文同前文讲述内容和对象相同,只是换了一个思路,加了一些心得而已,也考虑了micropython版本迭代的一些更新。
在python内核中注册machine模块包含的Pin模块
本节解释Pin作为machine的子模块,建立在代码中用“machine.Pin”的引用关系。
从ports/mimxrt/machine_pin.c文件开始说起。这个文件是为了实现Pin模块。 Pin模块底层的细节等下再看,但我们知道了整个源文件就是为了创建这一个machine_pin_type对象
const mp_obj_type_t machine_pin_type = {
{&mp_type_type},
.name = MP_QSTR_Pin,
.print = machine_pin_obj_print,
.call = machine_pin_obj_call,
.make_new = mp_pin_make_new,
.locals_dict = (mp_obj_dict_t *)&machine_pin_locals_dict,
};
machine_pin_type这个实例被包含在machine模块中,见同目录下的modmachine.c文件中:
STATIC const mp_rom_map_elem_t machine_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) },
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) },
{ MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_freq_obj) },
{ MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) },
{ MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) },
{ MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) },
#if NUM_LEDS
{ MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&machine_led_type) },
#endif
{ MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) },
{ MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) },
{ MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) },
{ MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) },
{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) },
};
STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table);
const mp_obj_module_t mp_module_machine = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&machine_module_globals,
};
machine_pin_type <- MP_QSTR_Pin <- machine_module_globals_table <- machine_module_globals <- mp_module_machine
这个结构体中定义了mp_module_machine 对象内部的属性清单,并建立一个类似于python中map的表,这样在micropython内核中,就可以通过QSTR字符串“Pin”,找到指向machine_pin_type结构体实体的指针,我们或可认为通过python内部的某种机制就可以通过“.”这个字符,映射到其对应的内存块。
而mp_module_machine这个对象,是在mpconfigport.h中被注册到micropython中。
extern const struct _mp_obj_module_t mp_module_machine;
extern const struct _mp_obj_module_t mp_module_mimxrt;
extern const struct _mp_obj_module_t mp_module_onewire;
extern const struct _mp_obj_module_t mp_module_uos;
extern const struct _mp_obj_module_t mp_module_utime;
#define MICROPY_PORT_BUILTIN_MODULES \
{ MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&mp_module_machine) }, \
{ MP_ROM_QSTR(MP_QSTR_mimxrt), (mp_obj_t)&mp_module_mimxrt }, \
{ MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \
{ MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, \
{ MP_ROM_QSTR(MP_QSTR__onewire), MP_ROM_PTR(&mp_module_onewire) }, \
mp_module_machine <- MP_QSTR_machine <- MICROPY_PORT_BUILTIN_MODULES
我们再走深入一点,MICROPY_PORT_BUILTIN_MODULES在py/objmodule.c文件中被引用,同其它的系统组件(例如:math、gc等)一起,作为mp_builtin_module_table的表项:
TATIC 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_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
...
#if MICROPY_PY_GC && MICROPY_ENABLE_GC
{ MP_ROM_QSTR(MP_QSTR_gc), MP_ROM_PTR(&mp_module_gc) },
#endif
// extmod modules
#if MICROPY_PY_UASYNCIO
{ MP_ROM_QSTR(MP_QSTR__uasyncio), MP_ROM_PTR(&mp_module_uasyncio) },
#endif
#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
...
// extra builtin modules as defined by a port
MICROPY_PORT_BUILTIN_MODULES
#ifdef MICROPY_REGISTERED_MODULES
// builtin modules declared with MP_REGISTER_MODULE()
MICROPY_REGISTERED_MODULES
#endif
};
MP_DEFINE_CONST_MAP(mp_builtin_module_map, mp_builtin_module_table);
MICROPY_PORT_BUILTIN_MODULES <- mp_builtin_module_table <- mp_builtin_module_map
进一步就不再看了,大体是通过**mp_obj_t mp_module_get(qstr module_name)**函数,可以通过qstr字符串类型的模块名,在mp_builtin_module_map表中找到对应的模块实体。
小结
简单小结一下Pin对象实体在被定义之后,如何层层被包含在python内核中的。
-
在ports/mimxrt/machine_pin.c文件中,定义了machine_pin_type结构体实体。
-
在ports/mimxrt/modmachine.c文件中,建立从machine_pin_type到mp_module_machine的注册关联:
machine_pin_type <- MP_QSTR_Pin <- machine_module_globals_table <- machine_module_globals <- mp_module_machine
- 在ports/mimxrt/mpconfigport.h文件中,建立从mp_module_machine到MICROPY_PORT_BUILTIN_MODULES的注册关联:
mp_module_machine <- MP_QSTR_machine <- MICROPY_PORT_BUILTIN_MODULES
- 在py/objmodule.c文件中,建立从MICROPY_PORT_BUILTIN_MODULES到mp_builtin_module_map的注册关联,至此组装映射表完毕。
MICROPY_PORT_BUILTIN_MODULES <- mp_builtin_module_table <- mp_builtin_module_map
- 在py/objmodule.c文件中,mp_builtin_module_map被函数mp_module_get()引用,在映射表中通过模块名字查询对应实体。
这里画个图更直观,但这个图有可能很大。暂时就先这样吧。
END
只是一点点推理就已经占用了这么多篇幅,看来要分多篇文章了。