必看!LuatOS自定义C库全新教程,一文极速上手

Posted 合宙Luat

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了必看!LuatOS自定义C库全新教程,一文极速上手相关的知识,希望对你有一定的参考价值。

今天继续讲LuatOS的开发,上一期简单说了一下,相信很多朋友已经看过了。那么今天,我就开始讲C和Lua调用的部分教程。

LuatOS相关资料及Lua语言的官方定义,详见以下链接:

LuatOS仓库:

https://gitee.com/openLuat/LuatOS

Lua 5.3 中文参考手册:

https://www.runoob.com/manual/lua53doc/contents.html




1


闲谈C与Lua的调用




LuatOS自定义C库可以实现用户的自定义功能,比如一些对延时要求很高的需求,通过C进行使用会更方便快捷:

Lua使用一个虚拟栈来和C互传值,也就是说,C和Lua的数据交互是在栈上进行的。

栈的基本概念及原理




栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。

它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。


必看!LuatOS自定义C库全新教程,一文极速上手

入栈就是将一个新的元素放到栈顶,出栈就是从栈顶取出一个元素。栈顶的元素总是最后入栈,需要出栈时,又最先被从栈中取出。


如果你没接触过栈,就当它是个先入后出的大袋子,你要做的就是往里面放东西和拿东西。


无论何时Lua调用C,被调用的函数都得到一个新的栈,这个栈独立于C函数本身的栈,也独立于之前的Lua栈。它里面包含了Lua传递给C函数的所有参数,而C函数则把要返回的结果放入这个栈以返回给调用者 。

所以,C和Lua的调用说大白话就是——在栈上操作。






2


常用函数简介




在写函数的时候,首先使用lua_State:Lua虚拟机中的环境表、注册表、运行堆栈、虚拟机的上下文等数据。

常用函数还涉及判断函数、检查函数、取出函数、返回函数等等,如下所示:

常用判断函数




▼上下滚动,查看全部▼


lua_isboolean : 

lua_isboolean (lua_State *L, int index); 当给定索引的值是一个布尔量时,返回 1 ,否则返回 0 。

lua_isinteger :

lua_isinteger (lua_State *L, int index); 当给定索引的值是一个整数时,返回 1 ,否则返回 0 。

lua_isnil :

lua_isnil (lua_State *L, int index); 当给定索引的值是 nil 时,返回 1 ,否则返回 0 。

lua_isnumber :

lua_isnumber (lua_State *L, int index); 当给定索引的值是一个数字,或是一个可转换为数字的字符串时,返回 1 ,否则返回 0 。

lua_isstring :

lua_isstring (lua_State *L, int index); 当给定索引的值是一个字符串或是一个数字( 数字总能转换成字符串)时,返回 1 ,否则返回 0 。

lua_istable :

lua_istable (lua_State *L, int index); 当给定索引的值是一张表时,返回 1 ,否则返回 0 。






常用检查函数




▼上下滚动,查看全部▼


luaL_checkinteger : 

luaL_checkinteger (lua_State *L, int arg); 检查函数的第 arg 个参数是否是一个 整数(或是可以被转换为一个整数) 并以 lua_Integer 类型返回这个整数值。


luaL_checknumber :

luaL_checknumber (lua_State *L, int arg); 检查函数的第 arg 个参数是否是一个 数字,并返回这个数字。


luaL_checkstring :

luaL_checkstring (lua_State *L, int arg); 检查函数的第 arg 个参数是否是一个 字符串并返回这个字符串。


luaL_checklstring : 

luaL_checklstring (lua_State *L, int arg, size_t *l); 检查函数的第 arg 个参数是否是一个 字符串,并返回该字符串;如果 l 不为 NULL , 将字符串的长度填入 *l。字符串内可以是任意二进制数据,包括零字符。





常用取出函数




▼上下滚动,查看全部▼


luaL_optinteger : 

luaL_optinteger (lua_State *L , int arg , lua_Integer d);

如果函数的第 arg 个参数是一个 整数(或可以转换为一个整数), 返回该整数。若该参数不存在或是 nil, 返回 d。除此之外的情况,抛出错误。


luaL_optnumber : 

