MicroPython学习笔记

1、MicroPython简介

1.1 MicroPython是什么

  由Micro+Python两个部分组成;Micro代表微小Python是编程语言,两者合起来的字面意思就是微型的Python。实际上MicroPython就是用于嵌入式系统上的Python。Python是一种脚本语言,是一款非常容易使用的脚本语言,语法简介、使用简单、功能强大、容易扩展。有非常多的库可以使用。网络功能和计算功能也很强。方便的和其他语言配合使用。完全开源,受到原来越多的开发者青睐。不过由于受到硬件成本、运行性能、开发习惯等原因。没有在通用嵌入式方面得到太多应用。所以MicroPython应运而生。

1.2 MicroPython的历史

​  由英国剑桥大学的教授Damien Georage(达米安乔治)发明的。随着半导体技术和制造工艺的快速发展,芯片的升级换代速度也越来越快,芯片的功能、内部的存储器容量和资源不断增加,而成本却在不断降低。特别是随着像ST公司和乐鑫公司高性价比的芯片和方案应用越来越多,这就给Python在低端嵌入式系统上的使用带来了可能。

1.3 MicroPython的特点

​​  1)MicroPython并没有带来一种全新的编程语言;
​​  2)为嵌入式开发带来了一种新的编程方式和思维,让大家可以将重点放在应用层的开发上。
​  3)MicroPython的特点是简单易用、移植性好、程序容易维护。


2、基础知识

2.1 MicroPython的系统结构

  一个MicroPython系统的典型结构所示,它由微控制器(系统底层)硬件、MicroPython固件用户程序三大部分组成。
MicroPython学习笔记_第1张图片
图2.1 MicroPython系统的典型结构


2.2 MicroPython的REPL

  REPL是Read-Evaluate-Print Loop(读取-计算-输出循环)的缩写。在MagicBox Project屏蔽了REPL交互编程功能,而采用了文本编程。


3、硬件平台介绍

3.1 基本硬件平台介绍

  MicroPython可以在多种嵌入式硬件平台上运行,目前已经有STM32、ESP8266/ESP32、CC3200、dsPIC33、MK20DX256、nRF51/nRF52、MSP432等多个平台。公司MagicBox基于STM32F4 DISCOVERY开发板开发。
MicroPython学习笔记_第2张图片
图3.1 STM32F4 DISCOVERY开发板

4、框架介绍

官方micropython代码托管在github上micropython。

4.1 添加python有两种方法:

4.1.1 在官方的python接口之上直接使用python的语法写自己的python脚本来实现相应的功能

优点:无需自己编译micropython的代码

缺点:可扩展性有限,某些底层功能单纯使用python接口无法完成

4.1.2 基于micropython的代码,用c语言扩展micropython的接口

优点:大大增加可扩展性,满足开发需求

缺点:需要修改micropython的代码


4.2 用C添加接口

4.2.1 在ports/stm32文件夹下新建一个文件test.c

打开test.c,输入以下内容,来实现一个简单的模块添加

#include "stdint.h"
#include "stdio.h"

#include "py/obj.h"
#include "py/runtime.h"
//定义的test全局字典,之后我们添加type和function就要添加在这里
STATIC const mp_rom_map_elem_t test_globals_table[] = {
    {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_test)},   //这个对应python层面的__name__ 属性
};
//这个可以认为是把test_globals_table注册到 mp_module_test.globals里面去
STATIC MP_DEFINE_CONST_DICT(mp_module_test_globals, test_globals_table);   

//这个是定义一个module类型
const mp_obj_module_t mp_module_test = {
    .base = {&mp_type_module},    
    .globals = (mp_obj_dict_t *)&mp_module_test_globals,
};

4.2.2 可以看到test.c主要有三个部分,这三个部分是添加一个模块必须要有的,可以打开官方的其他模块代码都是遵循这样一个模型

mp_rom_map_elem_t test_globals_table[]

MP_DEFINE_CONST_DICT(mp_module_test_globals, test_globals_table)

mp_obj_module_t mp_module_test


4.2.3 把test.c添加到Makefile文件中去

打开Makefile找到如下脚本的地方,按照格式添加test.c就可以了:

SRC_C = \
	main.c \
	uart.c \
        …………
	test.c\
	$(SRC_MOD)

4.2.4 定义的模块注册到micropython中去,这个是在mpconfigport.h文件中修改

找到MICROPY_PORT_BUILTIN_MODULES 定义的地方按照格式添加我们定义的module

extern const struct _mp_obj_module_t mp_module_machine;
extern const struct _mp_obj_module_t mp_module_network;
extern const struct _mp_obj_module_t mp_module_onewire;
extern const struct _mp_obj_module_t mp_module_test;    //这个是我们添加的,需要声明一下应用外部的struct

