MircoPython 的组件扩展方法

MircoPython 的组件扩展方法1、背景本节内容将详细介绍如何扩展一个新的MicroPython的组件。组件扩展方式分两种:模块扩展,模块+类扩展。右边netmgr功能以模块的方式扩展的,在使用的时候直接导入模块进行使用。左边的ADC是通过模块+类的方式进行扩展,使用的时候需要通过模块导入ADC类进行使用。图1组件扩展方式2、基础知识组件扩展过程中会用到函数定义、参数类型转及如何通过Python呼叫C语言代码等功能,下面是对这些功能的说明。2.1函数和参数定义方式MP_DEFI…

MircoPython 的组件扩展方法

1、背景

本节内容将详细介绍如何扩展一个新的MicroPython 的组件。
组件扩展方式分两种:模块扩展,模块+类扩展。
右边netmgr 功能以模块的方式扩展的,在使用的时候直接导入模块进行使用。左边的ADC 是通过模块+类的方式进行扩展,使用的时候需要通过模块导入ADC类进行使用。

 

MircoPython 的组件扩展方法

图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)

开发者支持

如需更多技术支持,可加入钉钉开发者群,或者关注微信公众号。

MircoPython 的组件扩展方法

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

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注