luaL_optnumber (lua_State *L, int arg, lua_Number d);

如果函数的第 arg 个参数是一个 数字,返回该数字。若该参数不存在或是 nil, 返回 d。除此之外的情况,抛出错误。


luaL_optstring :

luaL_optstring (lua_State *L, int arg, const char *d);

如果函数的第 arg 个参数是一个 字符串,返回该字符串。若该参数不存在或是 nil, 返回 d。除此之外的情况,抛出错误。


luaL_optlstring :

luaL_optlstring (lua_State *L , int arg , const char *d , size_t *l);

如果函数的第 arg 个参数是一个 字符串,返回该字符串。若该参数不存在或是 nil, 返回 d。除此之外的情况,抛出错误。若 l 不为 NULL, 将结果的长度填入 *l 。字符串内可以是任意二进制数据,包括零字符。





常用返回函数




▼上下滚动,查看全部▼


lua_pushboolean :

void lua_pushboolean (lua_State *L, int b); 把 b 作为一个布尔量压栈。

lua_pushinteger:

lua_pushinteger (lua_State *L, lua_Integer n); 把值为 n 的整数压栈。

lua_pushnumber : 

lua_pushnumber (lua_State *L, lua_Number n); 把一个值为 n 的浮点数压栈。

lua_pushstring :

lua_pushstring (lua_State *L, const char *s); 将指针 s 指向的零结尾的字符串压栈。如果 s 为 NULL,将 nil 压栈并返回 NULL。

lua_pushlstring : 

lua_pushlstring (lua_State *L, const char *s, size_t len); 把指针 s 指向的长度为 len 的字符串压栈。字符串内可以是任意二进制数据,包括零字符。




注意:

xxxlstring与xxxstring的区别,前者传二进制数据是必须的!!!







3


第一个函数




3.1 创建自定义模块框架:

在LuatOS源码目录下找到luat/mouldes文件夹,新建一个名为luat_lib_xxx.c的文件;

在C文件中先引入"luat_base.h",这个头文件里面包含了我们需要用到的函数。


3.2 创建自定义函数:

▼上下滚动,查看全部▼

static int l_jiaFa(lua_State *L)

//函数名一般写l_xxx ,

这样可以直观的知道这是一个和lua交互的函数

{

    int a = luaL_checkinteger(L, 1);

    //取出输入的第一个值(int类型)

    int b = luaL_optinteger(L, 2, 0);

   //取出输入的第二个值(int类型)

    lua_pushinteger(L, a + b);   //返回ab之和

    return 1; //返回一个值

}



分析一下:

函数输入(lua_State *L),前面提到了,C和Lua的调用是在栈里进行的;

a的值是通过luaL_checkinteger(L, 1);获得的,这是一个检查函数,输入的第一个值是int类型的时候,函数会把值返回,在这里就是赋值给了a。

b的值是通过luaL_optinteger(L, 2, 0);获得的,只是一个取出函数,取出的对象是lua函数输入的第二个值,如果第二个值不是int类型,则会返回0。

lua_pushinteger(L, a + b); 是向lua函数返回值(int类型)

最后的return 1; 代表这个函数的返回值只有一个。


3.3 注册函数:

▼上下滚动,查看全部▼


#include "rotable.h"

static const rotable_Reg exa[] =

//exa[]就是你的自定义库名字

    {

        {"jiaFa", l_jiaFa, 0},

       //第一个是lua要调用的函数名字,

         第二个是对应的c函数,

         第三个是lua_Integer值,一般写0

        {NULL, NULL, 0}};

       //结尾要有这一行,代表没有其他的函数了


LUAMOD_API int luaopen_exa(lua_State *L)

//注册一个LUAMOD_API,之后要写入luat_base.h

{

    luat_newlib(L, exa);

    //创建一张新的表,并把列表L中的函数注册进去

    return 1;

}


这里重点是rotable_Reg:

typedef struct rotable_Reg

