小白用Python编写小安派第八篇

[复制链接]
查看973 | 回复6 | 2023-8-30 14:37:37 | 显示全部楼层 |阅读模式
本帖最后由 妖猊 于 2023-8-30 14:36 编辑

第八篇 正文:
在 IOT、智能终端等嵌入式应用场景中,脚本开发是一个方便快捷的解决方案。也是小白入门最方便快捷的语言(个人认为)

                               
登录/注册后可看大图

说到嵌入式使用脚本语言开发,可能首先想到的就是 micropython , micropython 可以让工程师使用脚本语言 python 进行 mcu 开发,极大地降低了开发门槛。如果为没有现成 micropython 固件的 mcu 移植 micropython 显然也是一件工程浩大且门槛很高的工作。有现成的micropython 固件就还好,像现在的esp32、esp8266都有现成的micropython 固件了


                               
登录/注册后可看大图


但是呢,MicroPython是Python3的精简实现,包括Python标准库的一小部分,经过优化可在微控制器和受限的环境中运行,在官方提供了相应的开发板,但最低的配置都需要32KB的SRAM空间和4KB的STACK空间,对MCU的性能也有绝对的要求。

想要在一个MCU上运行Python程序,一般步骤如下:首先得需要一个Linux环境(大多数人选择通过虚拟机来安装Linux)、然后需要更新相应的命令工具并下载交叉编译工具和编译器(交叉编译器选择gcc-arm-none-eabi、编译器选择gcc)、接着需要下载MicroPython源代码,生成固件程序(建议选择官方已经支持的开发板,否则需要自行实现和移植)、最后通过烧录工具或者使用USB的DFU模式烧录程序到MCU、至此才可以开始使用Python编程来实现应用功能;期间一步都不能错哦……

而且 python 的运行效率较低,在资源紧缺的 mcu 中显得尤为明显,使用 python 开发也难以充分利用 mcu 的中断、 dma 等硬件特性。在高实时性的信号处理、数据采集、实时控制等应用中,python 难以成为真正落地于生产环境。


                               
登录/注册后可看大图

就目前而言,在 mcu 开发中,占 80% 左右的开发仍然是使用 c 语言,c++ 也仅占不到20%。

但是无疑脚本语言的便利性是非常明显的。服务器端的开发者往往熟悉 python 和 JavaScript 等支持面向对象的脚本语言,如果能够直接用脚本语言调用 mcu 的功能,将明显降低开发难度。

那么,如果使用 c 语言进行 mcu 嵌入式开发,又向上位机或者服务器提供面向对象的脚本语言调用接口,不就可以兼顾 mcu 运行效率和开发效率了吗?


                               
登录/注册后可看大图

PiKaScript应运而生

