MicroPython分析

 

相关参考:Micropython--WIN7-64位下搭建开发环境

一介绍
Micro Python运行在微控制器上的Python。遵守MIT协议。由剑桥大学的理论物理学家乔治·达明设计。

Micro Python的软件特点:

  • Python 3 语法.
  • 完整的Python词法分析器, 解析器,编译器,虚拟机和运行时。
  • 包含命令行接口,可离线运行。
  • Python 字节码由内置虚拟机编译运行.
  • 有效的内部存储算法,能带来高效的内存利用率。整数变量存储在内存堆中,而不是栈中。
  • 使用Python decorators特性,函数可以被编译成原生机器码,虽然这一特性会带来大约2倍的内存消耗,但也使python有更快的执行速度。
  • 函数编译可设置使用底层整数代替python内建对象作为数字使用。有些代码的运行效率可以媲美c的效率,并且可以被python直接调用,适合做时间紧迫性,运算复杂度高的应用。
  • 通过内联汇编功能,应用可以完全接入底层运行时,内联汇编器也可以像普通的python函数一样调用。
  • 基于简单和快速标记的内存垃圾回收算法,运行周期少于4ms,许多函数都可以避免使用栈内存段,因此也不需要垃圾回收功能。

二 源码分析

      MicroPython实现了基本的python的词法分析器, 解析器,编译器,虚拟机和运行时。在github上获得代码:https://github.com/micropython/micropython

解压之后目录大概如下:

     主要目录的解释也可在根目录的README.md中找到。

本文以STM32 MicroPython为例。目前MicroPython可运行于类unix系统,PIC16,ARM等平台。

首先,需要了解Python的运行机制,可以参考:http://tech.uc.cn/?p=1932

     然后,micropython-master\py目录下个人认为是最核心的发动机,也就是MicroPython的实现,C语言实现的python的解析器,运行时,虚拟机组建。

    最后我们来看STM32是如何实现Python编程与控制的:

源码图中绿色注释的部分是STM32 MicroPython的主要代码,STM32(以下简称MCU)main函数位于stmhal/main.c,MCU启动后进入main函数,那么我们看main函数里干了什么事情:

int main(void) {
    // TODO disable JTAG

    // Stack limit should be less than real stack size, so we have a chance
    // to recover from limit hit.  (Limit is measured in bytes.)
    mp_stack_ctrl_init();
    mp_stack_set_limit((char*)&_ram_end - (char*)&_heap_end - 1024);

    /* STM32F4xx HAL library initialization:
         - Configure the Flash prefetch, instruction and Data caches
         - Configure the Systick to generate an interrupt each 1 msec
         - Set NVIC Group Priority to 4
         - Global MSP (MCU Support Package) initialization
       */
    HAL_Init();

    // set the system clock to be HSE
    SystemClock_Config();
         .........
port init, LED init, switch init , sdcard initµÈ°åÉÏÍâÉè³õʼ»¯
          ......
    // GC init
    gc_init(&_heap_start, &_heap_end);

    // Micro Python init
    mp_init();
    mp_obj_list_init(mp_sys_path, 0);
    mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); // current dir (or base dir of the script)
    mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash));
    mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash_slash_lib));
    mp_obj_list_init(mp_sys_argv, 0);

    // zero out the pointers to the mounted devices
    memset(MP_STATE_PORT(fs_user_mount), 0, sizeof(MP_STATE_PORT(fs_user_mount)));

    // Initialise low-level sub-systems.  Here we need to very basic things like
    // zeroing out memory and resetting any of the sub-systems.  Following this
    // we can run Python scripts (eg boot.py), but anything that is configurable
    // by boot.py must be set after boot.py is run.

    readline_init0();
    pin_init0();
    extint_init0();
    timer_init0();
    uart_init0();

    // Define MICROPY_HW_UART_REPL to be PYB_UART_6 and define
    // MICROPY_HW_UART_REPL_BAUD in your mpconfigboard.h file if you want a
    // REPL on a hardware UART as well as on USB VCP
#if defined(MICROPY_HW_UART_REPL)
    {
        mp_obj_t args[2] = {
            MP_OBJ_NEW_SMALL_INT(MICROPY_HW_UART_REPL),
            MP_OBJ_NEW_SMALL_INT(MICROPY_HW_UART_REPL_BAUD),
        };
        MP_STATE_PORT(pyb_stdio_uart) = pyb_uart_type.make_new((mp_obj_t)&pyb_uart_type, MP_ARRAY_SIZE(args), 0, args);
    }
#else
    MP_STATE_PORT(pyb_stdio_uart) = NULL;
#endif

    i2c_init0();
   
soft_reset_exit:
}
在main函数中大概是初始化MCU的时钟,初始化各个外设,然后调用python的垃圾回收器gc_init(), 初始化python对象列表,相当于启动了python的虚拟机(本人 暂时对python的实现机制理解有限)。后面紧接着启动了python的REPL,bind到uart6,这也就允许你通过串口来执行python代码了。
启动之后单片机底层的操作通过micropython-master\stmhal\hal\f4目录下的HAL驱动来完成,那么在串口敲下的命令又是如何调用底层的驱动的呢?

三 内建对象集成
    拿简单的DAC来说,与之相关的文件为micropython-master\stmhal\下的dac.h, dac.c, dac.x文件声明并定义了MicroPython board DAC类的方法与属性,通过MP_DEFINE_CONST_FUN_OBJ_KW或MP_DEFINE_CONST_FUN_OBJ_x等注册给MicroPython的内建对象。比如下面的函数:pyb_dac_write()函数调用STM32 的HAL lib实现硬件的操作,通过MP将此函数注册为pyb_dac_write_obj
