micropyton源码-添加C扩展类模块(2)

看看machine_pin_type实例的定义
回到ports/mimxrt/machine_pin.c文件,同machine_pin_type并列定义的还有“machine_pin_af_type”,在“pin.h”和“ports/mimxrt/boards/mimxrt_prefix.c”文件中有所涉及,关于指定引脚功能复用的,似乎也作为一个实例对象定义的,但未被注册到任何模块中,暂且放过,待看完machine_pin_type后再回来看。

至于“machine_pin_irq_methods”,仍是在定义machine_pin_type内所属的方法中被调用,等下会看到。

交待了旁枝末节,终于可以静下心来关注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.c就是为了填充这一个mp_obj_type_t类型的结构体实例,此处用到的是“硬回调”的编码模式。 关于“mp_obj_type_t”的定义,具体可参见“py/obj.h”文件,此处仅对必要的字段进行解释。

“&mp_type_type”指定本模块的基类是一个类型对象,直接继承了一些类型(type)对象的方法。“mp_type_type”在dynruntime.h文件中定义,同mp_type_type并列定义的还有mp_type_str、mp_type_tuple、mp_type_list等。

“.name”中登记的是本模块的名字,在python的实现中,都是通过名字字符串进行索引的,关键字字符串都是用qstr类型,qstr类型的字符串直接用“MP_QSTR_”前缀,并且不用双引号引起来。这在编译过程中,会通过编译主机python脚本自动提取这些qstr的实际字符串,这个操作类似于c编译器工具链中的预处理环节。

“.print”指定的函数,是在用户在python中调用print(Pin)函数时,将Pin的实例化对象打印成字符串。

machine_pin对print函数的实现如下,打印“machine_pin_obj_t”结构体类型中“name”字段的字符串。
 

STATIC void machine_pin_obj_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind) {
    (void)kind;
    const machine_pin_obj_t *self = MP_OBJ_TO_PTR(o);
    mp_printf(print, "Pin(%s)", qstr_str(self->name));
}

“machine_pin_obj_t”结构体类型是在“pin.h”文件中定义的,但我感觉也可以直接放在“machine_pin.h”文件中。在mimxrt的实现中,machine_pin_type和machine_pin_af_type都归在了“pin.h”中。至于这个“name”字段是么时候填进去的呢,我猜是new的时候填入的,让我们拭目以待。“mp_print_t”是打印的对象,可以是交互终端,也可以是log文件。

“.call”指定的函数,对应的是Python中一个非常特殊的类的实例方法,即 __call__()。该方法的功能类似于在类中重载 () 运算符,使得类实例对象可以像调用普通函数那样,以“对象名()”的形式使用。在直白一点,就是将实例对象的名字本身也当成作为一个函数名。machine_pin对call函数的实现如下:
 

STATIC mp_obj_t machine_pin_obj_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
    mp_arg_check_num(n_args, n_kw, 0, 1, false);
    machine_pin_obj_t *self = self_in;

    if (n_args == 0) {
        return MP_OBJ_NEW_SMALL_INT(mp_hal_pin_read(self));
    } else {
        mp_hal_pin_write(self, mp_obj_is_true(args[0]));
        return mp_const_none;
    }
}

结合此处的实现举例call()属性函数的用法:

pin1 = Pin() # 实例化一个Pin的对象pin1
print(pin1()) # 若参数数量为0个,则返回mp_hal_pin_read()函数读到的引脚值
pin1(0) #若参数为1个,则将传入的第一个参数args[0]作为mp_hal_pin_write()函数的参数,写入引脚指定电平值

参见:Python call()方法(详解版)

“.make_new”指定的函数,是实例化一个类对象的操作过程。这个new函数会“无中生有”,在gc管理的内存区中动态申请一块内存,填入必要的属性信息,然后将这块内存的句柄(指针)返回给调用者。在下文中将详细介绍对应“mp_pin_make_new()”函数的实现,这个函数实现的传参过程比较有趣,可以以不同的编号方式指定对象,下文会有专门的篇幅进行详细介绍。
补充一句,如果按照看起来统一的命名规范,这里的“mp_pin_make_new()”函数名字应同其它属性函数一样使用“machine_pin_obj”前缀,称为“machine_pin_obj_make_new”