PiKaScript可以为资源受限的MCU提供极易部署和拓展的Python脚本支持。PiKaScript不需要操作系统和文件系统,支持裸机运行,最低可运行在RAM≥4KB,FLASH≥32KB的MCU中,而且还支持KEIL、IAR等IDE集成开发环境。此外PiKaScript是完全开源的(https://github.com/pikasTech/pikascript),采样的是MIT协议,允许修改和商用,但是要注意保留原作者的署名即可。

PikaScript可以在所有支持libc的裸机和操作系统上运行,只需要编译器能够支持C99标准即可。当前PikaScript仅支持32位和64位内核的MCU,暂不支持8位内核的MCU;考虑到拓展模块的资源占用情况,如果是ARM内核的MCU推荐最低应该配备64KB FLASH和8KB SRAM,如果是RISC内核的MCU推荐最低应该配备128KB FLASH和8KB SRAM。

Pikascrpit 库可以为 c 语言开发的 mcu 工程提供面向对象的脚本语言调用接口。PikaPython 有以下几个特点:

  • 支持裸机运行,可运行于内存 4Kb 以上的 mcu 中,如 stm32f103 ,esp32。
  • 支持跨平台,可运行于 linux, windows 环境,方便仿真开发。
  • 代码可读性强,仅使用 C 标准库,尽可能的结构清晰(尽我所能),几乎不使用宏。
下面我就照搬一些pikapython文档的讲解和配上一些自己的理解,哈哈哈,毕竟学习也是从官网文档开始学起
一架构原理:

PikaPython 的架构示意图如下图所示。从上往下逐层分析。


                               
登录/注册后可看大图


1、PikaRun 脚本运行层

PikaRun 脚本运行层是 PikaPython 的最上层调用接口,只需要调用 obj_run 就可以实现脚本运行。在调用 obj_run时,需要指定一个对象,脚本在运行时,会检索这个对象的方法和这个对象的子对象的方法。

下图所示的是一个常见的嵌入式开发中的对象结构,sys 是最上层的对象,sys 对象有 reboot() 方法,sys 对象下挂载了 device 子对象和 task 子对象,这两个对象下面又挂载了子对象,每个子对象有自己的方法。


                               
登录/注册后可看大图

这个时候,我们只需要在 obj_run 中传入最上面的 sys 对象的指针,就可以用如下图所示的方法调用所有对象的所有方法。其中,reboot() 方法直接属于 sys 对象,因此使用直接运行 obj_run(sys, "reboot()") 就可以调用,而 led 对象则通 过 obj_run(sys, "device.led.on()") 进行调用。


                               
登录/注册后可看大图

在实际开发中,我们可以让 mcu 将串口接收到的数据直接作为脚本运行。

例如:

  1. obj_run(sys, uartReciveBuff);
复制代码

其中 uartReciveBuff 是串口接收到的数据。

这时向 mcu 的串口中发送 "device.led.on()" ,就可以点亮 led 灯。

二、PikaObj 对象支持层

现在已经知道如何使用 PikaPython 在已有的对象结构中执行脚本。那么下一个问题就是,如何构造对象,又如何为对象定义属性和方法呢?

(1)构造器函数

PikaPython 通过一个构造器函数来构造对象。一个构造器函数对应一个 PikaPython 中的类。如下所示的就是一个 LED 的构造器函数。在 PikaPython 中,所有的构造器函数都使用相同的入口参数和返回参数。

入口参数 args 是一个参数列表,args 内部基于链表,可以传入任意个数、任意类型的参数,此处 args 是构造器的初始化参数,在进行含参构造时将会用到。 构造器函数的返回值是一个 PikaObj 对象。

  1. PikaObj * New_LED(Args *args){
  2.   // 继承自MimiObj基本类   
  3.   PikaObj *self = New_PikaObj(args);   
  4.   // 为对象定义属性   
  5.   obj_setInt(self, "isOn", 0);   
  6.   // 为LED1对象绑定on()方法   
  7.   obj_defineMethod(self, "on()", onFun);   
  8.   return self;
  9. }
复制代码

构造器的第一行代码用于类继承, LED  类继承自  Pikaobj  基类, Pikaobj  基类是所有类的源头。

obj_setInt 为  LED  类定义一个属性,属性名为"isOn",初始值为为0。


                               
登录/注册后可看大图

obj_defineMethodLED 类绑定一个方法,绑定的方法为 on() 方法。onFunon() 方法所绑定的 c 原生函数的函数指针。onFun 函数具体的编写方式在第三章和第四章中介绍。

(2)构造对象


构造对象有两种方法,一种是构造 obj_run 传入的对象,称为根对象,如下图中的 sys 对象,其他对象则是一般对象,挂载在根对象之下。

一般一个项目中只构造一个根对象。


                               
登录/注册后可看大图

newRootObj 函数用于构造根对象。构造根对象需要传入对象名 "led" 和构造器函数指针,newRootObj 的返回值是根对象的指针。

  1. PikaObj *led = newRootObj("led", New_LED);
复制代码

一般对象的构造在父对象的构造器中完成,如果要在 sys 对象下挂载 led 子对象,就可以这样编写 SYS 类的构造器函数:

  1. PikaObj * New_SYS(Args *args){
  2.     // 继承自MimiObj基本类
  3.     PikaObj *self = New_PikaObj(args);
  4.     // 通过LED类的构造器导入LED类
  5.     obj_import(self, "LED", New_LED);
  6.     // 使用LED类新建led对象,led对象作为sys对象的子对象
  7.     obj_newObj(self, "led", "LED");
  8.     return self;
  9. }
复制代码

obj_import 通过构造函数的函数指针导入一个类,上面代码中导入的类名为 LED。obj_newObj 通过导入的类新建对象,新建对象作为子对象挂载在当前类下。 这时,通过调用下面的函数,就可以得到一个挂载了 led 对象的 sys 根对象了。

  1. PikaObj *sys = newRootObj("sys", New_SYS);
复制代码
三、dataArgs 动态参数列表

dataArgs 是基于链表的动态参数列表,其结构体是 Args,dataArgs 在运行时动态地申请和释放内存,所以可以在运行时增删改查参数,Pikaobj 的属性和方法信息的存取均基于 dataArgs 参数列表实现。

dataArgs 支持整形、浮点型、字符串、指针类型的参数,也支持将原生的c语言变量绑定为 dataArgs 中的参数。

下面的例子是 Args 的基本使用方法。dataArgs 的实现原理会在后续的文章中介绍,在此文中不作重点。

  1. // 新建一个参数列表
  2. Args *args = New_Args();
  3. // 向参数列表中存入一个整形参数a,值为1
  4. args_setInt(args, "a", 1);
  5. // 取出参数a,值为1
  6. int a = args_getInt(args, "a");
  7. // 修改a的值为2
  8. args_setInt(args, "a", 2);
  9. // 再取出a,值为2
  10. a = args_getInt(args, "a");
  11. // 销毁参数列表
  12. args_deinit(args);
复制代码
四、dataMemory

dataMemory 为 dataArgs 提供动态内存申请和释放,有关内存管理的学起来比较麻烦。。。。。。





















本帖被以下淘专辑推荐:

回复

使用道具 举报

爱笑 | 2023-8-30 14:52:26 | 显示全部楼层
妖哥高产!
用心做好保姆工作
回复

使用道具 举报

Mr.Tail | 2023-8-30 16:53:42 | 显示全部楼层
妖哥牛逼!
回复

使用道具 举报

粉色小风扇 | 2023-8-30 16:55:10 | 显示全部楼层
妖哥666
回复

使用道具 举报

粉肠 | 2023-8-31 07:35:10 | 显示全部楼层
资瓷妖哥
回复

使用道具 举报

iiv | 2023-9-2 12:09:56 | 显示全部楼层
666
回复

使用道具 举报

iiv | 2023-9-15 19:57:39 | 显示全部楼层
插眼
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则