1、背景
本节内容将详细介绍如何扩展一个新的MicroPython 的组件。
组件扩展方式分两种:模块扩展,模块+类扩展。
右边netmgr 功能以模块的方式扩展的,在使用的时候直接导入模块进行使用。左边的ADC 是通过模块+类的方式进行扩展,使用的时候需要通过模块导入ADC类进行使用。
图1 组件扩展方式
2、基础知识
组件扩展过程中会用到函数定义、参数类型转及如何通过Python呼叫C语言代码等功能,下面是对这些功能的说明。
2.1 函数和参数定义方式
MP_DEFINE_CONST_FUN_OBJ_0(obj_name, fun_name) //表示函数无参数
#define MP_DEFINE_CONST_FUN_OBJ_1(obj_name, fun_name) // 表示函数有一个参数
#define MP_DEFINE_CONST_FUN_OBJ_2(obj_name, fun_name) // 表示函数有两个参数
#define MP_DEFINE_CONST_FUN_OBJ_3(obj_name, fun_name) //表示函数有三个参数
#define MP_DEFINE_CONST_FUN_OBJ_VAR(obj_name, n_args_min, fun_name) // n_args_min 最小参数个数
#define MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name, n_args_min, n_args_max, fun_name) // n_args_min, n_args_max 表示函数的参数个数范围
2.2 类型定义和转换
MP_OBJ_NEW_SMALL_INT(small_int) //构造int 类型的obj 对象
MP_OBJ_NEW_QSTR(qst) // 构造qstr data类型的obj 对象
MP_OBJ_NEW_IMMEDIATE_OBJ(val) // 构造immediate类型的obj 对象
MP_ROM_INT(i) //构造Int类型的obj对象
MP_ROM_QSTR(q) //构造qstr data 类型的obj对象
MP_ROM_PTR //构造存储指针的obj对象
MP_OBJ_TO_PTR(o) //将obj 对象转换成obj指针
MP_OBJ_FROM_PTR(p) //将obj指针转换成obj对象
MP_ROM_NONE //构造空的obj对象
MP_ROM_FALSE //构造false值的obj对象
MP_ROM_TRUE //构造true值的obj对象
2.3 Python参数转换成C参数方法
mp_int_t mp_obj_get_int(mp_const_obj_t arg) //将int类型的obj 参数转换成int 类型
mp_float_t mp_obj_get_float(mp_obj_t self_in) //将float类型的obj参数转换成float类型
const char *mp_obj_get_type_str(mp_const_obj_t o_in) //将str 类型的obj参数转换成char类型
3、模块扩展
下面以netmgr功能为例讲解模块扩展的方式具体是怎么实现的。
代码路径:
./components/py_engine/modules/netmgr/modnetmgr.c
首先通过Python 引擎的mp_obj_module_t数据结构定义netmgr 模块。其中base成员是mp_obj_base_t类型的,一般作为Python对象的第一个成员变量。
globals成员是mp_obj_dict_t类型的,里面存储的是功能映射关系。
const mp_obj_module_t netmgr_module = {
.base = {&mp_type_module},
.globals = (mp_obj_dict_t *)&netmgr_module_globals,
};
映射表netmgr_module_globals包含多个函数功能:
STATIC const mp_rom_map_elem_t netmgr_module_globals_table[] = {
{MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_netmgr)},
{MP_OBJ_NEW_QSTR(MP_QSTR_init), MP_ROM_PTR(&netmgr_obj_init)},
{MP_OBJ_NEW_QSTR(MP_QSTR_getInfo), MP_ROM_PTR(&netmgr_obj_get_info)},
{MP_OBJ_NEW_QSTR(MP_QSTR_getType), MP_ROM_PTR(&netmgr_obj_get_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_getStatus), MP_ROM_PTR(&netmgr_obj_get_status)},
{MP_OBJ_NEW_QSTR(MP_QSTR_connect), MP_ROM_PTR(&netmgr_obj_connect_wifi)},
{MP_OBJ_NEW_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&netmgr_obj_disconnect_wifi)},
{MP_OBJ_NEW_QSTR(MP_QSTR_on), MP_ROM_PTR(&netmgr_obj_on)},
{MP_OBJ_NEW_QSTR(MP_QSTR_register_call_back), MP_ROM_PTR(&mp_wifi_register_call_back)},};
其中负责网络连线功能的函数是mp_obj_t connect_wifi,该函数有两个参数,分别为Wi-Fi路由器名称(ssid)和路由器密码(pwd),通过这两个参数将Python代码中设定的路由器名称和密码传到C代码。拿到参数后,通过mp_obj_str_get_str函数将mp_obj转换成char类型。然后呼叫aos_task_new创建一个名为wifi_connect_task的线程完成Wi-Fi连线功能。 下面是mp_obj_t_connect_wifi的C代码的具体实现:
STATIC mp_obj_t connect_wifi(mp_obj_t ssid,mp_obj_t pwd) {
char* _ssid = mp_obj_str_get_str(ssid); // 获取SSID字串
char* _pwd = mp_obj_str_get_str(pwd); // 获取密码字串
netmgr_wifi_connect_params_t *params;
params = (netmgr_wifi_connect_params_t*) malloc(sizeof(netmgr_wifi_connect_params_t));
if(params == NULL) {
LOGE(LOG_TAG, "%s:%d malloc failed\n", __func__, __LINE__);
return mp_obj_new_int(-1);
}
memset(params, 0, sizeof(netmgr_wifi_connect_params_t));
strncpy(params->ssid, _ssid, sizeof(params->ssid)-1);
params->timeout = 18000; // 设置Wi-Fi连线超时时间为1800 ms
strncpy((char* )params->pwd, _pwd, sizeof(params->pwd)-1);
aos_task_new("wifi_connect_task",wifi_connect_handle, params, 4*1024); // 创建新线程进行Wi-Fi连线操作
return mp_obj_new_int(0);
}
MP_DEFINE_CONST_FUN_OBJ_2(netmgr_obj_connect_wifi, connect_wifi);
netmgr组件扩展成Python联网模块之后就可以在Python应用程序中调用其提供的PythonAPI了,具体使用方法如下面代码所示:
import netmgr as nm # 导入netmgr模块,为该模块取别名为nm,后续访问此模块功能均通过nm
import utime as time # 导入utime模块,为该模块取名为time
import sys # 导入sys库
nm.init() # netmgr组件初始化
connected = nm.getStatus() # 获取Wi-Fi连接状态
def on_wifi_connected(status): # 定义Wi-Fi连线成功的回调函数
global connected
print('*******wifi connected*********')
connected = True # 设置connected变量为True
if not connected: # 如果连线没有成功
nm.register_call_back(1,on_wifi_connected) # 向nm组件注册连线成功回调函数
if(len(sys.argv) == 3):
nm.connect(sys.argv[1],sys.argv[2]) # 如果执行此python脚本的时候同时输入了Wi-Fi SSID和密码作为参数则连接用户输入的指定路由器
else:
nm.connect("KIDS","12345678") # 如果执行此python脚本的时候没有输入Wi-Fi SSID和密码,则连接预设的名为KIDS的路由器
while True : # 等待Wi-Fi连接成功
if connected:
break # 连线成功则调出此循环
else:
print('Wait for wifi connected')
time.sleep(1) # 没有连线成功则打印日志并休眠1秒
if nm.getStatus():
print('DeviceIP:' + nm.getInfo()['IP']) # 连线成功则通过呼叫getInfo获取IP地址信息
else:
print('DeviceIP:get failed')
print("ConnectWifi finished")
4、模块+类扩展
下面以ADC功能为例来讲解如何通过模块+类的扩展方式扩展一个Python类。
相关代码路径如下:
●模块代码:
components/py_engine/adapter/haas/moddriver.c
●类代码:
components/py_engine/modules/driver/adc.c
首先要通过Python引擎的mp_obj_module_t数据结构定义一个driver_module模块。
const mp_obj_module_t driver_module = {
.base = {&mp_type_module},
.globals = (mp_obj_dict_t *)&driver_locals_dict,
};
然后通过数据结构mp_rom_map_elem_t 定义此模块对应的类表,截止本文撰写的时候driver模块支持的类有:ADC、PWM、GPIO、I2C、UART、SPI、RTC、TIMER及DAC等。
STATIC const mp_rom_map_elem_t driver_locals_dict_table[] = {
{MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_driver)},
{MP_OBJ_NEW_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&driver_adc_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&driver_pwm_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_GPIO), MP_ROM_PTR(&driver_gpio_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&driver_i2c_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_UART), MP_ROM_PTR(&driver_uart_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&driver_spi_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&driver_rtc_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_TIMER), MP_ROM_PTR(&driver_timer_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_CAN), MP_ROM_PTR(&driver_can_type)},
//{MP_OBJ_NEW_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&driver_dac_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_IR), MP_ROM_PTR(&driver_ir_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&driver_wdt_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_KeyPad), MP_ROM_PTR(&driver_keypad_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_Location), MP_ROM_PTR(&driver_location_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_UND), MP_ROM_PTR(&driver_und_type)},
{MP_OBJ_NEW_QSTR(MP_QSTR_Crypto), MP_ROM_PTR(&driver_crypto_type)},
};
接下来需要通过Python引擎的mp_obj_type_t数据结构定义driver_adc_type模块,此结构体的成员主要包含构造函数,打印函数,功能映射表等信息。
const mp_obj_type_t driver_adc_type = {
.base = {&mp_type_type},
.name = MP_QSTR_ADC, // ADC 模块名称
.print = adc_obj_print, // 打印函数
.make_new = adc_obj_make_new, // 构造函数
.locals_dict = (mp_obj_dict_t *)&adc_locals_dict, // 功能映射表
};
类和模块一样,也是通过数据结构mp_rom_map_elem_t来定义函数功能表。
STATIC const mp_rom_map_elem_t adc_locals_dict_table[] = {
{MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ADC)},
{MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&adc_obj_open)},
{MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&adc_obj_close)},
{MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&adc_obj_read)},
};
下面是通过ADC进行读操作的具体函数实现:
STATIC mp_obj_t obj_read(size_t n_args, const mp_obj_t *args)
{
LOGD(LOG_TAG, "entern %s; n_args = %d;\n", __func__, n_args);
int ret = -1;
adc_dev_t *adc_device = NULL;
int32_t adc_value = -1;
if (n_args < 1)
{
LOGE(LOG_TAG, "%s: args num is illegal :n_args = %d;\n", __func__, n_args);
return mp_const_none;
}
mp_obj_base_t *self = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
mp_adc_obj_t* driver_obj = (mp_adc_obj_t *)self;
if (driver_obj == NULL)
{
LOGE(LOG_TAG, "driver_obj is NULL\n");
return mp_const_none;
}
adc_device = py_board_get_node_by_handle(MODULE_ADC, &(driver_obj->adc_handle)); // 获取ADC设备的指针
if (NULL == adc_device) {
LOGE(LOG_TAG, "%s: py_board_get_node_by_handle failed;\n", __func__);
return mp_const_none;
}
(void)aos_hal_adc_value_get(adc_device, (void *)&adc_value, 0); // 呼叫C语言进行ACD读操作的API
LOGD(LOG_TAG, "%s:out adc_value = %d;\n", __func__, adc_value);
return MP_ROM_INT(adc_value);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR(adc_obj_read, 1, obj_read);
在driver模块中扩展ADC类之后,在Python应用层代码中使用ADC类的案例如下:
from driver import ADC # 从driver库中导入ADC类
adc = ADC() # 新建一个ADC设备对象
adc.open("ADC0") # 打开ADC的通道0
value = adc.read() # 进行ADC读操作
print(value)
adc.close() # 关闭ADC对象(即关闭ADC通道0)
开发者支持
如需更多技术支持,可加入钉钉开发者群,或者关注微信公众号。
GITHUB: https://github.com/alibaba/AliOS-Things/tree/rel_3.3.0
GITEE: https://gitee.com/organizations/alios-things/projects
CODECHINA: https://codechina.csdn.net/alios-things/AliOS-Things/-/tree/rel_3.3.0
更多技术与解决方案介绍,请访问HaaS官方网站https://haas.iot.aliyun.com。
今天的文章MircoPython 的组件扩展方法分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/9820.html