{

 char const* name;

 lua_CFunction func;

 lua_Integer value;

rotable_Reg;



你需要写几个函数,就在这个结构体里写,最后以 {NULL, NULL, 0}结尾,代表结束。

写完上面这一段之后,在luat_base.h里面加上LUAMOD_API int luaopen_exa(lua_State *L);


3.4 启用自定义模块

打开芯片/模组对应的base文件,例如air302的base文件是:
https://gitee.com/openLuat/LuatOS/blob/master/bsp/air302/src/luat_air302_base.c

根据,我们在loadedlibs[]里加入 {"exa", luaopen_exa}, 代表启用该模块。


3.5 编译新固件

这里不细说了,在LuatOS仓库里都有编译说明,air302我之前也做过编译教程,详见:
https://doc.openluat.com/article/2047

3.6 编写Lua脚本

-- LuaTools需要PROJECT和VERSION这两个信息

PROJECT = "exademo"

VERSION = "1.0.0"


-- sys库是标配

_G.sys = require("sys")


local function exatext()

    log.info("jiafa", exa.jiaFa(2,10))

end


sys.timerLoopStart(exatext, 1000)

-- 用户代码已结束---------------------------------

-- 结尾总是这一句

sys.run()

-- sys.run()之后后面不要加任何语句!!!!!









4


进阶函数




当前面的步骤做完,确保正确运行后,再往下看进阶部分。

4.1 返回字符串

static int l_reString(lua_State *L)

{

    if (!lua_isnumber(L, 1))

   //如果输入的是字符串(不是number自然就是字符串咯)

    {

        const char *c = luaL_optstring(L, 1, "");

        lua_pushstring(L, c);

        //如果第一个输入值是string,直接返回去

        return 1;

    }

    else

    {

        lua_pushstring(L,"nostring");

       //到这里就代表输入的第一个值不是字符串,返回nostring

        return 1;

    }

}



这一部分较基础部分多了判断函数,根据注释了解一下即可,不难的。

4.2 多输入多返回

static int l_reMore(lua_State *L)

{

    if (lua_isinteger(L, 1)) 

    //判断输入的第一个值是不是int

    {

        lua_pushboolean(L, 1); //返回true

        const char *st = luaL_optstring(L, 2, "nostring");

        lua_pushstring(L, st); //返回字符串

        return 2;

    }

    else

    {

        lua_pushboolean(L, 0);    //返回flase

        return 1;

    }

}



这里边看代码边说明:

1)函数首先判断lua输入的第一个值类型,如果是int类型,那么返回第一个bool类型值;

2)现在已经确定第一个值是int类型了,那么取出lua输入的第二个值,如果是string就赋值给st,如果是nil那就把“nostring”赋值给st;

3)返回st,使用函数为lua_pushstring(L, st);

4)分析返回值的定义——我们可以看到在if为真的大括号里,我们先返回了bool类型的值,然后返回了string类型的值,一共返回了两个值,那么return的值我们写为2;在else的大括号里,我们只返回了一个bool类型的值,那么return的值即为1。



今天的分享就到这里了,相信大家多多少少都对调用方式有了基础的了解,大家可以在实际应用中多多实践,更多问题欢迎加入技术交流群共同探讨。

必看!LuatOS自定义C库全新教程,一文极速上手

必看!LuatOS自定义C库全新教程,一文极速上手

合宙Luat交流群18(LuatOS):1061642968

Luat训练营第一期:221504157

合宙Luat交流群03:1092305811

合宙Luat交流群04:877164555  

合宙Luat交流群17(iRTU):1027923658


每个建议都值得关注

每个技能都值得分享

必看!LuatOS自定义C库全新教程,一文极速上手

- 更多精彩等你参与 -

了解更多相关内容







必看!LuatOS自定义C库全新教程,一文极速上手


以上是关于必看!LuatOS自定义C库全新教程,一文极速上手的主要内容,如果未能解决你的问题,请参考以下文章

来了!STM32移植LuatOS,潘多拉示例全新教程

干货 | LuatOS BSP移植教程,简单到复制粘贴!!!

LuatOS | 体验全新BSP—基于Win32的LuatOS仿真器,悬赏活动火热来袭

LuatOS | 全新在线模拟器,随时随地发挥创意

BZZ挖矿教程,一文读懂Swarm(小白必看)

《ABP Framework 极速开发》 - 教程首发