“.locals_dict”是本类包含的局部关键字列表以及它们对应的功能(常量?函数?)。在python中有一个内建函数locals(),locals() 函数会以字典类型返回当前位置的全部局部变量。猜测这里的“locals_dict”是提供一个可返回的字典,同时用作模块内部可通过“.”算符继续访问的属性和方法资源。
参见:Python locals() 函数

machine_pin对locals_dict的实现如下:

STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
    // instance methods
    { MP_ROM_QSTR(MP_QSTR_off),     MP_ROM_PTR(&machine_pin_off_obj) },
    { MP_ROM_QSTR(MP_QSTR_on),      MP_ROM_PTR(&machine_pin_on_obj) },
    { MP_ROM_QSTR(MP_QSTR_low),     MP_ROM_PTR(&machine_pin_off_obj) },
    { MP_ROM_QSTR(MP_QSTR_high),    MP_ROM_PTR(&machine_pin_on_obj) },
    { MP_ROM_QSTR(MP_QSTR_value),   MP_ROM_PTR(&machine_pin_value_obj) },
    { MP_ROM_QSTR(MP_QSTR_init),    MP_ROM_PTR(&machine_pin_init_obj) },
    { MP_ROM_QSTR(MP_QSTR_irq),     MP_ROM_PTR(&machine_pin_irq_obj) },
    // class attributes
    { MP_ROM_QSTR(MP_QSTR_board),   MP_ROM_PTR(&machine_pin_board_pins_obj_type) },
    { MP_ROM_QSTR(MP_QSTR_cpu),     MP_ROM_PTR(&machine_pin_cpu_pins_obj_type) },
    // class constants
    { MP_ROM_QSTR(MP_QSTR_IN),      MP_ROM_INT(PIN_MODE_IN) },
    { MP_ROM_QSTR(MP_QSTR_OUT),     MP_ROM_INT(PIN_MODE_OUT) },
    { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(PIN_MODE_OPEN_DRAIN) },

    { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(PIN_PULL_UP_100K) },
    { MP_ROM_QSTR(MP_QSTR_PULL_UP_47K), MP_ROM_INT(PIN_PULL_UP_47K) },
    { MP_ROM_QSTR(MP_QSTR_PULL_UP_22K), MP_ROM_INT(PIN_PULL_UP_22K) },
    { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(PIN_PULL_DOWN_100K) },
    { MP_ROM_QSTR(MP_QSTR_PULL_HOLD), MP_ROM_INT(PIN_PULL_HOLD) },

    { MP_ROM_QSTR(MP_QSTR_DRIVER_OFF), MP_ROM_INT(PIN_DRIVE_OFF) },
    { MP_ROM_QSTR(MP_QSTR_POWER_0),    MP_ROM_INT(PIN_DRIVE_POWER_0) }, // R0 (150 Ohm @3.3V / 260 Ohm @ 1.8V)
    { MP_ROM_QSTR(MP_QSTR_POWER_1),    MP_ROM_INT(PIN_DRIVE_POWER_1) }, // R0/2
    { MP_ROM_QSTR(MP_QSTR_POWER_2),    MP_ROM_INT(PIN_DRIVE_POWER_2) }, // R0/3
    { MP_ROM_QSTR(MP_QSTR_POWER_3),    MP_ROM_INT(PIN_DRIVE_POWER_3) }, // R0/4
    { MP_ROM_QSTR(MP_QSTR_POWER_4),    MP_ROM_INT(PIN_DRIVE_POWER_4) }, // R0/5
    { MP_ROM_QSTR(MP_QSTR_POWER_5),    MP_ROM_INT(PIN_DRIVE_POWER_5) }, // R0/6
    { MP_ROM_QSTR(MP_QSTR_POWER_6),    MP_ROM_INT(PIN_DRIVE_POWER_6) }, // R0/7

    { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(1) },
    { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(2) },

“mp_rom_map_elem_t”顾名思义是存放在rom中的map元素类型,内部包含若干的“键值对”,左边为qstr类型的关键字字符串,右边对应资源。例如:“MP_QSTR_off”对应的是“off”关键字和函数“machine_pin_off_obj”,“MP_ROM_PTR()”宏特别指定了这是一个“PTR”指针;“MP_QSTR_IN”对应的是“IN”关键字和“PIN_MODE_IN”常量,“MP_ROM_INT()”宏特别指定了这是一个“INT”整数,“PIN_MODE_INT”的值也是在“pin.h”文件中定义的。

