From 19405cca28f083d0214c610c0edc3a9fad2602a5 Mon Sep 17 00:00:00 2001 From: lyon Date: Mon, 2 Aug 2021 14:03:07 +0800 Subject: [PATCH] add wechat post --- doc/artical/01-wechatPost01.md | 238 +++++++++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) diff --git a/doc/artical/01-wechatPost01.md b/doc/artical/01-wechatPost01.md index e69de29bb..345ad7dba 100644 --- a/doc/artical/01-wechatPost01.md +++ b/doc/artical/01-wechatPost01.md @@ -0,0 +1,238 @@ +# 使用mimiscript为你的mcu轻松添加面向对象脚本支持 + +## abstrict + +mimiscript是一个使用c语言写成的脚本支持库,可以非侵入地为mcu提供对象化的脚本支持。 + +mimiscript能够在完全不修改原本工程代码的情况下(非侵入性),将c语言的函数以及变量绑定到脚本对象上,可以直接使用脚本调用。 + +mimiscript默认支持python语法绑定,也可以支持typescript语法绑定。 + +## 1.introduction + +在嵌入式开发的IOT、智能终端等应用场景中,脚本开发是一个方便快捷的解决方案。 + +说到嵌入式使用脚本语言开发,可能首先想到的就是micropython,micropython可以让工程师使用脚本语言python进行mcu开发,极大地降低了开发门槛。 + +但是使用micropython开发能够直接使用的开发板并不多,为没有现成micropython固件的mcu移植micropython显然也是一件工程浩大且门槛很高的工作。 + +python的运行效率较低,在资源紧缺的mcu中显得尤为明显,而且使用python开发难以充分利用mcu的中断、dmp等硬件特性。在高实时性的信号处理、数据采集、实时控制等应用中,python难以成为真正落地于生产环境。 + +就目前而言,在mcu开发中,占80%左右的开发仍然是使用c语言,c++也仅占不到20%。 + +但是无疑脚本语言的便利性是非常明显的。 + +在IOT领域中,服务器端的开发者往往熟悉python和JavaScript等支持面向对象的脚本语言,如果能够直接用脚本语言调用mcu的功能,那么也将达到降低开发难度的效果。 + +也就是说,使用c语言进行mcu嵌入式开发,又向上位机或者服务器提供面向对象的脚本语言调用接口,不就可以兼顾mcu运行效率和开发效率了吗? + +本文介绍的mimisciprt库正是可以起到这样的作用。 + +mimiscrpit库可以为c语言开发的mcu工程提供面向对象的脚本语言调用接口。mimiscript有以下几个特点: + +1. 支持裸机运行,可运行于内存40Kb以上的mcu中,如stm32f103,esp32。 + +2. 支持跨平台,可运行于linux环境。 + +3. 仅使用C标准库,尽可能的结构清晰(尽我所能),几乎不使用宏。 + +## 2. 用mimiscript点一个灯 + +鲁迅曾说过:“点灯是嵌入式领域的hellow world”。 + +那我们就以点灯为例,看一看mimiscript如何为mcu提供对象化的脚本支持。 + +我们以STM32的HAL库为例,假设在管脚PA8上连接了一个LED灯,我们称之为LED1,PA8拉高时灯亮,拉低时灯灭。 + +那么使用下面的c语言代码就可以点亮灯LED1: +``` c +HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,SET); +``` +但是我们希望使用如下的面向对象的脚本语言优雅地点灯: +``` python +LED1.on() +``` + +下面我们看看如何使用mimiscript实现这个需求。 + +1. 编写一个on()函数。这个函数将会被作为一个方法注册到脚本对象里面,注册之后就不会再由开发者在c语言开发中调用了,只会在脚本运行时被脚本解释器调用。 + + on()函数的入口参数有self和args,其中self是对象的指针,args是参数列表,args内部基于链表,可以传入任意个数、任意类型的参数。 + + 在mimiscript中,所有被绑定为方法的函数都使用这两个入口参数。 +``` c +void on(MimiObj *self, Args *args) +{ + HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,SET); +} +``` +2. 然后编写LED1对象的构造器。 + + 构造器的入口参数是args,用于传入初始化对象所需的参数,返回的是一个脚本对象,所有的脚本对象都使用MimiObj结构体。 + + class_defineMethod用于将编写的c语言函数绑定为脚本对象的方法。 + + class_defineMethod的第一个入口参数是对象指针,第二个参数是绑定的接口定义,第三个入口参数是被绑定函数的函数指针。 +``` c +MimiObj * New_LED1(Args *args) +{ + // 继承自MimiObj基本类 + MimiObj *self = New_MimiObj(args); + // 为LED1对象绑定on()方法 + class_defineMethod(self, "on()", on); + return self; +} +``` +3. 创建对象并监听串口的输入数据。当获得整行数据后直接当作脚本执行。 +``` c +int uartReciveOk; //串口单行接收完成的标志位 +char uartReciveBuff[256];// 串口接收到的单行数据 +int main() +{ + // 硬件的初始化代码略 + + // 创建根对象 + MimiObj *root = newRootObj("root",New_MimiObj_sys); + // 新建LED1对象,LED1对象会挂载在root对象下 + obj_newObj(root, "LED1", New_LED1); + while(1) + { + // 串口已经接收到单行数据 + if(uartReciveOk) + { + // 执行串口输入的单行数据 + obj_run(root, uartReciveBuff); + // 清除串口接收标志位 + uartReciveOk = 0; + } + } +} + +``` + +4. 这时只要向mcu的串口发送``` led.on()```,灯就亮了~ + +## 3. 用mimiscript实现一个加法函数。 + +上面的例子中的方法没有输入输出,下面的例子中,我们会定义一个test类,并为test类添加一个add函数,实现加法功能。 + +``` c +#include "sysObj.h" + +/* + 被绑定的方法 + self 是对象指针,指向执行方法的对象 + args 是参数列表,用于传入传出参数 + (所有被绑定的方法均使用此形参) +*/ +void add(MimiObj *self, Args *args) +{ + /* + 参数传递 + 从参数列表中取出输入参数val1和val2 + */ + int val1 = args_getInt(args, "val1"); + int val2 = args_getInt(args, "val2"); + + /* 实现方法的功能 */ + int res = val1 + val2; + + /* 将返回值传回参数列表 */ + method_returnInt(args, res); +} + +/* + 定义测试类的构造器,一个构造器对应一个类 + 通过构造器即可新建对象 + args是构造器的初始化参数列表 + MimiObj*是新建对象的指针 + (所有构造器均使用此形参) +*/ +MimiObj *New_MimiObj_test(Args *args) +{ + /* + 继承sys类 + 只需要直接调用父类的构造器即可 + */ + MimiObj *self = New_MimiObj_sys(args); + + /* + 为test类绑定一个方法(支持重载) + 1.入口参数self:对象指针,指向当前对象 + 2.传入的第二参数是被绑定方法的接口定义 + (此处使用typescript语法,简单的修改即可支持python格式) + 3.传入的第三个参数是被绑定方法的函数指针 + */ + class_defineMethod(self, "add(val1:int, val2:int):int", add); + + + /* 返回对象 */ + return self; +} + +void main() +{ + /* + 新建根对象,对象名为“sys” + 传入对象名和构造器的函数指针 + */ + MimiObj *sys = newRootObj("sys", New_MimiObj_sys); + + /* + 新建test对象 + test对象作为子对象挂载在sys对象下(对象树) + */ + obj_newObj(sys, "test", New_MimiObj_test); + + /* + 运行单行脚本。 + 因为test对象挂在在sys对象下, + 因此可以通过test.add调用test对象的方法 + 运行后会动态新建res属性,该属性属于sys对象 + */ + obj_run(sys, "res = test.add(val1 = 1, val2 = 2)"); + /* + (也支持 "res = test.add(1, 2)"的调用方式) + */ + + /* 从sys对象中取出属性值res */ + int res = obj_getInt(sys, "res"); + + /* + 析构对象 + 所有挂载在sys对象下的子对象都会被自动析构 + 本例中挂载了test对象,因此在析构sys对象前, + test对象会被自动析构 + */ + obj_deinit(sys); + + /* 打印返回值 res = 3*/ + printf("%d\r\n", res); +} +``` + +# 结构摘要: + +## 1.数据结构 mimidata +datamemory:内存管理 + +datalink:非侵入式双向链表 + +dataArg:数据容器,支持int、float、string、object类型和自定义类型 + +dataArgs:基于双向链表的数据列表,常用作动态参数列表,并用来存储对象的属性和方法 + +dataSting,字符串处理 + +## 2.对象支持 (mimiObject) +dataObject:对象构造、对象析构、对象树、创建属性、绑定属性、绑定方法、python接口绑定、python接口解析 + +## 3.命令行交互层 (mimishell) +mimiSH:用于调用python接口 + +## 4.基于发布-订阅模型的事件机制(mimiEvnet) +支持同步事件回调 + +## 5.单元测试 (mimitest) +几乎完全覆盖的单元测试 +