为micropython添加模块(1)-全局模块

使用官方文档的方式为micropython添加一个全局模块

概述

官方提供的开发指导文档中, 描述了一个最简单的增加模块的样例:

http://docs.micropython.org/en/latest/develop/porting.html#adding-a-module-to-the-port

在本文中, 我分析管官方开发指导文档中的代码撰写流程, 根据自己的理解, 创建一个新的模块. 并试图通过实践, 观察代码的实际工作效果.

在我的样例代码中, 我将要创建一个LED灯的模块, 并包含on()和off()两个函数对应开灯和关灯两个动作。

根据官方描述步骤创建一个led模块

为新模块创建一个源文件

参考官方样例的命名规范, 这里在lpc5500移植项目的目录下创建mod_led.c

PS: 原始的命名范例中没有使用下划线"_"将前缀"mod"单独分隔出来, 但我通过阅读代码发现, 增加下划线的命名方式更符合micropython的命名规范. 实际上, 在micropython的源代码中, 都是使用下划线作为命名单词的分隔符的. 我有点不明白为什么文件的命令没有使用分隔符. 按照我的开发习惯, 在规模比较大的软件项目中, 使用分隔符的命名方式可读性更好, 所以在我自己的练习代码中, 将会使用下划线作为名字之间的分隔符.

在新模块中首先编写最基本的led模块对应底层驱动的三个函数:

  • hw_led_init()
  • hw_led_on()
  • hw_led_off()

目前先做一个最简单的样例, 实现从python到c语言函数的调用. 目前的样例中仅仅传递函数指针, 不传入任何参数, 在后续的文章中将专门探讨传递参数的问题.

逐层封装

毕竟使用了armgcc编译器, 底层的代码还是以C语言方式运行的, 从python到底层的C就是层层调用. 反过来在开发过程中, 准备好底层的C代码之后, 想要在python层面上被识别, 就需要层层封装并注册.

从官方的样例代码中可以看到, 大体上分为三个层次的封装, 对应了三个封装的宏操作:

  • MP_DEFINE_CONST_FUN_OBJ_0. 将函数封装成对象. 在python中, 一切皆是对象.
  • MP_DEFINE_CONST_DICT. 将所有的函数对象封装成一个操作清单.
  • MP_REGISTER_MODULE. 将操作清单和对象绑定在一起, 注册到python系统中.

到目前为止, 完整的mod_led.c源文件内容如下:

/* mod_led.c */
#include "py/runtime.h"

#include "fsl_common.h"
#include "fsl_iocon.h"
#include "fsl_clock.h"
#include "fsl_gpio.h"

/******************************************************************************
 * hardware level functions.
 *****************************************************************************/
void hw_led_init(void)
{
    CLOCK_EnableClock(kCLOCK_Iocon);
    CLOCK_EnableClock(kCLOCK_Gpio1);

    uint32_t pinmode = IOCON_FUNC0
                     | IOCON_MODE_INACT
                     | IOCON_GPIO_MODE
                     | IOCON_DIGITAL_EN
                     ;
    IOCON_PinMuxSet(IOCON, 1u, 6u, pinmode); /* pio1_6. */

    gpio_pin_config_t gpio_pin_config;
    gpio_pin_config.pinDirection = kGPIO_DigitalOutput;
    gpio_pin_config.outputLogic = 1u;
    GPIO_PinInit(GPIO, 1u, 6u, &gpio_pin_config);
}

void hw_led_on(void)
{
    GPIO_PinWrite(GPIO, 1u, 6u, 0u);
}

void hw_led_off(void)
{
    GPIO_PinWrite(GPIO, 1u, 6u, 1u);
}

/******************************************************************************
 * object function wrappers.
 *****************************************************************************/
STATIC mp_obj_t led_init_func(void)
{
    hw_led_init();
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(led_init_obj, led_init_func);

STATIC mp_obj_t led_on_func(void)
{
    hw_led_on();
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(led_on_obj, led_on_func);

STATIC mp_obj_t led_off_func(void)
{
    hw_led_off();
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(led_off_obj, led_off_func);

/******************************************************************************
 * pack the objects into module.
 *****************************************************************************/
STATIC const mp_rom_map_elem_t led_module_globals_table[] = {
    { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_led) },
    { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&led_init_obj) },
    { MP_ROM_QSTR(MP_QSTR_on)  , MP_ROM_PTR(&led_on_obj)   },
    { MP_ROM_QSTR(MP_QSTR_off) , MP_ROM_PTR(&led_off_obj)  },
};
STATIC MP_DEFINE_CONST_DICT(led_module_globals, led_module_globals_table);

const mp_obj_module_t led_module = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t *)&led_module_globals,
};

MP_REGISTER_MODULE(MP_QSTR_led, led_module, 1);

/* EOF. */

在Makefile文件中更新SRC_C和SRC_QSTR

在SRC_C中添加mod_led.c文件, 将新增代码编译firmware中.