/// \method write(value)
/// Direct access to the DAC output (8 bit only at the moment).
STATIC mp_obj_t pyb_dac_write(mp_obj_t self_in, mp_obj_t val) {
    pyb_dac_obj_t *self = self_in;
    if (self->state != DAC_STATE_WRITE_SINGLE) {
        DAC_ChannelConfTypeDef config;
        config.DAC_Trigger = DAC_TRIGGER_NONE;
        config.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;
        HAL_DAC_ConfigChannel(&DAC_Handle, &config, self->dac_channel);
        self->state = DAC_STATE_WRITE_SINGLE;
    }
    // DAC output is always 12-bit at the hardware level, and we provide support
    // for multiple bit "resolutions" simply by shifting the input value.
    HAL_DAC_SetValue(&DAC_Handle, self->dac_channel, DAC_ALIGN_12B_R,
        mp_obj_get_int(val) << (12 - self->bits));
    HAL_DAC_Start(&DAC_Handle, self->dac_channel);
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_dac_write_obj, pyb_dac_write);
最后将所有的方法注册给MicroPython

STATIC const mp_map_elem_t pyb_dac_locals_dict_table[] = {
    // instance methods
    { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_dac_init_obj },
    { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&pyb_dac_write_obj },
    #if defined(TIM6)
    { MP_OBJ_NEW_QSTR(MP_QSTR_noise), (mp_obj_t)&pyb_dac_noise_obj },
    { MP_OBJ_NEW_QSTR(MP_QSTR_triangle), (mp_obj_t)&pyb_dac_triangle_obj },
    { MP_OBJ_NEW_QSTR(MP_QSTR_write_timed), (mp_obj_t)&pyb_dac_write_timed_obj },
    #endif
    // class constants
    { MP_OBJ_NEW_QSTR(MP_QSTR_NORMAL),      MP_OBJ_NEW_SMALL_INT(DMA_NORMAL) },
    { MP_OBJ_NEW_QSTR(MP_QSTR_CIRCULAR),    MP_OBJ_NEW_SMALL_INT(DMA_CIRCULAR) },
};
STATIC MP_DEFINE_CONST_DICT(pyb_dac_locals_dict, pyb_dac_locals_dict_table);
const mp_obj_type_t pyb_dac_type = {
    { &mp_type_type },
    .name = MP_QSTR_DAC,
    .make_new = pyb_dac_make_new,
    .locals_dict = (mp_obj_t)&pyb_dac_locals_dict,
};

该结构体在dac.h中声明为外部变量:

extern const mp_obj_type_t pyb_dac_type;
然后在stmhal/modpyb.c中包含了dac.h,并将pyb_dac_type声明为python的对象:

#if MICROPY_HW_ENABLE_DAC
    { MP_OBJ_NEW_QSTR(MP_QSTR_DAC), (mp_obj_t)&pyb_dac_type },
#endif
因此如果想添加内建的对象或方法应该遵循如下的步骤与原则:
1. 建立mp对象:
const mp_obj_type_t pyb_led_type = {
    { &mp_type_type },
    .name = MP_QSTR_LED,   ///name
    .print = led_obj_print,       ///重载的print方法
    .make_new = led_obj_make_new, ///构造函数
    .locals_dict = (mp_obj_t)&led_locals_dict, ///该对象所拥有的方法字典
};
2. 建立方法字典led_locals_dict:
 
3. 实现方法
实现上图中画横线的方法以及第一步中的重载的print方法,构造方法等。
4. 将mp对象pyb_led_type 添加到modpyb.c中的pyb_module_globals_table[]全局python对象表里:

{ MP_OBJ_NEW_QSTR(MP_QSTR_DAC), (mp_obj_t)&pyb_dac_type }

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
昨天看了坛友的推荐,发现MicroPython开发板的确很不错。功能比Arduino强,使用也很灵活。淘宝上microPython开发板很少,价格也很高,都是200以上。因此,考虑到价格原因,加上板上元件不多,网友假如自己DIY完成的话,起码可以节约70%以上成本。故将其所有资料开源。官方网站:micropython.org Micro Python的硬件特点: STM32F405RG MCU. 168 MHz Cortex-M4 CPU with 32-bit hardware floating point. 1 MiB flash storage, 192 KiB RAM. USB口, 支持 串口,通用存储,HID协议。 SD卡插槽。 MMA76603轴加速度计. 4 LEDs, 1复位按钮, 1通用按钮. 3.3V0.3A板载 LDO , 可从USB口或者外置电池供电。 实时时钟。 30个通用IO口,其中28个支持5V输入输出。 2个 SPI接口, 2个 CAN接口, 2个I2C接口, 5个USART接口. 14个 12-bit ADC引脚。 2个DAC 引脚。 Micro Python的软件特点: Python 3 语法. 完整的Python词法分析器, 解析器,编译器,虚拟机和运行时。 包含命令行接口,可离线运行。 Python 字节码由内置虚拟机编译运行. 有效的内部存储算法,能带来高效的内存利用率。整数变量存储在内存堆中,而不是栈中。 使用Python decorators特性,函数可以被编译成原生机器码,虽然这一特性会带来大约2倍的内存消耗,但也使python有更快的执行速度。 函数编译可设置使用底层整数代替python内建对象作为数字使用。有些代码的运行效率可以媲美c的效率,并且可以被python直接调用,适合做时间紧迫性,运算复杂度高的应用。 通过内联汇编功能,应用可以完全接入底层运行时,内联汇编器也可以像普通的python函数一样调用。 基于简单和快速标记的内存垃圾回收算法,运行周期少于4ms,许多函数都可以避免使用栈内存段,因此也不需要垃圾回收功能。 MicroPython开发板入门图说明: MicroPython开发板原理图截图:

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值