#define MICROPY_PORT_BUILTIN_MODULES \
    { MP_OBJ_NEW_QSTR(MP_QSTR_esp), (mp_obj_t)&esp_module }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_esp32), (mp_obj_t)&esp32_module }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_utime), (mp_obj_t)&utime_module }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_uos), (mp_obj_t)&uos_module }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_usocket), (mp_obj_t)&mp_module_usocket }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_network), (mp_obj_t)&mp_module_network }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR__onewire), (mp_obj_t)&mp_module_onewire }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_uhashlib), (mp_obj_t)&mp_module_uhashlib }, \
    { MP_OBJ_NEW_QSTR(MP_QSTR_test), (mp_obj_t)&mp_module_test }, \     //这个是我们添加的test

5、程序流程

以 MagicBox程序启动(基于STM32F4DISC) 为例。

5.1 复位后,进入到复位向量: micropython/ports/stm32/resethandler.s 中

    .syntax unified
    .cpu cortex-m4
    .thumb

    .section .text.Reset_Handler
    .global Reset_Handler
    .type Reset_Handler, %function

Reset_Handler:
    /* Save the first argument to pass through to stm32_main */
    mov  r4, r0

    /* Load the stack pointer */
    ldr  sp, =_estack

    /* Initialise the data section */
    ldr  r1, =_sidata
    ldr  r2, =_sdata
    ldr  r3, =_edata
    b    .data_copy_entry
.data_copy_loop:
    ldr  r0, [r1], #4 /* Should be 4-aligned to be as fast as possible */
    str  r0, [r2], #4
.data_copy_entry:
    cmp  r2, r3
    bcc  .data_copy_loop

    /* Zero out the BSS section */
    movs r0, #0
    ldr  r1, =_sbss
    ldr  r2, =_ebss
    b    .bss_zero_entry
.bss_zero_loop:
    str  r0, [r1], #4 /* Should be 4-aligned to be as fast as possible */
.bss_zero_entry:
    cmp  r1, r2
    bcc  .bss_zero_loop

    /* Initialise the system and jump to the main code */
    bl   SystemInit
    mov  r0, r4
    b    stm32_main

    .size Reset_Handler, .-Reset_Handler

C的函数入口是stm32_main


5.2 micropython/ports/stm32/main.c :

1)开启指令、数据等的Cache
2)中断、时钟配置
3)MicroPython的线程配置
4)LED、Switch、Machine库、RTC、UART0、I2C0、SDCard、LWIP(如果有的话)、GC(垃圾回收)、定时
器、CAN、USB(默认枚举成VCP + U盘)
5)运行内部 flash 文件系统或者外置 SD 卡文件系统中的 boot.py
6)运行内部 flash 文件系统或者外置 SD 卡文件系统中的 main.py
7)当 boot.py 和 main.py 运行结束后,进入到 REPL 模式。在该模式下,我们在PC中可以通过串口与MicroPython
实现命令行的交互


5.3 boot.py的创建及启动(其他py/txt/inf/ini等文件创建方式类似)

5.3.1 boot.py内容以字符串保存在fresh_boot_py[]里

#if MICROPY_HW_ENABLE_STORAGE 
static char fresh_boot_py[] =
"# boot.py -- run on boot-up\r\n"
"# can run arbitrary Python, but best to keep it minimal\r\n"
"\r\n"
"import machine\r\n"
"import pyb\r\n"
"import _thread\r\n"
"import mb\r\n"
"def thread_entry():\r\n"
"    while True:\r\n"
"        res = mb.ReadCH375Data()\r\n"
"        if not res:\r\n"
"            _thread.exit()\r\n"
"pyb.UART(2).init(115200, bits=8, parity=None, stop=1)\r\n"
"_thread.start_new_thread(thread_entry, ())\r\n"
"#pyb.main('main.py') # main script to run after this one\r\n"
#if MICROPY_HW_ENABLE_USB
"#pyb.usb_mode('VCP+MSC') # act as a serial and a storage device\r\n"
"#pyb.usb_mode('VCP+HID') # act as a serial device and a mouse\r\n"
#endif
;

5.3.2 在虚拟U盘里创建boot.py

res = f_stat(&vfs_fat->fatfs, fresh_boot_py, &fno);
if (res != FR_OK) {
    // doesn't exist, create fresh file
    f_open(&vfs_fat->fatfs, &fp, fresh_boot_py, FA_WRITE | FA_CREATE_ALWAYS);
    f_write(&fp, fresh_boot_py, sizeof(fresh_boot_py) - 1 /* don't count null terminator */, &n);
    f_close(&fp);
}

5.3.3 在micropython/ports/stm32/main.c中启动boot.py

const char *boot_py = "System/boot.py";
mp_import_stat_t stat = mp_import_stat(boot_py);
if (stat == MP_IMPORT_STAT_FILE) {
    int ret = pyexec_file(boot_py);
    PrintInfo("pyexec boot file result: %d", ret);
    if (ret & PYEXEC_FORCED_EXIT) {
        goto soft_reset_exit;
    }
    if (!ret) {
        flash_error(4);
    }
}

参考资料

1)《MicroPython入门指南》

2)《MicroPython开发环境搭建及移植使用分析》

3)《MicroPython补充说明》

你可能感兴趣的:(MicroPython学习笔记)