在SRC_QSTR中添加mod_led.c文件, 让build过程扫描mod_led.c文件, 从中提取字符串关键字增加到micropython的qstr列表当中.

第二步操作是根据官方文档的说明进行的操作, 应该是比较正式的添加qstr的方式. 实际上, 在之前的研究中, 我是人为地在qstrdefsport.h文件中增加qstr关键字的. 这里终于看到官方推荐的做法了.
在这里插入图片描述
按照官方开发文档的说明, 到这里就可以重新make, 然后可以在micropython的交互命令行中引用led模块的. 但实际情况呢… 然并卵.

好不容易清除编译错误, 把firmware下载到板子上, 还是不能识别新模块, "import led"报错无效.


进一步调试

通过各种比较代码, 比较同mimxrt的移植和自己早年开发的参考代码, 最终通过如下操作实现了预期的功能.

在mpconfigport.h文件中注册新模块

在mpconfigport.h文件中添加代码如下:


extern const struct _mp_obj_module_t led_module;

#define MICROPY_PORT_BUILTIN_MODULES \
    { MP_ROM_QSTR(MP_QSTR_led), MP_ROM_PTR(&led_module) }, \

通过编译之后, 下载firmware到电路板上, 复位后通过终端命令行交互, 能够成功识别led模块, 并且在板子上确实看到led灯受控亮灭了.
在这里插入图片描述
这句话顾名思义是向系统中注册可以识别的模块的. 同官方文档中的最后一步封装注册的操作MP_REGISTER_MODULE相似, 但貌似没起作用.

进一步追一下代码, 看到了MP_REGISTER_MODULE操作的定义, 忍不住爆了句粗口"娘…希…匹".

在源文件"py/obj.h"中, MP_REGISTER_MODULE仅仅操作了个寂寞.

在这里插入图片描述
这句话确实没用.

总结

官方开发文档的内容同实际代码有一定偏差, 完全照搬不能实现预期功能, 但已经指明了一个正确的和规范化的方向, 仍然有很重要的参考价值. 本例中, 我通过进一步的调试, 通过附加最后一步注册的操作, 最终实现了官方文档中预期的功能.

另一方面, 官方文档中讲述的方法仅仅适用于增加全局模块, 例如实现电路板复位或者系统延时之类的功能, 仍然木有介绍类似于新创建一个类实例的那种模块, 例如我期望类似于下面的这种用法:

led1 = led(1)
led1.on()
led1.off()

在这种用法中, 首先从led类中实例化一个对象led1, 后续的操作都是针对对象led1进行展开.

所以, 到目前为止, micropython官方提供的参考开发文档仍未满足我的需求. 实际上, 我早年在ke18f上的移植中已将摸索出了这种开发模式, 但我仍希望看到官方开放相关的文档, 以描述一种规范的开发方式.

幸运的是, 我看到mimxrt的移植中呈现了我想要的这种实现的代码, 虽然没有专门的文档描述, 但mimxrt的移植相当简单, 几乎只有一个led类的实现, 那么, 通过对这份代码进行解析, 也可以间接地学习官方实现类模块的开发规范.

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
2022 / 01/ 30: 新版esptool 刷micropython固件指令不是 esptool.py cmd... 而是 esptool cmd... 即可;另外rshell 在 >= python 3.10 的时候出错解决方法可以查看:  已于2022年发布的: 第二章:修复rshell在python3.10出错 免费内容: https://edu.csdn.net/course/detail/29666 micropython语法和python3一样,编写起来非常方便。如果你快速入门单片机玩物联网而且像轻松实现各种功能,那绝力推荐使用micropython。方便易懂易学。 同时如果你懂C语音,也可以用C写好函数并编译进micropython固件里然后进入micropython调用(非必须)。 能通过WIFI联网(2.1章),也能通过sim卡使用2G/3G/4G/5G联网(4.5章)。 为实现语音控制,本教程会教大家使用tensorflow利用神经网络训练自己的语音模型并应用。为实现通过网页控制,本教程会教大家linux(debian10 nginx->uwsgi->python3->postgresql)网站前后台入门。为记录单片机传输过来的数据, 本教程会教大家入门数据库。  本教程会通过通俗易懂的比喻来讲解各种原理与思路,并手把手编写程序来实现各项功能。 本教程micropython版本是 2019年6月发布的1.11; 更多内容请看视频列表。  学习这门课程之前你需要至少掌握: 1: python3基础(变量, 循环, 函数, 常用库, 常用方法)。 本视频使用到的零件与淘宝上大致价格:     1: 超声波传感器(3)     2: MAX9814麦克风放大模块(8)     3: DHT22(15)     4: LED(0.1)     5: 8路5V低电平触发继电器(12)     6: HX1838红外接收模块(2)     7:红外发射管(0.1),HX1838红外接收板(1)     other: 电表, 排线, 面包板(2)*2,ESP32(28)  
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值