特别说明
在machine_pin.c文件中使用了一些宏操作,将一个函数或者一个数组封装成对象。切记,在python中,一切皆对象,函数、数组、变量甚至常量。

小如指针、常量:
 

MP_ROM_PTR(&machine_pin_off_obj)
MP_ROM_INT(1)

大如函数、数组:

MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_off_obj, machine_pin_off);
MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_init);
MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table);

它们的实现代码在“py/obj.h”文件中,实际上是定义了一个引用了当前资源的新的变量:

#define MP_DEFINE_CONST_FUN_OBJ_0(obj_name, fun_name) \
    const mp_obj_fun_builtin_fixed_t obj_name = \
    {{&mp_type_fun_builtin_0}, .fun._0 = fun_name}
#define MP_DEFINE_CONST_FUN_OBJ_1(obj_name, fun_name) \
    const mp_obj_fun_builtin_fixed_t obj_name = \
    {{&mp_type_fun_builtin_1}, .fun._1 = fun_name}
#define MP_DEFINE_CONST_FUN_OBJ_2(obj_name, fun_name) \
    const mp_obj_fun_builtin_fixed_t obj_name = \
    {{&mp_type_fun_builtin_2}, .fun._2 = fun_name}
#define MP_DEFINE_CONST_FUN_OBJ_3(obj_name, fun_name) \
    const mp_obj_fun_builtin_fixed_t obj_name = \
    {{&mp_type_fun_builtin_3}, .fun._3 = fun_name}
...

#define MP_DEFINE_CONST_FUN_OBJ_KW(obj_name, n_args_min, fun_name) \
    const mp_obj_fun_builtin_var_t obj_name = \
    {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, true), .fun.kw = fun_name}
...

#define MP_DEFINE_CONST_DICT(dict_name, table_name) \
    const mp_obj_dict_t dict_name = { \
        .base = {&mp_type_dict}, \
        .map = { \
            .all_keys_are_qstrs = 1, \
            .is_fixed = 1, \
            .is_ordered = 1, \
            .used = MP_ARRAY_SIZE(table_name), \
            .alloc = MP_ARRAY_SIZE(table_name), \
            .table = (mp_map_elem_t *)(mp_rom_map_elem_t *)table_name, \
        }, \
    }

至于其中“mp_type_fun_builtin_1”的定义,位于“py/objfun.c”文件中,是一个常量结构体,而不是一个类型:

STATIC mp_obj_t fun_builtin_1_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
    assert(mp_obj_is_type(self_in, &mp_type_fun_builtin_1));
    mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in);
    mp_arg_check_num(n_args, n_kw, 1, 1, false);
    return self->fun._1(args[0]);
}

const mp_obj_type_t mp_type_fun_builtin_1 = {
    { &mp_type_type },
    .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN,
    .name = MP_QSTR_function,
    .call = fun_builtin_1_call,
    .unary_op = mp_generic_unary_op,
};

mp_type_fun_builtin_1”的“.call”指向“fun_builtin_1_call()”函数,从这个函数的实现代码可以看出,其中通过“mp_arg_check_num()”函数检查了参数,有效传入参数仅为1个。之后通过“self->fun._1()”函数执行MP_DEFINE_CONST_FUN_OBJ_1(obj_name, fun_name)执行其中的fun_name,而obj_name就是这里的“self”。

类似地,在“fun_builtin_2_call()”函数中,也过滤成有效参数为2个,“self->fun._2()”函数的传入参数为两个,args[0]和args[1]。

至于func._1和func._2,以及后面可能会经常看到的变参数或关键字参数的函数指针var和kw,其实都是函数指针,在结构体里定义成共享一块内存的union。在“py/obj.h”中有:

typedef struct _mp_obj_fun_builtin_fixed_t {
    mp_obj_base_t base;
    union {
        mp_fun_0_t _0;
        mp_fun_1_t _1;
        mp_fun_2_t _2;
        mp_fun_3_t _3;
    } fun;
} mp_obj_fun_builtin_fixed_t;

typedef struct _mp_obj_fun_builtin_var_t {
    mp_obj_base_t base;
    uint32_t sig; // see MP_OBJ_FUN_MAKE_SIG
    union {
        mp_fun_var_t var;
        mp_fun_kw_t kw;
    } fun;
} mp_obj_fun_builtin_var_t;

  • 26
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值