在mm32f3270为micropython创建Pin模块(2)
苏勇,2021年8月
Introduction
上文书说,已经搭建了一个基本的能够在REPL中引用到的Pin模块。为了规避Hardfault的问题,暂时屏蔽了history的功能。勉强已经可以用起来了。
本文将要打通python对底层硬件的操作,进一步完成Pin模块。
本文可能会涉及到引脚表的设计问题
make_new()
static inline void mp_arg_check_num (
size_t n_args, /* 位置参数的总数量 */
size_t n_kw, /* 关键字参数的数量 */
size_t n_args_min, /* 位置参数最少数量 */
size_t n_args_max, /* 位置参数最大数量 */
bool takes_kw ) /* 是否包含关键字参数 */
{
mp_arg_check_num_sig(n_args, n_kw, MP_OBJ_FUN_MAKE_SIG(n_args_min, n_args_max, takes_kw));
}
感觉在machine_pin_obj_make_new()里检查这个参数清单没啥功能性作用。如果参数无效,则直接报错?
后来在试用程序的过程发现挺有用的,当用户输入参数不正确时,会提示错误类型。
接下来用第一个make_new()的第一个传参找引脚。这个args[0]是个什么鬼?当传入pin_find时,是当它为一个对象的,可能是一个预先初始化好的引脚对象,也可能是一个数字
const machine_pin_obj_t *pin = pin_find(args[0]);
在machine_pin.c中实现pin_func()函数如下:
/* 格式化pin对象,传入参数无论是已经初始化好的pin对象,还是一个表示pin清单中的索引编号,通过本函数都返回一个期望的pin对象。 */
const machine_pin_obj_t *pin_find(mp_obj_t user_obj)
{
//const machine_pin_obj_t *pin_obj;
/* 如果传入参数本身就是一个Pin的实例,则直接送出这个pin。 */
if ( mp_obj_is_type(user_obj, &machine_pin_type) )
{
return user_obj;
}
/* 如果传入参数是一个代表Pin清单的索引,则通过索引在Pin清单中找到这个pin,然后送出这个pin。 */
if ( mp_obj_is_small_int(user_obj) )
{
uint8_t pin_idx = MP_OBJ_SMALL_INT_VALUE(user_obj);
if ( pin_idx < machine_pin_board_pins_num)
{
return machine_pin_board_pins[pin_idx];
}
}
/* 如果传入参数是一个字符串,则通过这个字符串在Pin清单中匹配引脚名字,然后送出找到的pin */
const machine_pin_obj_t *named_pin_obj = pin_find_by_name(&machine_pin_board_pins_locals_dict, user_obj);
if ( named_pin_obj )
{
return named_pin_obj;
}
mp_raise_ValueError(MP_ERROR_TEXT("Pin doesn't exist"));
}
/* 通过字符串在引脚清单中匹配引脚 */
const machine_pin_obj_t *pin_find_by_name(const mp_obj_dict_t *name_dict, mp_obj_t name)
{
mp_map_t *name_map = mp_obj_dict_get_map((mp_obj_t)name_dict);
mp_map_elem_t *name_elem = mp_map_lookup(name_map, name, MP_MAP_LOOKUP);
if ( (name_elem != NULL) && (name_elem->value != NULL) )
{
return name_elem->value;
}
return NULL;
}
其中,machine_pin_board_pins[]和machine_pin_board_pins_locals_dict[]在“ports/mm32/board/MB_F3270/machine_pin_board_pins.c”文件中定义。
const uint32_t machine_pin_board_pins_num = 3;
const machine_pin_obj_t pin_PA1 = { .base = { &machine_pin_type }, .name = MP_QSTR_PA1, .gpio_port = GPIOA, .gpio_pin = 1 };
const machine_pin_obj_t pin_PA2 = { .base = { &machine_pin_type }, .name = MP_QSTR_PA2, .gpio_port = GPIOA, .gpio_pin = 2 };
const machine_pin_obj_t pin_PA3 = { .base = { &machine_pin_type }, .name = MP_QSTR_PA3, .gpio_port = GPIOA, .gpio_pin = 3 };
const machine_pin_obj_t *machine_pin_board_pins[] =
{
&pin_PA1,
&pin_PA2,
&pin_PA3,
};
STATIC const mp_rom_map_elem_t pin_board_pins_locals_dict_table[] =
{
{ MP_ROM_QSTR(MP_QSTR_PA1), MP_ROM_PTR(&pin_PA1) },
{ MP_ROM_QSTR(MP_QSTR_PA2), MP_ROM_PTR(&pin_PA2) },
{ MP_ROM_QSTR(MP_QSTR_PA3), MP_ROM_PTR(&pin_PA3) },
};
MP_DEFINE_CONST_DICT(machine_pin_board_pins_locals_dict, pin_board_pins_locals_dict_table);
是的,我还是创建了“machine_pin.h”。
引入“machine_pin.h”是为了解决在通用machine_pin和板级machine_pin之间共享machine_pin_obj_t的问题,顺便把hal_gpio.h中配置PinMode选项的枚举类型用mp的方式映射出来。
typedef enum
{
PIN_MODE_IN_ANALOG = 0u,
PIN_MODE_IN_FLOATING,
PIN_MODE_IN_PULLDOWN,
PIN_MODE_IN_PULLUP,
PIN_MODE_OUT_OPENDRAIN,
PIN_MODE_OUT_PUSHPULL,
PIN_MODE_AF_OPENDRAIN,
PIN_MODE_AF_PUSHPULL,
} machine_pin_mode_t;
在machine_pin.c还有:
static const GPIO_PinMode_Type machine_pin_modes[] =
{
GPIO_PinMode_In_Analog ,
GPIO_PinMode_In_Floating ,
GPIO_PinMode_In_PullDown ,
GPIO_PinMode_In_PullUp ,
GPIO_PinMode_Out_OpenDrain,
GPIO_PinMode_Out_PushPull ,
GPIO_PinMode_AF_OpenDrain ,
GPIO_PinMode_AF_PushPull ,
};
这样就可以通过mp中的顺序索引访问GPIO配置驱动中的不连续的设定值了。
machine_pin_modes[PIN_MODE_IN_PULLUP] = GPIO_PinMode_In_PullUp
同时,这些PinMode配置值还要变成关键字,才能被用户从脚本中调用到。
/* class locals_dict_table. */
STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] =
{
/* Class instance methods. */
{ MP_ROM_QSTR(MP_QSTR_low), MP_ROM_PTR(&machine_pin_low_obj) },
{ MP_ROM_QSTR(MP_QSTR_high), MP_ROM_PTR(&machine_pin_high_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) },
/* Class constants. */
{ MP_ROM_QSTR(MP_QSTR_IN_ANALOG ), MP_ROM_INT(PIN_MODE_IN_ANALOG ) },
{ MP_ROM_QSTR(MP_QSTR_IN_FLOATING ), MP_ROM_INT(PIN_MODE_IN_FLOATING ) },
{ MP_ROM_QSTR(MP_QSTR_IN_PULLDOWN ), MP_ROM_INT(PIN_MODE_IN_PULLDOWN ) },
{ MP_ROM_QSTR(MP_QSTR_IN_PULLUP ), MP_ROM_INT(PIN_MODE_IN_PULLUP ) },
{ MP_ROM_QSTR(MP_QSTR_OUT_OPENDRAIN), MP_ROM_INT(PIN_MODE_OUT_OPENDRAIN) },
{ MP_ROM_QSTR(MP_QSTR_OUT_PUSHPULL ), MP_ROM_INT(PIN_MODE_OUT_PUSHPULL ) },
{ MP_ROM_QSTR(MP_QSTR_AF_OPENDRAIN ), MP_ROM_INT(PIN_MODE_AF_OPENDRAIN ) },
{ MP_ROM_QSTR(MP_QSTR_AF_PUSHPULL ), MP_ROM_INT(PIN_MODE_AF_PUSHPULL ) },
};
STATIC MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table);
准备了这么多,最后才能开始写make_new()函数:
/* return an instance of machine_pin_obj_t. */
mp_obj_t machine_pin_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args)
{
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
const machine_pin_obj_t *pin = pin_find(args[0]);
if ( (n_args > 1) || (n_kw > 0) )
{
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); /* 将关键字参数从总的参数列表中提取出来,单独封装成kw_args。 */
machine_pin_obj_init_helper(pin, n_args - 1, args + 1, &kw_args);
}
return (mp_obj_t)pin;
}
其中machine_pin_obj_init_helper()的实现如下:
typedef enum
{
PIN_INIT_ARG_MODE = 0,
PIN_INIT_ARG_VALUE,
} machine_pin_init_arg_t;
STATIC mp_obj_t machine_pin_obj_init_helper (
const machine_pin_obj_t *self, /* machine_pin_obj_t类型的变量,包含硬件信息 */
size_t n_args, /* 位置参数数量 */
const mp_obj_t *pos_args, /* 位置参数清单 */
mp_map_t *kw_args ) /* 关键字参数清单结构体 */
{
static const mp_arg_t allowed_args[] =
{
[PIN_INIT_ARG_MODE] { MP_QSTR_mode , MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = PIN_MODE_IN_PULLUP} },
[PIN_INIT_ARG_VALUE]{ MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
//{ MP_QSTR_drive, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PIN_DRIVE_POWER_3}},
};
/* 解析参数 */
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
static const GPIO_PinMode_Type machine_pin_modes[] =
{
GPIO_PinMode_In_Analog ,
GPIO_PinMode_In_Floating ,
GPIO_PinMode_In_PullDown ,
GPIO_PinMode_In_PullUp ,
GPIO_PinMode_Out_OpenDrain,
GPIO_PinMode_Out_PushPull ,
GPIO_PinMode_AF_OpenDrain ,
GPIO_PinMode_AF_PushPull ,
};
GPIO_Init_Type gpio_init;
gpio_init.Speed = GPIO_Speed_50MHz;
gpio_init.Pins = (1u << self->gpio_pin);
gpio_init.PinMode = machine_pin_modes[args[PIN_INIT_ARG_MODE].u_int];
GPIO_Init(self->gpio_port, &gpio_init);
if (args[PIN_INIT_ARG_VALUE].u_obj != MP_OBJ_NULL)
{
if ( mp_ojb_is_true(args[PIN_INIT_ARG_VALUE].u_obj) )
{
GPIO_WriteBit(self->gpio_port, 1u << self->gpio_pin, 1u);
}
else
{
GPIO_WriteBit(self->gpio_port, 1u << self->gpio_pin, 0u);
}
}
return mp_const_none;
}
pin_init()天然复用了init_helper()函数好,不用额外代码了。
pin_low()和pin_high()
STATIC mp_obj_t machine_pin_high(mp_obj_t self_in)
{
/* self_in is machine_pin_obj_t. */
machine_pin_obj_t * pin = (machine_pin_obj_t *)self_in;
GPIO_WriteBit(pin->gpio_base, 1u << pin->gpio_pin, 1u);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_high_obj, machine_pin_high);
STATIC mp_obj_t machine_pin_low(mp_obj_t self_in)
{
/* self_in is machine_pin_obj_t. */
machine_pin_obj_t * pin = (machine_pin_obj_t *)self_in;
GPIO_WriteBit(pin->gpio_base, 1u << pin->gpio_pin, 0u);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_low_obj, machine_pin_low);
到此为止,就可以试着编一下,然后下载程序试着运行一下了。
MicroPython v1.16 on 2021-08-23; MB_F3270 with MM32F3277G7P
>>> import machine
>>> dir(machine)
['__name__', 'Pin', 'freq', 'mem16', 'mem32', 'mem8']
>>> from machine import Pin
>>> dir(Pin)
['value', 'AF_OPENDRAIN', 'AF_PUSHPULL', 'IN_ANALOG', 'IN_FLOATING', 'IN_PULLDOWN', 'IN_PULLUP', 'OUT_OPENDRAIN', 'OUT_PUSHPULL', 'high', 'init', 'low']
>>> pin0 = Pin(0)
>>> pin0.init(mode=OUT_PUSHPULL)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name not defined
>>> Pin.OUT_PUSHPULL
5
>>> pin0.init(mode=Pin.OUT_PUSHPULL)
>>> pin0.low()
>>> pin0.high()
>>>
通过试用可以看到,即使定义了“OUT_PUSHPULL”,也不能直接用,必须使用“Pin.OUT_PUSHPULL”。
想来如果使用了“from Pin import *”,就有可能直接用“OUT_PUSHPULL”
>>> from Pin import *
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: module not found
>>> from machine.Pin import *
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: module not found
>>>
实测是不能这么用的。
先到这里。等下再补完value()函数。
END